概述
剑指offer刷题笔记
- day1
- 02 回文链表
- 03 I 数组中重复的是数字
- 03II 不修改数组找出重复的数字
- 04 二维有序数组的查找
- 05 替换空格
- 06 从尾到头打印链表
- 07 根据前序和中序重新构建二叉树
- 08 二叉树中序遍历的下一个节点
- 09 两个栈实现队列
- 10 斐波那契数列、青蛙跳台阶
- 11 旋转数组中的最小数字
- 12 矩阵中的路径
- day2
- 13 机器人的运动范围
- 14 剪绳子
- 15 二进制中1的个数
- 16 数值的整数次幂
- 17 打印1到最大的n位数
- 18 删除链表的节点
- LC 83 删除有序链表中的重复节点
- 19 正则表达式匹配
- 20 PASS
- 21. 调整数组顺序使奇数位于偶数前面
- 22. 链表中倒数第k个节点
- 23 链表中环的入口
- day3
- 24 反转链表
- 25 合并两个排序链表
- 26 树的子结构
- 27 二叉树镜像
- 28 对称的二叉树
- 29 顺时针打印矩阵
- 30 包含min函数的栈
- 31 栈的输入和弹出序列
- 32 从上到下打印二叉树(不分行)
- 32 从上到下打印二叉树(分行)
- 32 从上到下打印二叉树(之字形:左右左)
- day4
- 33. 二叉搜索树的后序遍历序列
- 34. 二叉树中和为某一值的路径
- 35 复杂链表的复制
- 36 二叉搜索树与双线链表
- 37 二叉树的序列化和反序列化
- 38 字符串序列
- 39 数组中出现次数超过一半的数字
- day5
- 40 最小的k个数
- 41 数据流中位数
- 42 连续子数组最大和
- 43 1~n中1出现的次数
- 44 数字序列中某一位的数字
- 45 把数组排成最小的数
- 46 数字翻译成字符串
- 47 礼物的最大价值
- 48 最长不含重复子串
- 49 丑数
- 50 第一个只出现一次的字符
- day6
- 51 数组中的逆序对
- 52 两个链表的第一个公共节点
- 53I 排序数组中查找数字
- 53II 0~n-1中缺失的数字
- 53III 数组中数值和下标相等的元素
- 54 二叉搜索树的第k大节点
- 55I 二叉树的深度
- 55II 平衡二叉树
- 56I 数组中数字出现的次数
- 56II 数组中数字出现的次数
- 57I 和为s的数字
- 57II 和为s的连续正数序列
- 58I 翻转单词顺序
- 58II 左旋转字符串
- 59 队列的最大值
- 60I n个骰子的点数次数
- 60II n个骰子的点数概率
- day7
- 61 扑克牌的顺子
- 62 圆圈中最后剩下的数字(约瑟夫环)
- 63 股票的最大利润
- 64 1+2+..+n
- 65 不用加减乘除的加法
- 66 构建乘积数组
- 67 字符串转换为整数
- 68 二叉树的最低公共祖先
过去几天复习了一遍剑指offer,整理一下笔记方便以后复习,题目来源leetcode,题解来源acwing
day1
02 回文链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* SearchHalfNode(ListNode* head)
{
auto fast = head;
auto slow = head;
while (fast)
{
fast = fast -> next;
slow = slow -> next;
if (fast) fast = fast->next;
}
return slow;
}
ListNode* reverseList(ListNode* node)
{
ListNode* pre = nullptr;
while (node)
{
auto next_ = node->next;
node -> next = pre;
pre = node;
node = next_;
}
return pre;
}
bool isPalindrome(ListNode* head) {
auto mid = SearchHalfNode(head);
auto end = reverseList(mid);
while (end)
{
if (head->val != end->val)
return false;
head = head->next;
end = end-> next;
}
return true;
}
}
03 I 数组中重复的是数字
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
int n = nums.size();
for (auto x: nums)
if (x < 0 || x >= n )
return -1;
for (int i = 0; i < n; i ++)
{
while (i != nums[i] && nums[nums[i]] != nums[i]) swap(nums[i], nums[nums[i]]);//交换i nums[i] nums[nums[i]]
if (i != nums[i] && nums[nums[i]] == nums[i]) return nums[i];//交换位置已经有值,那么返回nums[i]
}
return -1;
}
};
03II 不修改数组找出重复的数字
#include <iostream>
#include <vector>
using namespace std;
const int N = 1010;
vector<int> nums;
class Solution
{
public:
int duplicateInArray(vector<int> &nums)
{
int l = 1, r = nums.size() - 1; //最大值、最小值
while (l < r)
{
//无序数组二分
int mid = l + r >> 1; //按中位数切分成两半
// [l,mid] [mid+1,r]
int s = 0;
for (auto x : nums)
s += x >= l && x <= mid; //按区间计数
if (s > mid - l + 1) //如果左边区间的点大于区间中的点位
r = mid; //去左边查找
else
l = mid + 1; //否则去右边查找
}
return r;
}
void display()
{
cout << "this is my function!" << endl;
}
};
int main()
{
int n;
cin >> n;
for (int i = 0; i <= n; i++)
{
int x;
cin >> x;
nums.push_back(x);
}
Solution solution;
int res;
// solution.display();
res = solution.duplicateInArray(nums);
cout << res << endl;
}
04 二维有序数组的查找
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
/*
从左下角开始查找,上边的数一定小,右边的数一定大
每次都会省略一行
*/
int m, n;
if (matrix.empty() || matrix[0].empty()) return false;
m = matrix.size();
n = matrix[0].size();
int i = m - 1, j = 0;
while (i >= 0 && j < n)
{
if (matrix[i][j] > target) i --;
else if (matrix[i][j] < target) j ++;
else return true;
}
return false;
}
};
05 替换空格
class Solution {
public:
string replaceSpace(string s) {
string res;
for (auto x: s)
{
if (x == ' ') res += "%20";
else res += x;
}
return res;
}
};
06 从尾到头打印链表
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {} //构造函数初始化,可以传入参数x
};
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
vector<int> res;
while (head)
{
res.push_back(head -> val);
head = head -> next;
}
return vector<int>(res.rbegin(), res.rend());//rbegin与begin正好相反
}
};
07 根据前序和中序重新构建二叉树
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
public:
map <int, int> hash;
vector<int> preorder, inorder;
TreeNode* buildTree(vector<int>& _preorder, vector<int>& _inorder) {
/*
根左右找到根节点的值
左根右找到根节点的位置,然后递归左子树和右子树
*/
preorder = _preorder, inorder = _inorder;
for (int i = 0; i < inorder.size(); i++) hash[inorder[i]] = i;//使用hash存储每个节点在中序遍历中的位置
return dfs(0, preorder.size() - 1, 0, inorder.size() - 1);
}
TreeNode* dfs(int pl, int pr, int il, int ir)
{
if (pl > pr) return nullptr;
auto root = new TreeNode(preorder[pl]);
int k = hash[root -> val];
auto left = dfs(pl + 1, pl + k - il, il, k - 1);//左子树k-il个元素
auto right = dfs(pl + k - il + 1, pr, k + 1, ir);
root -> left = left;
root -> right = right;
return root;
}
};
08 二叉树中序遍历的下一个节点
#include <cstdlib>
struct TreeLinkNode
{
int val;
struct TreeLinkNode *left;
struct TreeLinkNode *right;
struct TreeLinkNode *next; // father指针
TreeLinkNode(int x) : val(x), left(NULL), right(NULL), next(NULL){}
};
class Solution
{
public:
TreeLinkNode *GetNext(TreeLinkNode *pNode)
{
//当前节点在中序遍历中的下一个节点:后继
//二叉搜索树中比当前节点大的最小节点
// next是父节点
if (pNode->right)
{
pNode = pNode->right;
while (pNode->left)
pNode = pNode->left;//如果有右子树,那么下一个节点一定是右子树的最左节点
return pNode;
}
// 如果没有右子树,中序遍历会向上看,如果是父节点的左节点那么直接返回父节点
// 如果是父节点的右节点,那么沿着父节点继向上,直到某个节点是父节点的左节点,返回父节点
// 两种情况可以统一为
while (pNode->next && pNode == pNode->next->right)
pNode = pNode->next;
return pNode->next;
}
};
09 两个栈实现队列
class CQueue {
public:
stack<int> stk, cache;
CQueue() {}
void appendTail(int value) {
stk.push(value);
}
void copy(stack<int> &a, stack<int> &b)
{
while (a.size())
{
b.push(a.top());
a.pop();
}
}
int deleteHead() {
if (stk.size())
{
copy(stk, cache);//保存到缓存中
int res = cache.top();//删除队头
cache.pop();
copy(cache, stk);//拷贝回去
return res;
}
return -1;
}
};
10 斐波那契数列、青蛙跳台阶
class Solution {
public:
int fib(int n) {
int a = 0, b = 1;
for (int i = 1; i <= n; i ++)
{
int r = (a + b) % 1000000007;// (a + b) % c = (a % c + b % c) % c 所以可以提前取余,避免整形溢出
a = b;
b = r;
}
return a;
}
};
11 旋转数组中的最小数字
class Solution {
public:
int minArray(vector<int>& numbers) {
// 有序数组 搬移后仍然是有序数组
// 左数组最左边 可能 等于右数组最右边 去掉右数组的右半边
// 右边数组完全小于左边,可以找到右数组的最左元素
// 使用二分
int n = numbers.size() - 1;
if (n < 0) return -1;
while (n > 0 && numbers[0] == numbers[n]) n --;
if (numbers[0] <= numbers[n]) return numbers[0]; //只剩左数组
//二分搜索 小于numbers[0]的最左数
int l = 0, r = n;
while (l < r)
{
int mid = l + (r - l) / 2;//[l,mid] [mid+1,r]
if (numbers[mid] < numbers[0]) r = mid;//mid落在了右半
else l = mid + 1;//mid落在左半
}
return numbers[r];
}
};
12 矩阵中的路径
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
//dfs 从首字母开始查找,如果找到立即返回
if (board.empty()) return false;
for (int i = 0; i < board.size(); i ++)
for(int j = 0; j < board[i].size(); j++)
{
if (dfs(board, word, 0, i, j)) return true;
}
return false;
}
bool dfs(vector<vector <char>> &board, string word, int u, int x, int y)
{
if (board[x][y] != word[u]) return false;
// 如果二维矩阵元素等于当前元素
if (u == word.size() - 1) return true;
// 如果当前元素相等且不是末尾元素,向下查找
char t = board[x][y];
board[x][y] = '*';
int dx[4] = {-1,1,0,0}, dy[4] = {0,0,-1,1};
for (int i = 0; i < 4; i++)
{
int a = x + dx[i];
int b = y + dy[i];
//如果在范围内,寻找下一层
if (a >=0 && a < board.size() && b >= 0 && b < board[a].size())
if (dfs(board, word, u + 1, a, b)) return true;
}
board[x][y] = t;//回溯
return false;
}
};
day2
13 机器人的运动范围
class Solution {
public:
int get_single_sum(int x)
{
int s = 0;
while (x)
{
s += x % 10;
x /= 10;
}
return s;
}
int get_sum(pair<int, int> p) //坐标位数之和不能大于k
{
return get_single_sum(p.first) + get_single_sum(p.second);
}
int movingCount(int m, int n, int k) {
int res = 0;
if (!m || !n) return 0;
vector<vector<bool>> st(m, vector<bool>(n));
queue<pair<int, int>> q;
q.push({0, 0});//pair列表初始化
int dx[2] = {1, 0}, dy[2] = {0, 1};//只需要向下和向右查找即可
while (!q.empty())
{
auto t = q.front();
q.pop();
if (get_sum(t) > k||st[t.first][t.second]) continue;
//如果满足条件
res ++;
st[t.first][t.second] = true;
// 广度优先搜索
for (int i = 0; i < 2; i++)
{
int a = t.first + dx[i];
int b = t.second + dy[i];
if (a >= 0 && a < m &&b >= 0 && b < n)
q.push({a, b});
}
}
return res;
}
};
14 剪绳子
/*
整数拆分,使其因子乘积最大
结论:
ni%3 余1 拆出一个2*2=4,剩下被3拆分
ni%3 余2 拆出一个2,剩下被3拆分
分析过程:
假设N > 0, 正整数N拆分成m个数,N = n1 + n2 + n3 + n4 + n..+nm
1 最优解肯定没有1, 1*(x-1)< x
2 最优解可以没有4, 2*2=4 可拆 最优解2 3 >4
3 ni>5, ni必须拆成3*(ni-3),最优解不能包含大于等于5的数,最优解只包含 2和3
3 * (ni - 3) >= ni
3 * ni - 9 >= ni
2 * ni >= 9 成立,拆了更好
4 最优解若有3个2: 2 * 2 *2 < 3 * 3 最优解最多两个2
*/
class Solution {
public:
int cuttingRope(int n) {
int res = 1;
if (n <= 3) return n -1;
if (n % 3 == 1) res *= 4, n -= 4;
if (n % 3 == 2) res *= 2, n -= 2;
while (n)
{
res *= 3;
n -= 3;
}
return res;
}
};
15 二进制中1的个数
/*两种解法:
1 lowbit 最后一位1的二进制表示的十进制数
在原来的基础上减去即可消去最右边的1,每消一次计数+1
1010 原码
0110 补码
2 直接转换为正数的原码
在原来的基础上每次右移1位,每次计数 + x&1
*/
//lowbit算法
class Solution {
public:
int hammingWeight(uint32_t n) {
int s = 0;
while (n)
{
int x = n & -n;
n -= x;
s ++;
}
return s;
}
};
//直接转化为正数
class Solution {
public:
int hammingWeight(uint32_t n) {
unsigned int n_ = n;
int s = 0;
while (n)
{
s += n & 1;
n >>= 1;
}
return s;
}
};
16 数值的整数次幂
/*
快速幂:指数的二进制拆分
x^N = x2*x2^2*x2^3*x2^4...
N = 2^2+2^3+2^4...表示为二进制数即可。
N & 1
0 res*=x
1 res*=x*2
2 res*=x*4
3 res*=x*8
...
*/
class Solution {
public:
double myPow(double x, int n) {
//注意负指数
double res = 1;
long long N = abs(n);
while (N)
{
if (N & 1) res *= x;//二进制位=1,那么累加结果
x *= x;
N >>= 1;//指数的二进制拆分
}
if (n < 0) res = 1 / res;
return res;
}
};
17 打印1到最大的n位数
class Solution {
public:
vector<int> printNumbers(int n) {
//输出所有的n位数
int m = (pow(10, n)) - 1;
vector<int> nums(m, 1);
for (int i = 1; i < m; i ++)
nums[i] = nums[i-1] + 1;
return nums;
}
};
18 删除链表的节点
class Solution {
public:
ListNode* deleteNode(ListNode* head, int val) {
//如果是头节点
if (head -> val == val) return head -> next;
//不是头节点
ListNode* cur = head;
while(cur-> next -> val != val)
cur = cur -> next;
cur -> next = cur -> next -> next;
return head;
}
};
LC 83 删除有序链表中的重复节点
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
// dummy 哨兵机制 头结点可能被删掉
auto dummy = new ListNode(-1);
dummy -> next = head;
// 链表双指针 dummy 1 1 1 2 2 3 3 4 4 分段看
auto p = dummy;
while (p -> next)
{
auto cur = p -> next;
while (cur -> next && cur -> val == cur -> next -> val)
cur = cur -> next;
// 如果没有重复,p移动到cur,
if (p -> next == cur) p = cur;
// 如果有重复,将p的next指针指向cur
else p -> next = cur;//cur在下一个区间
}
return dummy -> next;
}
};
19 正则表达式匹配
class Solution {
public:
int n, m;
vector<vector<int>> f;
string s, p;
bool isMatch(string s_, string p_) {
s = s_, p = p_;
n = s.size(), m = p.size();
f = vector<vector<int>>(n + 1, vector<int>(m + 1, -1));
return dp(0, 0);
}
bool dp(int x, int y)
{
if (f[x][y] != -1)
return f[x][y];
if (y == m)
return f[x][y] = x == n; // 初始化
bool first_match = x < n && (p[y] == '.' || s[x] == p[y]);
if (y + 1 < m && p[y + 1] == '*')
//匹配0次跳过两个匹配字符
//匹配一次以上: 当前字符匹配且f[x+1][y]后面字符也是匹配的
f[x][y] = dp(x, y + 2) || (first_match && dp(x + 1, y));
else
{
f[x][y] = first_match && dp(x + 1, y + 1);
}
return f[x][y];
}
};
20 PASS
21. 调整数组顺序使奇数位于偶数前面
class Solution {
public:
vector<int> exchange(vector<int>& nums) {
/*双指针
第一个指针前面全是奇数
第二个指针后面全是偶数*/
int l = 0, r = nums.size() - 1;
while (l <= r)
{
while (l <= r && nums[l] % 2 == 1) l ++;
while (l <= r && nums[r] % 2 == 0) r --;
if (l < r) swap(nums[l], nums[r]);
}
return nums;
}
};
22. 链表中倒数第k个节点
class Solution {
public:
ListNode* getKthFromEnd(ListNode* head, int k) {
auto slow = head;
int idx = 0;
while (head)
{
if (idx >= k)
slow = slow -> next;
head = head->next;
idx ++;
}
return slow;
}
};
23 链表中环的入口
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
// 链表中环的检测
auto fast = head;
auto slow = head;
bool find = false;
while (slow && fast)
{
//fast走两步
fast = fast -> next;
if (fast) fast = fast -> next;
//fast空时,无环
if (!fast) return nullptr;
//slow走一步
slow = slow -> next;
//如果相遇,那么停止
if (fast == slow)
{
find = true;
break;
}
}
//没有环
if (!find) return nullptr;
//从头开始走,直到相遇
slow = head;
while (slow && fast && slow != fast)
{
fast = fast -> next;
slow = slow -> next;
}
return slow;
}
};
day3
24 反转链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
//记录一个前驱节点
ListNode * pre = nullptr;
auto cur = head;
while (cur)
{
auto next = cur -> next;
cur -> next = pre;
pre = cur;
cur = next;
}
return pre;
}
};
25 合并两个排序链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
//归并排序的并操作
auto dummy = new ListNode(-1);
auto cur = dummy;
while (l1 && l2)
{
if (l1 -> val <= l2 -> val)
{
cur -> next = l1;
l1 = l1 ->next;
}
else
{
cur -> next = l2;
l2 = l2 -> next;
}
cur = cur ->next;
}
if (l1) cur -> next = l1;
else cur ->next = l2;
return dummy -> next;
}
};
26 树的子结构
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isSubStructure(TreeNode* A, TreeNode* B) {
// B恰好能覆盖A的部分节点 类似字符串匹配
// 从树根开始看,如果不是,后移一位 暴力匹配 空树不是任意子结构
if (!A || !B) return false;//某棵树为空 返回fasle
if (isPart(A, B)) return true;// 如果AB匹配返回true
return isSubStructure(A->left, B) || isSubStructure(A->right,B);//如果不匹配 看左右子树是否匹配
}
bool isPart(TreeNode* p1, TreeNode* p2)
{
if (!p2) return true;//如果p2没有点了,匹配成功
if (!p1 || p1 -> val != p2 -> val) return false;//p1没有电,匹配失败
//如果p1 p2有点且值相等,那么当前节点匹配,看左右节点是否匹配
return isPart(p1 -> left, p2 -> left) && isPart(p1 -> right, p2 -> right);
}
};
27 二叉树镜像
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode * mirrorTree(TreeNode* root) {
if (!root) return root;
swap(root->left, root->right);
mirrorTree(root->left);
mirrorTree(root->right);
return root;
}
};
28 对称的二叉树
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isSymmetric(TreeNode* root) {
// 除了根节点
// 左节点的左儿子和右节点的右儿子 相等
// 左节点的右儿子和右节点的左儿子 相等
if (!root) return true;
// 看左右子树是否对称
return dfs(root -> left, root->right);
}
bool dfs(TreeNode* p1, TreeNode* p2)
{
if (!p1 || !p2) return !p1 && !p2;
//只有两个同时为空才返回true;
if (p1 -> val != p2 -> val) return false;
//如果两个节点相等,则递归判断两个节点的左右儿子
return dfs(p1 -> left, p2 -> right) && dfs(p1 -> right, p2 -> left);
}
};
29 顺时针打印矩阵
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector <int> res;
if (matrix.empty()) return res;
int m = matrix.size();
int n = matrix[0].size();
vector<vector<bool>> st(m, vector<bool>(n, false));
int dx[4] = {0,1,0,-1}, dy[4] = {1, 0, -1, 0};
int x = 0, y = 0, d = 0;
for (int i = 0; i < m * n; i ++)
{
res.push_back(matrix[x][y]);
st[x][y] = true;
int a = x + dx[d], b = y + dy[d];
if (a < 0 || a >= m || b < 0 || b >= n || st[a][b])
{
//如果走出了矩阵范围,那么回退到x,y 然后调整方向
d = (d + 1) % 4;
a = x + dx[d], b = y + dy[d];
}
x = a, y = b;//新的坐标
}
return res;
}
};
30 包含min函数的栈
class MinStack {
public:
stack<int> stk, min_stk;
/** initialize your data structure here. */
MinStack() {
}
//单调栈
void push(int x) {
stk.push(x);
if (min_stk.empty() || x <= min_stk.top()) min_stk.push(x);
}
void pop() {
if (stk.top() == min_stk.top()) min_stk.pop();
stk.pop();
}
int top() {
return stk.top();
}
int min() {
return min_stk.top();
}
};
31 栈的输入和弹出序列
class Solution {
public:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
int m = pushed.size();
int n = popped.size();
stack<int> stk;
for (int i = 0, j = 0; i < m; i ++)
{
stk.push(pushed[i]);
while (j < n && stk.size() && stk.top() == popped[j])
stk.pop(), j++;
}
if (stk.size()) return false;
return true;
}
};
32 从上到下打印二叉树(不分行)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> levelOrder(TreeNode* root) {
vector<int> res;
if (!root) return res;
queue<TreeNode*> q;
q.push(root);
while (q.size())
{
auto t = q.front();
q.pop();
res.push_back(t -> val);
if (t -> left) q.push(t -> left);
if (t -> right) q.push(t -> right);
}
return res;
}
};
32 从上到下打印二叉树(分行)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
// 第一行加入null标记
// 遍历到第一行的null时,第二行自然也是null此时代表第二行结束...依次类推
queue<TreeNode*> q;
if (!root) return res;
q.push(root);
q.push(nullptr);
vector<int> level;
while (q.size())
{
auto t = q.front();
q.pop();
if (!t)
{
// 如果到达末尾
if (level.empty()) return res;
// 如果到达行尾
res.push_back(level);
level.clear();
q.push(nullptr);
continue;
}
level.push_back(t -> val);
if (t -> left) q.push(t -> left);
if (t -> right) q.push(t -> right);
}
return res;
}
};
32 从上到下打印二叉树(之字形:左右左)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
if (!root) return res;
vector<int> level;
queue<TreeNode*> q;
q.push(root);
q.push(nullptr);
bool zigzag = false;
while(q.size())
{
auto t = q.front();
q.pop();
if(!t)
{
if (level.empty()) return res;
if (zigzag) reverse(level.begin(), level.end());
res.push_back(level);
level.clear();
q.push(nullptr);
zigzag = !zigzag;
continue;
}
level.push_back(t -> val);
if (t -> left) q.push(t -> left);
if (t -> right) q.push(t -> right);
}
return res;
}
};
day4
33. 二叉搜索树的后序遍历序列
// 中序+后序 nlogn
class Solution {
public:
vector<int> inorder, postorder;
map<int, int> hash;
bool verifyPostorder(vector<int>& postorder_) {
postorder = postorder_;
sort(postorder_.begin(), postorder_.end());
inorder = postorder_;
for (int i = 0; i < postorder.size(); i ++) hash[inorder[i]] = i;
return dfs(0, postorder.size() - 1, 0, inorder.size() - 1);
}
int dfs(int pl, int pr, int il, int ir)
{
if (pl > pr) return true;
auto root = postorder[pr];
int k = hash[root];
if (k < il || k > ir) return false; //当前的根节点不在中序遍历的范围内
bool left = dfs(pl, pl + k- il - 1 , il, k - 1);//长度为k-il
bool right = dfs(pl + k-il, pr - 1, k+ 1, ir);
return left && right;
}
};
//只用后序遍历 n*n
class Solution {
public:
vector<int> postorder;
bool verifyPostorder(vector<int>& postorder_) {
postorder = postorder_;
return dfs(0, postorder.size() - 1);
}
bool dfs(int l, int r)
{
if (l >= r) return true;
int root = postorder[r];
int k = l;
//左子树的元素都小于根节点
while (k < r && postorder[k] < root)
k++;//右子树的第一个元素
for (int i = k; i < r; i ++)
// 右子树的元素都大于根节点
if (postorder[i] < root)
return false;
return dfs(l, k - 1) && dfs(k, r - 1);
}
};
34. 二叉树中和为某一值的路径
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> path;
vector<vector<int>> ans;
vector<vector<int>> pathSum(TreeNode* root, int target) {
dfs(root, target);
return ans;
}
void dfs(TreeNode* root, int target)
{
if (!root) return;
// 单层递归逻辑
/*
1 先放入当前节点
2 检查是否为叶节点
如果是
满足和条件,直接加入答案
不满足条件,回溯 if(!root) return
如果不是,递归 if(!root) return
*/
path.push_back(root->val);
target -= root->val;
if (!root-> left && !root->right && !target)
ans.push_back(path);
dfs(root-> left, target);
dfs(root-> right, target);
path.pop_back();
}
};
35 复杂链表的复制
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
if (!head) return nullptr;
//复制链表
Node* cur = head;
while (cur)
{
Node* post = new Node(cur->val);
post-> next = cur -> next;
cur -> next = post;
cur = post -> next;
}
// 复制随机指针
cur = head;
while (cur)
{
if (cur -> random)
cur -> next -> random = cur -> random -> next;
cur = cur -> next -> next;
}
// 分离节点
auto dummy = new Node(-1);
cur = dummy;
Node* raw = head;
while (raw)
{
cur -> next = raw -> next;
raw -> next = cur -> next-> next;
cur = cur -> next;
raw = raw -> next;
}
return dummy-> next;
}
};
36 二叉搜索树与双线链表
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node() {}
Node(int _val) {
val = _val;
left = NULL;
right = NULL;
}
Node(int _val, Node* _left, Node* _right) {
val = _val;
left = _left;
right = _right;
}
};
*/
class Solution {
public:
Node* treeToDoublyList(Node* root) {
if (!root) return root;
auto slides = dfs(root);
slides.first -> left = slides.second;
slides.second -> right = slides.first;
return slides.first;
}
pair<Node*, Node*> dfs(Node* root)
{
if (!root->left && !root-> right) return {root, root};
else if (root->left && root->right)
{
//pari存储双向链表的头尾节点,经过dfs后所有子树都链接成了双向链表
// 将头尾指针连接即可得到双向循环链表
auto lslide = dfs(root->left), rslide = dfs(root->right);
lslide.second -> right = root, root-> left = lslide.second;
root-> right = rslide.first, rslide.first -> left = root;
return {lslide.first, rslide.second};
}
else if (root->left)
{
auto lslide = dfs(root->left);
lslide.second -> right = root, root-> left = lslide.second;
return {lslide.first, root};
}
else
{
auto rslide = dfs(root->right);
root-> right = rslide.first, rslide.first -> left = root;
return {root, rslide.second};
}
}
};
37 二叉树的序列化和反序列化
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Codec{
public:
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
string res;
dfs_s(root, res);
return res;
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
int u = 0;
return dfs_d(data, u);
}
void dfs_s(TreeNode* root, string& res)
{
if (!root)
{
res += "null ";
return;
}
res += to_string(root->val) + " ";
dfs_s(root->left, res);
dfs_s(root->right,res);
}
TreeNode* dfs_d(string data, int& u)
{
if (u == data.size()) return nullptr;
int k = u;
//当前节点的末尾后一位
while (data[k] != ' ') k ++;
//节点是null是null的话,空格的下一位 下一节点
if (data[u] == 'n')
{
u = k + 1;
return nullptr;
}
//如果是数字
int val = 0;
//负数
if (data[u] == '-')
{
for (int i = u + 1; i < k; i++)
val = val * 10 + (data[i]- '0');
val = -val;
}
//正数
else
{
for (int i = u; i < k; i++)
val = val * 10 + (data[i]- '0');
}
u = k + 1;
auto root = new TreeNode(val);
root-> left = dfs_d(data, u);//遍历到空节点,返回的是null
root-> right = dfs_d(data, u);//遍历到空节点,返回的是null
return root;
}
};
38 字符串序列
class Solution {
public:
vector<string> res;
string path;
vector<string> permutation(string s) {
res.clear();
path.clear();
sort(s.begin(), s.end());
vector<bool> used(s.size(), false);
dfs(s, used);
return res;
}
void dfs(string s, vector<bool>& used)
{
if (path.size() == s.size())
{
res.push_back(path);
return;
}
for (int i = 0; i < s.size(); i ++)
{
if (i > 0 && s[i] == s[i-1] && !used[i-1])
continue;
if (!used[i])
{
path += s[i];
used[i] = true;
dfs(s, used);
used[i] = false;
path.pop_back();
}
}
}
};
39 数组中出现次数超过一半的数字
class Solution {
public:
int majorityElement(vector<int>& nums) {
int val, cnt;
val = -1, cnt = 0;
/*
使用其他数字去抵消超过一半的数字,剩下的就是结果
如果数字等于val,cnt++;
如果数字不等于val,cnt--
如果cnt=0,更新数字 val=nums[i], cnt = 1
*/
for (int i = 0; i < nums.size(); i ++)
{
if (!cnt) val = nums[i], cnt = 1;
else if (nums[i] == val) cnt ++;
else cnt --;
}
return val;
}
};
day5
40 最小的k个数
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
/*
维护一个大根堆:因为大根堆元素比堆顶元素小
*/
vector<int> res;
priority_queue<int> heap;
for (auto x : arr)
{
heap.push(x);
if (heap.size() > k) heap.pop();
}
for (int i = 0; i < k; i ++)
{
res.push_back(heap.top());
heap.pop();
}
reverse(res.begin(), res.end());
return res;
}
};
41 数据流中位数
class MedianFinder {
public:
/** initialize your data structure here. */
priority_queue<int> max_heap;
priority_queue<int, vector<int>, greater<int>> min_heap;
MedianFinder() { }
void addNum(int num) {
max_heap.push(num);
if (min_heap.size() && max_heap.top() > min_heap.top())
{
auto maxv = max_heap.top(), minv = min_heap.top();
max_heap.pop(), min_heap.pop();
max_heap.push(minv), min_heap.push(maxv);
}
if (max_heap.size() - min_heap.size() > 1)
{
min_heap.push(max_heap.top());
max_heap.pop();
}
}
double findMedian() {
if (max_heap.size() + min_heap.size() & 1) return max_heap.top();
else return (max_heap.top() + min_heap.top()) / 2.0;
}
};
42 连续子数组最大和
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int summ = 0;
int res = INT_MIN;
for (int i = 0; i < nums.size(); i ++)
{
//如果小于0从新开始计算数组和
//以当前为结尾的前缀和
summ = max(summ + nums[i], nums[i]);
res = max(res, summ);
}
return res;
}
};
43 1~n中1出现的次数
class Solution {
public:
int countDigitOne(int n) {
/*
cur:当前位置
left:cur左边
right:cur右边
t: 10^右边的位数
左边取0~left-1(cur肯定会有1) 右边可以取全部 left*t次
左边取left的话,cur可能没有1
当前cur是0时,res+= 0
当前cur是1时,res+=right
当前cur>1时,res+=t
*/
if (!n) return 0;
vector<int> nums;
int res = 0;
while(n) nums.push_back(n % 10), n /= 10;
for (int i = nums.size() - 1; i >=0; i --)
{
int left = 0, right = 0, t = 1;
for(int j = nums.size() - 1; j > i; j --) left = left * 10 + nums[j];
for (int j = i - 1; j >=0; j --) right = right * 10 + nums[j], t *= 10;
res += left * t;
if (nums[i] == 1) res += right + 1;
else if (nums[i] > 1) res += t;
else res += 0;
}
return res;
}
};
44 数字序列中某一位的数字
class Solution {
public:
int findNthDigit(int n) {
//确定是几位数 n-10-90*2-900*3-9000*4-..直到小于i*s 位数*9
//确定该位数的第几个数 1000+235-1 =1234
//属于该数的第几位 1234 %
long long i = 1, s = 9, base = 1;
// 1 计算几位数i,剩下多少位n
while (n > i * s)
{
n -= i * s;
i ++;//位数+1
s*= 10;//数量级扩大十倍
base *=10; //基数扩大十倍
}
// 2 根据n,计算i位数的第多少个数字,下取整代替上取整
// 计算n,属于i位数的第几位
int number = base + (n + i - 1) / i - 1;
int r = n % i ? n % i : i;
// 3 根据i,number,r 可以定位到真实的数字
// 如果整除,那么就是最后一位,如果不整除,去掉r位后面的
for (int j = 0; j < i - r; j ++) number /= 10;
return number % 10;
}
};
45 把数组排成最小的数
class Solution {
public:
/* 定义新的比较方式
a < b <=> ab < ba
如果排好序的序列是
a1 a2 a3 a4.. an
证明 假设最小序列是
a1a2a4a3 ..an,且a4 > a3 即a4a3 > a3a4
交换a3a4的位置显然可以得到更小的一个数
a1a2|a3a4...an
*/
static bool cmp(int a, int b)
{
auto as = to_string(a), bs = to_string(b);
return as + bs < bs + as;
}
string minNumber(vector<int>& nums) {
sort(nums.begin(), nums.end(), cmp);
string res;
for (auto x: nums) res += to_string(x);
return res;
}
};
46 数字翻译成字符串
class Solution {
public:
int translateNum(int num) {
/*
1 状态表示 dp[i] 前i位数字有多少中不同表示
2 状态计算
第i位数翻译成一位: dp[i-1]
i-1,i翻译成一位: dp[i-2]
必须在10~25
dp[i] = dp[i-1] + dp[i-2]
3 边界
dp[0] = 1
*/
auto s = to_string(num);
int n = s.size();
vector<int> dp(n + 1);
dp[0] = 1;
dp[1] = 1;
for (int i = 2; i <= n; i ++)
{
dp[i] = dp[i - 1];
int t = (s[i - 2] - '0') * 10 + (s[i-1] - '0');
if (t >=10 && t <= 25) dp[i] += dp[i - 2];
}
return dp[n];
}
};
47 礼物的最大价值
class Solution {
public:
int maxValue(vector<vector<int>>& grid) {
/*
1状态表示dp[i,j]走到当前各自能够获得的最大价值是多少
2 状态转移
从上方dp[i-1,j] 或者从左边dp[i,j-1]的最大值
dp[i,j] = max(dp[i-1,j],dp[i, j -1]) + grid[i][j]
3 状态初始化
dp[i,0] = f[0,j] = 0;
*/
int m = grid.size(), n = grid[0].size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
for (int i = 1; i <= m; i ++)
for (int j = 1; j <= n; j ++)
dp[i][j] = max(dp[i-1][j], dp[i][j - 1]) + grid[i - 1][j - 1];
return dp[m][n];
}
};
48 最长不含重复子串
class Solution {
public:
int lengthOfLongestSubstring(string s) {
/*暴力做法
先确定结尾,然后枚举前缀最长不重复子串
双指针做法:借助哈希表
如果i,j之间有重复,那么把i往后移动一位
最后取最大值
*/
unordered_map<char, int> cnt;
int res = 0;
for (int i = 0, j = 0; j < s.size(); j ++)
{
cnt[s[j]]++;
if (cnt[s[j]] > 1)
{
while (cnt[s[i]] == 1)
{
cnt[s[i]] --;
i++;
}
cnt[s[i]] --;
i++;
}
res = max(res, j- i + 1);
}
return res;
}
};
49 丑数
class Solution {
public:
int nthUglyNumber(int n) {
/*三路归并
假设丑数序列为a1 a2 a3 a4 a5...
设置三个序列
nums1 = a1*2 a2*2 a3*2..
nums2 = a1*3 a2*3 a3*3..
nums3 = a1*5 a2*5 a3*5..
那么这三个序列一定是丑数序列,如何避免遗漏呢?
归并排序的思想
t = min(2*dp[i],3*dp[j],5*dp[k])
*/
if (n <= 1) return n;
vector<int> dp(1, 1);
int i = 0, j = 0, k = 0;
long long t = 0;
while (--n)
{
t = min(min(2 * dp[i], 3 * dp[j]), 5 * dp[k]);
if (t == 2 * dp[i]) i ++;
if (t == 3 * dp[j]) j ++;
if (t == 5 * dp[k]) k ++;
dp.push_back(t);
}
return dp.back();
}
};
50 第一个只出现一次的字符
class Solution {
public:
char firstUniqChar(string s) {
unordered_map<char, int> cnt;
char res = ' ';
for (auto x: s) cnt[x] ++;
for (auto x: s)
if (cnt[x] == 1)
{
res = x;
break;
}
return res;
}
};
day6
51 数组中的逆序对
class Solution {
public:
int merge_sort(vector<int>& nums, int l, int r)
{
if (l >= r) return 0;
int mid = l + r >> 1;
int res = merge_sort(nums, l, mid) + merge_sort(nums, mid + 1, r);
vector<int> temp;
int i = l, j = mid + 1;
while (i <= mid && j <= r)
{
if (nums[i] > nums[j])
{
temp.push_back(nums[j++]);
res += mid - i + 1;
}
else temp.push_back(nums[i++]);
}
while (i <= mid) temp.push_back(nums[i++]);
while (j <= r) temp.push_back(nums[j++]);
i = l;
for (auto x: temp) nums[i++] = x;
return res;
}
int reversePairs(vector<int>& nums) {
//暴力做法:冒泡排序 n2
// 归并排序模板题 nlogn
return merge_sort(nums, 0, nums.size() - 1);
}
};
52 两个链表的第一个公共节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
/*因为不等长,所以把两个链表拼接起来就登场了
headA headB 时就相交了*/
ListNode* prea = headA;
ListNode* preb = headB;
while (prea != preb)
{
prea = prea != NULL? prea -> next: headB;
preb = preb != NULL? preb -> next: headA;
}
return prea;
}
};
53I 排序数组中查找数字
class Solution {
public:
int search(vector<int>& nums, int target) {
if (nums.empty()) return 0;
int l = 0, r = nums.size() - 1;
// 分为三个区间
// <target =target > target
while (l < r)
{
// 只要小于k就会往右走,直到找到左端点
int mid = (l + r) / 2;
if (nums[mid] < target) l = mid + 1;
else r = mid;
}
if (nums[l] != target) return 0;
int left = l;
l = 0, r = nums.size() - 1;
while (l < r)
{
//只要小于等于k就会往右走,直到找到做端点
//l == mid时注意向上取整
int mid = (l + r + 1) / 2;
if (nums[mid] <= target) l = mid;
else r = mid - 1;
}
return r - left + 1;
}
};
53II 0~n-1中缺失的数字
class Solution {
public:
int missingNumber(vector<int>& nums) {
int n = nums.size();
int res = n * (n + 1) / 2; // 1~n 总共有n个数
for (auto x: nums) res -= x;
return res;
}
};
//二分查找
class Solution {
public:
int missingNumber(vector<int>& nums) {
int l = 0, r = nums.size() - 1;
while (l < r)
{
int mid = l + r >> 1;
if (nums[mid] > mid) r = mid - 1;
else if (nums[mid] == mid) l = mid + 1;
else continue;
}
if (nums[l] == l) return l + 1;
return l;
}
};
53III 数组中数值和下标相等的元素
class Solution {
public:
int getNumberSameAsIndex(vector<int>& nums) {
int l = 0, r = nums.size() - 1;
while (l < r)
{
// nums[i] - nums[i-1] >= 0
// i - i + 1 >= 0
// nums[i] - i - nums[i-1] + i + 1 >=0
// nums[i] - i 是单调递增的
int mid = l + r >>1;
if (nums[mid] - mid > 0) r = mid;
else if (nums[mid] - mid < 0) l = mid + 1;
else return mid;
}
if (nums[l] == l) return l;
return -1;
}
};
54 二叉搜索树的第k大节点
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* ans;
int kthLargest(TreeNode* root, int k) {
dfs(root, k);
return ans->val;
}
void dfs(TreeNode* root, int& k)
{
if (!root) return;
dfs(root-> right, k);
k --;
if (k == 0) ans = root;
if (k > 0 ) dfs(root->left, k);
}
};
55I 二叉树的深度
class Solution {
public:
int maxDepth(TreeNode* root) {
// 左子树深度+右子树深度+1
if (!root) return 0;
return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}
};
55II 平衡二叉树
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool ans = true;
bool isBalanced(TreeNode* root) {
dfs(root);
return ans;
}
int dfs(TreeNode* root)
{
if (!root) return 0;
int ldepth = dfs(root->left);
int rdepth = dfs(root->right);
if (abs(ldepth - rdepth) > 1) ans = false;
return max(ldepth, rdepth) + 1;
}
};
56I 数组中数字出现的次数
class Solution {
public:
vector<int> singleNumbers(vector<int>& nums) {
// 异或操作可以得到集合中计数为1的数(只能有1个)
// 如果有两个 异或结果为 x^y 其二进制表示一定有一位为1,另一个数该位为0
// 将所有数分成1集合和0集合,然后进行异或即可找出两个集合中计数为1的数
int sum = 0;
for (auto x: nums) sum ^= x;
int k = 0;
while (!(sum >> k & 1)) k ++;
int first = 0;
for (auto x: nums)
if (x >> k & 1)
first ^= x;
return vector<int>{first, sum ^ first};
}
};
56II 数组中数字出现的次数
class Solution {
public:
int singleNumber(vector<int>& nums) {
// // 唯一数出现1次,其他出现3次
// // 有限状态机
// // 对于x的所有位(0,0)-> (1,0) -> (0,1) -> (0,0)
// int ones = 0, twos = 0;
// for (auto x: nums)
// {
// ones = (ones ^ x) & (~twos);
// twos = (twos ^ x) & (~ones);
// }
// // ones相当于x的所有位 因为0遇到0->0 0遇到1->1
// return ones;
int ans = 0;
for (int i = 0; i < 32; i ++)
{
int cnt = 0;
for (auto x: nums)
if (x >> i & 1) cnt ++;
if (cnt % 3)
ans = ans | 1 << i;//%3=1的必定为唯一数
}
return ans;
}
};
57I 和为s的数字
//两数之和
class Solution {
public:
unordered_set<int> hash;
vector<int> twoSum(vector<int>& nums, int target) {
for (auto x: nums)
{
if (hash.count(target-x)) return vector<int> {target-x, x};
else hash.insert(x);
}
return vector<int> {-1,-1};
}
};
57II 和为s的连续正数序列
//双指针的题目
class Solution {
public:
vector<vector<int>> findContinuousSequence(int target) {
vector<vector<int>> res;
for(int i = 1, j = 1 ,s = 1; i <= target; i ++)
{
while (s < target) s += ++ j;
if (s == target && j - i + 1 > 1)
{
vector<int> line;
for (int k = i; k <= j; k ++) line.push_back(k);
res.push_back(line);
}
s -= i;//每次右移需要减去i
}
return res;
}
};
58I 翻转单词顺序
class Solution {
public:
string reverseWords(string s) {
string res;
for (int i = 0; i < s.size(); i ++)
{
while (s[i] == ' ') i ++;//start of word
int j = i;
while (j < s.size() && s[j] != ' ') j ++;//end of word
string word;
for (int k = i; k < j; k ++) word += s[k];//word
if (j < s.size()) res = ' ' + word + res;
else if (j) res = word + res;
i = j;
}
return res[0] == ' '? res.substr(1) : res;
}
};
58II 左旋转字符串
class Solution {
public:
string reverseLeftWords(string s, int n) {
// 全部翻转
// 前n-k和翻转 后k个翻转
reverse(s.begin(), s.end());
reverse(s.begin(), s.begin() + s.size() - n);
reverse(s.begin() + s.size() - n,s.end());
return s;
}
};
59 队列的最大值
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> res;
deque<int> que;
for (int i = 0; i <nums.size(); i++)
{
if (que.size() && i - que.front() + 1 > k)
que.pop_front();
while (que.size() && nums[que.back()] <= nums[i])
que.pop_back();
que.push_back(i);
if (que.size() && i >= k - 1) res.push_back(nums[que.front()]);
}
return res;
}
};
60I n个骰子的点数次数
// 可能的点数列表
// 背包问题:分组背包
class Solution {
public:
vector<int> numberOfDice(int n) {
vector<int> res;
vector<int> dp(6 * n + 1, 0);//可能的得分
for (int i = 1; i <= 6; i ++) dp[i] = 1;//第一次扔骰子1~6的投法都为1
for (int i = 2; i <= n; i++) //分组数
for (int j = 6 * i; j >= 0; j --) // 物品数 倒序遍历避免 当前dp[j-k]覆盖过去的dp[j-k]
{
dp[j] = 0;//清空数组,dp[j]不会由上一个同一得分求出
for (int k = 6; k >=1; k--)//价值
if (j > k)
dp[j] += dp[j-k];
}
for (int i = n; i < 6 * n + 1; i ++) res.push_back(dp[i]);
return res;
}
};
60II n个骰子的点数概率
//可能的点数对应的概率
class Solution {
public:
vector<double> dicesProbability(int n) {
//取值范围为n-6n 5n + 1中可能的值
//dp[N][i] 表示有n颗骰子且和为i时的概率
//dp[1][i] = 1/ 6
//dp[2][i] = dp[1][i-1]+dp[1][i-2]...+dp[1][i-6]
//res = dp[N]
//当前骰子点数之和i+1,,i+2, i+6,只能由dp即过去的的i中递推过来
//如果反向推到会导致数组越界
vector<double> dp(6, 1.0/6.0);
for (int i = 2; i <= n; i ++)
{
vector<double> temp(5*i + 1, 0.0);
for (int j = 0; j < dp.size(); j ++)
for (int k = 0; k < 6; k ++)
temp[j + k] += dp[j] * (1.0/ 6.0);
dp = temp;
}
return dp;
}
};
day7
61 扑克牌的顺子
class Solution {
public:
bool isStraight(vector<int>& nums) {
/*1 删掉0
2 重复
3 差<=4
*/
if (!nums.size()) return false;
sort(nums.begin(), nums.end());
int k = 0;
while (!nums[k]) k ++;
for (int i = k + 1; i < nums.size(); i ++)
if (nums[i] == nums[i-1])
return false;
return nums.back() - nums[k] <= 4;
}
};
62 圆圈中最后剩下的数字(约瑟夫环)
class Solution {
public:
int lastRemaining(int n, int m) {
/*
n个人每次删去第m个,最终剩下dp[n][m]
0 1 2 ... m-1 m m+1 m+2 ...n
重新编号 . —— 0 1 2
n-1个人则是,重新编号后的dp[n-1,m] (此时原来的编号为 新的编号+m)%n右移了m位
dp[n][m] = (dp[n-1][m] + m) % n
dp[1][m] = 0
*/
// vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
// for (int i = 2; i <= n; i ++)
// for (int j = 1; j <= m; j ++)
// dp[i][j] = (dp[i-1][j] + j) % i;
// return dp[n][m];
if (n == 1) return 0;
return (lastRemaining(n - 1, m) + m) % n;
}
};
63 股票的最大利润
class Solution {
public:
int maxProfit(vector<int>& prices) {
int minPrice = INT_MAX;
int res = 0;
for (auto p: prices)
{
if (p < minPrice) minPrice = p;
res = max(res, p - minPrice);
}
return res;
}
};
64 1+2+…+n
class Solution {
public:
int sumNums(int n) {
int res = n;
(n >0) && (res += sumNums(n- 1));
return res;
}
};
65 不用加减乘除的加法
class Solution {
public:
int add(int a, int b) {
// 位运算在计算机中的加法
// a + b = cd
// d = a^b
// c = a&b
/*
不考虑所有进位计算 a^b
再加上所有进位 a&b 有进位的地方都是1,需要把结果左移1 a&b << 1;
结果等于 a^b + a&b << 1 新的两个数相加
b中的进位总会消失
*/
while (b)
{
int sum1 = a ^ b;
unsigned int carry = (unsigned int)(a & b) << 1;
a = sum1;
b = carry;//
}
return a;
}
};
66 构建乘积数组
class Solution {
public:
vector<int> constructArr(vector<int>& a) {
if (!a.size()) return vector<int>();
vector<int> res(a.size(),1);
//左半边先乘进去 1 2 3 4 5
// 1 1 2 6 24
for (int i = 0, lprod = 1; i < a.size(); i ++){
res[i] = lprod;
lprod*= a[i];
}
//右半边再乘进去
// i= 4 24*1 右边没有 rprod = 5
// i= 3 6*5 右边是5 左边是123 rprod = 20
// i= 2 2*20 左边是12 右边是45 rprod=60
// i= 1 1*60 左边是1 rprod = 120
// i= 0 120
for (int i = a.size() - 1, rprod = 1; i >= 0; i --)
{
res[i]*= rprod;
rprod*= a[i];
}
return res;
}
};
67 字符串转换为整数
class Solution {
public:
int strToInt(string str) {
int k = 0;
while (k < str.size() && str[k] == ' ') k ++;
long long num = 0;
bool is_minus = false;
if (str[k] == '+') k ++;
else if (str[k] == '-')
{
k++;
is_minus=true;
}
while (k < str.size() && str[k] >= '0' && str[k] <= '9')
{
num = num * 10 + str[k] - '0';
k++;
if (!is_minus && num > INT_MAX) return INT_MAX;
if (is_minus && num > INT_MAX) return INT_MIN;
}
if (is_minus) num *= -1;
return (int)num;
}
};
68 二叉树的最低公共祖先
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
/*
在左右两边,返回根节点
都在左边,公共祖先在左边
都在右边,公共祖先在右边
*/
if (!root) return nullptr;
if (root == p || root == q) return root;
auto left = lowestCommonAncestor(root->left, p, q);//查找左子树中是否存在p q节点
auto right = lowestCommonAncestor(root->right,p, q);//查找右子树中是否存在p q节点
if (left && right) return root;
else if (left) return left;
return right;
}
};
最后
以上就是激情电源为你收集整理的剑指offer刷题笔记整理day1day2day3day4day5day6day7的全部内容,希望文章能够帮你解决剑指offer刷题笔记整理day1day2day3day4day5day6day7所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复