剑指Offer
Day1
用两个栈实现队列
剑指 Offer 09. 用两个栈实现队列 - 力扣(LeetCode) (leetcode-cn.com)
栈A用于存插入的数据,栈B用于出栈
插入函数,直接在A中入栈即可,出队时先看栈B是否为空,不为空直接输出B栈顶
否则看看A是否为空,为空的话说明没有数据
不是上述两种情况,则将A中的数据出栈后插入B中,再取出B的栈顶元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class CQueue { stack<int > A,B; public : CQueue () { while (A.size ())A.pop (); while (B.size ())B.pop (); } void appendTail (int value) { A.push (value); } int deleteHead () { int x; if (B.size ()){ x = B.top (); B.pop (); }else if (A.empty ()) x = -1 ; else { while (A.size ()){ B.push (A.top ()); A.pop (); } x = B.top (); B.pop (); } return x; } };
包含min函数的栈
剑指 Offer 30. 包含min函数的栈 - 力扣(LeetCode) (leetcode-cn.com)
用两个栈,第一个用来正常存,第二个用来存当前栈的最小值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class MinStack { public : stack<int > A,B; MinStack () { } void push (int x) { A.push (x); int min_x = (B.empty ()||x<B.top ())?x:B.top (); B.push (min_x); } void pop () { A.pop (); B.pop (); } int top () { return A.top (); } int min () { return B.top (); } };
Day2
从尾到头打印链表
遍历链表,将节点数据存入到栈中,然后再出栈,就行了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution {public : vector<int > reversePrint (ListNode* head) { stack<int > res; ListNode* p = head; while (p){ res.push (p->val); p = p->next; } vector<int > num; while (res.size ()){ num.push_back (res.top ()); res.pop (); } return num; } };
反转链表
剑指 Offer 24. 反转链表 - 力扣(LeetCode) (leetcode-cn.com)
解法1:遍历这个链表,然后用头插法插入每一个元素(有手就行,代码就不给了)
解法2:直接做,思路的话看着代码自己画一遍就懂了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution {public : ListNode* reverseList (ListNode* head) { if (head == NULL )return head; ListNode *p=head,*q=p->next,*pt=NULL ; while (q){ p->next = pt; pt = p; p = q; q = q->next; } p->next = pt; return p; } };
复杂链表的复制
在这题中,首先需要将复制的节点接到对应节点的next上,然后根据原来节点的random改变复制节点的randomp->next->random=p->random->next
,然后把链表分开即可。
剑指 Offer 35. 复杂链表的复制 - 力扣(LeetCode) (leetcode-cn.com)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 class Solution {public : Node* copyRandomList (Node* head) { for (Node* p = head;p;){ Node* tmp = new Node (p->val); Node* next = p->next; p->next = tmp; tmp->next = next; p = next; } for (Node* p = head;p;p=p->next->next) { if (p->random){ p->next->random = p->random->next; } } Node* h = new Node (-1 ); Node* now = h; for (Node* p = head;p;p=p->next){ now->next = p->next; now = now->next; p->next = now->next; } return h->next; } };
Day3
替换空格
剑指 Offer 05. 替换空格 - 力扣(LeetCode) (leetcode-cn.com)
记录一下原来字符串的长度,然后遍历字符串,看一下有多少个空格,每个空格会替换成%20,多增加2个字符
所以最终字符串的长度为s.size()+2*cnt
。然后用双指针,从原来的长度,和新的字符串长度的最后的位置向前,如果原字符串不为空格就直接等,不然就要替换掉,并且新字符串的指针要减去2。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : string replaceSpace (string s) { int cnt=0 ,len=s.size (); for (char a:s) if (a==' ' )cnt++; s.resize (s.size ()+2 *cnt); for (int i = len-1 ,j=s.size ()-1 ;i<j;i--,j--){ if (s[i]!=' ' )s[j]=s[i]; else { s[j]='0' ; s[j-1 ]='2' ; s[j-2 ]='%' ; j-=2 ; } } return s; } };
做旋转字符串
剑指 Offer 58 - II. 左旋转字符串 - 力扣(LeetCode) (leetcode-cn.com)
解法1 :暴力解法,先记录字符串的长度,然后substr
字符串前面指定长度的字符串添加到右边,然后在用substr
,从指定位置长度开始向后取出原来字符串的长度
1 2 3 4 5 6 7 8 class Solution {public : string reverseLeftWords (string s, int n) { int len = s.size (); s = s+s.substr (0 ,n); return s.substr (n,len); } };
解法2 :翻转字符串三次即可
第一次:翻转前n个字符
第二次:再翻转n后面的字符
第三次:翻转整个字符串
比如
abcdef 3
第一次:cba def
第二次:cba fed
第三次:defabc
1 2 3 4 5 6 7 8 9 class Solution {public : string reverseLeftWords (string s, int n) { reverse (s.begin (),s.begin ()+n); reverse (s.begin ()+n,s.end ()); reverse (s.begin (),s.end ()); return s; } };
Day4
数组中重复的数字
剑指 Offer 03. 数组中重复的数字 - 力扣(LeetCode) (leetcode-cn.com)
我的解法:遍历数组,存入哈希表中,key是数组对应数字的值,value是其出现的次数,当次数大于1时就返回
1 2 3 4 5 6 7 8 9 10 11 class Solution {public : int findRepeatNumber (vector<int >& nums) { unordered_map<int ,int > mp; for (int a:nums){ if (mp[a])return a; mp[a]++; } return 0 ; } };
在排序数组中查找数字
剑指 Offer 53 - I. 在排序数组中查找数字 I - 力扣(LeetCode) (leetcode-cn.com)
解法1 :暴力方法
遍历数组,记录目标值出现的次数,O ( n ) O(n) O ( n ) ,代码:有手就行
解法2 :二分查找法
首先用一个二分查找其左边界,再用另一个二分查找其右边界,然后右边界-左边界+1
即可,O ( 2 l o g N ) = O ( l o g N ) O(2logN)=O(logN) O ( 2 l o g N ) = O ( l o g N )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Solution {public : int search (vector<int >& nums, int target) { if (!nums.size ())return 0 ; int l=0 ,r=nums.size ()-1 ,mid; while (l<r){ mid = l+r>>1 ; if (nums[mid]>=target) r = mid; else l = mid+1 ; } int pos = l; if (nums[pos]!=target)return 0 ; l = 0 ,r = nums.size ()-1 ; while (l<r){ mid=l+r+1 >>1 ; if (nums[mid]<=target) l=mid; else r = mid-1 ; } return l-pos+1 ; } };
0~n-1中缺失的数字
剑指 Offer 53 - II. 0~n-1中缺失的数字 - 力扣(LeetCode) (leetcode-cn.com)
解法1 :暴力解法
遍历数组,找出来即可,O ( n ) O(n) O ( n )
1 2 3 4 5 6 7 8 9 class Solution {public : int missingNumber (vector<int >& nums) { for (int i = 0 ;i<nums.size ();i++) if (nums[i]!=i) return i; return nums.size (); } };
解法2 :二分查找法
下标:0 1 2 3 4 5 6(0~n-1)
数组:0 1 2 4 5 6 7 (给的数组)
从上面可以看出,缺失的数字是3,在3的位置,左边的数字和下标是相等的右边是不等的,可以根据这个性质进行二分。O ( l o g N ) O(logN) O ( l o g N )
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution {public : int missingNumber (vector<int >& nums) { int l = 0 ,r = nums.size ()-1 ; while (l<r){ int mid = l+r>>1 ; if (mid != nums[mid]) r = mid; else l = mid+1 ; } if (nums[l]==l)l++; return l; } };
Day5
今天拉了,竟然每题都只能暴力做
二维数组中的查找
剑指 Offer 04. 二维数组中的查找 - 力扣(LeetCode) (leetcode-cn.com)
解法1 :暴力方法,O ( n 2 ) O(n^2) O ( n 2 )
1 2 3 4 5 6 7 8 9 10 class Solution {public : bool findNumberIn2DArray (vector<vector<int >>& matrix, int target) { for (vector<int >a :matrix) for (int b:a) if (b==target) return true ; return false ; } };
解法2 :二分的方法
选择矩阵的右上角这个点开始找,假设这个点的位置为x,如果我们的目标比x大,在x的左边他的值一定小于x,并且目标比x大,且这个点没有右边,所以这个时候就可以向下找一行。
同理,如果我们的目标比x小,他下面的目标一定比x大,所以也一定比目标大,所以这一列就没有答案了,这时候可以向左一行寻找。
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution {public : bool findNumberIn2DArray (vector<vector<int >>& matrix, int target) { if (matrix.empty ())return false ; int x=0 ,y=matrix[0 ].size ()-1 ; while (x<matrix.size ()&&y>=0 ){ if (matrix[x][y]==target)return true ; if (matrix[x][y]>target)y--; else x++; } return false ; } };
旋转数组的最小数字
剑指 Offer 11. 旋转数组的最小数字 - 力扣(LeetCode) (leetcode-cn.com)
解法1 :暴力方法,O ( n ) O(n) O ( n )
1 2 3 4 5 6 7 8 9 class Solution {public : int minArray (vector<int >& numbers) { int mins = 1e9 ; for (int a:numbers) if (a<mins)mins = a; return mins; } };
解法2 :二分的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution {public : int minArray (vector<int >& numbers) { if (numbers.empty ())return -1 ; int n = numbers.size ()-1 ; while (n>0 &&numbers[n]==numbers[0 ])n--; if (numbers[n]>=numbers[0 ])return numbers[0 ]; int l = 0 ,r = n; while (l<r){ int mid = l+r>>1 ; if (numbers[mid]<numbers[0 ])r = mid; else l = mid+1 ; } return numbers[l]; } };
第一个只出现一次的字符
剑指 Offer 50. 第一个只出现一次的字符 - 力扣(LeetCode) (leetcode-cn.com)
暴力模拟解法
这里其实可以不用队列,因为我这里遍历的是哈希表,他是无序的所以要队列表示一下顺序
如果重新遍历字符串的话,就不需要队列了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution {public : char firstUniqChar (string s) { if (!s.size ())return ' ' ; unordered_map<char ,int > mp; queue<char > q; for (char a:s){ if (!mp[a])q.push (a); mp[a]++; } char ans = ' ' ; while (q.size ()){ if (mp[q.front ()]==1 ){ ans = q.front (); break ; } else q.pop (); } return ans; } };
Day6
从上到下打印二叉树
面试题32 - I. 从上到下打印二叉树 - 力扣(LeetCode) (leetcode-cn.com)
从上到下表示从左到右输出上到下的每一层,用二叉树的宽搜就行了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Solution {public : vector<int > levelOrder (TreeNode* root) { vector<int > res; queue<TreeNode*> q; if (!root)return res; q.push (root); while (q.size ()){ TreeNode* now = q.front (); q.pop (); res.push_back (now->val); if (now->left)q.push (now->left); if (now->right)q.push (now->right); } return res; } };
从上到下打印二叉树2
剑指 Offer 32 - II. 从上到下打印二叉树 II - 力扣(LeetCode) (leetcode-cn.com)
这题不太一样,他每一层的数都要做成一个数组
解法1 :暴力解法
同样是宽搜,不过这次用了两个队列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class Solution {public : vector<vector<int >> levelOrder (TreeNode* root) { queue<TreeNode*> q1,q2; vector<vector<int >> res; if (!root)return res; q1.push (root); while (q1.size ()||q2.size ()){ vector<int > tmp; while (q1.size ()){ TreeNode* now = q1.front (); q1.pop (); tmp.push_back (now->val); if (now->left)q2.push (now->left); if (now->right)q2.push (now->right); } if (tmp.size ())res.push_back (tmp); tmp.clear (); while (q2.size ()){ TreeNode* now = q2.front (); q2.pop (); tmp.push_back (now->val); if (now->left)q1.push (now->left); if (now->right)q1.push (now->right); } if (tmp.size ())res.push_back (tmp); } return res; } };
解法2 :在每一层的结束添加一个空指针,减少了多个队列的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class Solution {public : vector<vector<int >> levelOrder (TreeNode* root) { vector<vector<int >> res; if (!root)return res; queue<TreeNode*> q; q.push (root); q.push (nullptr ); vector<int > tmp; while (q.size ()){ TreeNode* t = q.front (); q.pop (); if (!t){ if (tmp.empty ())break ; res.push_back (tmp); tmp.clear (); q.push (nullptr ); continue ; } tmp.push_back (t->val); if (t->left)q.push (t->left); if (t->right)q.push (t->right); } return res; } };
从上到下打印二叉树3
剑指 Offer 32 - III. 从上到下打印二叉树 III - 力扣(LeetCode) (leetcode-cn.com)
这次是按Z字型,分层打印了,在第二题的基础上加个flag,确定插入结果的数组是否需要翻转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 class Solution {public : vector<vector<int >> levelOrder (TreeNode* root) { vector<vector<int >> res; if (!root)return res; queue<TreeNode*> q; q.push (root); q.push (nullptr ); vector<int > tmp; bool flag = false ; while (q.size ()){ TreeNode* t = q.front (); q.pop (); if (!t){ if (tmp.empty ())break ; if (flag)reverse (tmp.begin (),tmp.end ()); flag = !flag; res.push_back (tmp); tmp.clear (); q.push (nullptr ); continue ; } tmp.push_back (t->val); if (t->left)q.push (t->left); if (t->right)q.push (t->right); } return res; } };
Day7
树的子结构
剑指 Offer 26. 树的子结构 - 力扣(LeetCode) (leetcode-cn.com)
主要考察边界条件的判断,这题用的是dfs,详解见代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Solution {public : bool isSubStructure (TreeNode* A, TreeNode* B) { if (!A||!B)return false ; if (ispart (A,B))return true ; return isSubStructure (A->left,B) || isSubStructure (A->right,B); } bool ispart (TreeNode*A,TreeNode*B) { if (!B)return true ; if (!A||A->val != B->val)return false ; return ispart (A->left,B->left)&&ispart (A->right,B->right); } };
二叉树的镜像
剑指 Offer 27. 二叉树的镜像 - 力扣(LeetCode) (leetcode-cn.com)
实现一个交换函数,然后先交换root节点,然后递归镜像其左右子树即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Solution {public : TreeNode* mirrorTree (TreeNode* root) { if (!root)return NULL ; swapTreeNode (root); mirrorTree (root->left); mirrorTree (root->right); return root; } void swapTreeNode (TreeNode* head) { if (head){ TreeNode* tmp = head->left; head->left = head->right; head->right = tmp; } } };
对称的二叉树
剑指 Offer 28. 对称的二叉树 - 力扣(LeetCode) (leetcode-cn.com)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Solution {public : bool isSymmetric (TreeNode* root) { if (!root)return true ; return dfs (root->left,root->right); } bool dfs (TreeNode* a, TreeNode*b) { if (!a||!b)return !a&&!b; if (a->val!=b->val)return false ; return dfs (a->right,b->left)&&dfs (a->left,b->right); } };
Day8
斐波那契数列
剑指 Offer 10- I. 斐波那契数列 - 力扣(LeetCode) (leetcode-cn.com)
这题刚学过编程的人都会了,我就不多说了,递归的方法会TLE,所以这里用迭代的方法计算。
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution {public : int fib (int n) { const int N = 110 ,mod = 1e9 +7 ; int f[N]; memset (f,0 ,sizeof f); f[1 ]=1 ; for (int i = 2 ;i<=n;i++){ f[i] = (f[i-1 ]+f[i-2 ])*1ll %mod; } return f[n]; } };
青蛙跳台问题
剑指 Offer 10- II. 青蛙跳台阶问题 - 力扣(LeetCode) (leetcode-cn.com)
这题就是斐波那契数列套壳,青蛙可以跳一级,也可以跳两级,也就是说如果当前位置是i的话,他要么从i-1的位置来,要么从i-2的位置来
那么最终的结果肯定和斐波那契数列一样有通项公式f [ i ] = f [ i − 1 ] + f [ i − 2 ] f[i] = f[i-1]+f[i-2] f [ i ] = f [ i − 1 ] + f [ i − 2 ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution {public : int numWays (int n) { const int N = 110 ,mod = 1e9 +7 ; int f[N]; memset (f,0 ,sizeof f); f[1 ] = 1 ; f[0 ] = 1 ; for (int i = 2 ;i<=n;i++){ f[i] = (f[i-1 ]+f[i-2 ])*1ll %mod; } return f[n]; } };
股票的最大利润
剑指 Offer 63. 股票的最大利润 - 力扣(LeetCode) (leetcode-cn.com)
明明是暴力题还要打上dp的tag,误导人是吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : int maxProfit (vector<int >& prices) { if (prices.empty ())return 0 ; int min_v = prices[0 ],ans=0 ; for (int i = 1 ;i<prices.size ();i++){ if (min_v>prices[i])min_v = prices[i]; else ans = max (ans,prices[i]-min_v); } return ans; } };
Day9
(昨天没写,今天补上)
连续子数组的最大和
剑指 Offer 42. 连续子数组的最大和 - 力扣(LeetCode) (leetcode-cn.com)
f[i]
表示以i结尾的连续子数组最大和的值,然后遍历一遍f[i]=max(f[i-1]+nums[i],nums[i])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution {public : int maxSubArray (vector<int >& nums) { int f[100010 ]; memset (f,0 ,sizeof f); f[0 ] = nums[0 ]; for (int i = 1 ;i<nums.size ();i++){ f[i] = max (f[i-1 ]+nums[i],nums[i]); } int res = -110 ; for (int i = 0 ;i<nums.size ();i++){ if (res<f[i]) res = f[i]; } return res; } };
礼物的最大价值
剑指 Offer 47. 礼物的最大价值 - 力扣(LeetCode) (leetcode-cn.com)
这题就是计算一下当前位置向右向下取得的价值是否比对应位置的最大价值大就行了,f[i][j]
作为转换方程,他表示在(i,j)位置取得礼物的最大值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : int maxValue (vector<vector<int >>& grid) { vector<vector<int >> f (grid.size (),vector<int >(grid[0 ].size ())); f[0 ][0 ] = grid[0 ][0 ]; int dx[]={1 ,0 },dy[]={0 ,1 }; for (int i = 0 ;i<grid.size ();i++){ for (int j=0 ;j<grid[0 ].size ();j++){ for (int k = 0 ;k<2 ;k++){ int x = i+dx[k],y = j+dy[k]; if (x<grid.size ()&&y<grid[0 ].size ()){ f[x][y] = max (f[x][y],f[i][j]+grid[x][y]); } } } } return f[grid.size ()-1 ][grid[0 ].size ()-1 ]; } };
Day10
把数字翻译成字符串
剑指 Offer 46. 把数字翻译成字符串 - 力扣(LeetCode) (leetcode-cn.com)
f[i]
表示前i个字符可以翻译的种数
比如:1223
1 可以翻译成a
12 可以翻译成ab,J
122可以翻译成abb,jb,av
1223可以翻译成abbc,jbc,avc,abw,jw
共5种
观察一下,第一个字符只有一种,得到初始条件f[0]=1
第二行,第一个字符已经确定了有f[0]
种,然后本身直接翻译,就是一种等于f[0]
,再观察一下当前和前一个组合是否在范围内,12显然在,所以在f[0]
的基础上还要加上当前位置往后2格的种类数f[1-2]=f[-1]
越界了,所以默认为1
第三行,前两个字符已经确定了,本身直接翻译就有f[3-1]=f[2]
种,再观察前一个和当前的组合是否在范围内,22显然在,所以在f[2]
的基础上还要加上f[2-2]=f[0]=1
,所以f[3]=f[2]+f[0]=2+1=3
于是可以得到转化方程f[i] = f[i-1] + 前一个和当前的组合在范围内?(当前位置-2之后不为负数?1:f[i-2]):0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution {public : int translateNum (int num) { string a = to_string (num); vector<int > f (a.size()) ; f[0 ] = 1 ; for (int i = 1 ;i<a.size ();i++){ f[i] = f[i-1 ]; if (a[i-1 ]=='1' ||(a[i-1 ]=='2' &&a[i]<'6' )){ if (i>1 )f[i] +=f[i-2 ]; else f[i]+=1 ; } } return f[a.size ()-1 ]; } };
最长不含重复字符的子字符串
[剑指 Offer 48. 最长不含重复字符的子字符串 - 力扣(LeetCode) (leetcode-cn.com) ](https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/ )
子串:连在一起的叫子串
子序列:可以不连在一起的叫子序列
定义目标串的起点和终点都在最初的位置,然后开始向后遍历终点,终点位置如果没有出现在哈希表里,就将当前位置的字符作为key,value为end+1放到哈希表中,每次更新最长的距离为num = max(num,end-start+1)
,如果当前字符已经出现过了,就确定是在start之前还是之后出现的,更细一下start的位置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution {public : int lengthOfLongestSubstring (string s) { unordered_map<char ,int > mp; int n=s.size (),start,end,res=0 ; for (start=0 ,end=0 ;end<n;end++){ if (mp[s[end]]){ start = max (start,mp[s[end]]); } res = max (res,end-start+1 ); mp[s[end]]=end+1 ; } return res; } };
Day11
删除链表的节点
剑指 Offer 18. 删除链表的节点 - 力扣(LeetCode) (leetcode-cn.com)
就单纯的链表删除方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution {public : ListNode* deleteNode (ListNode* head, int val) { if (!head)return head; if (head->val==val)return head->next; ListNode* p = head; while (p->next){ if (p->next->val==val){ p->next = p->next->next; break ; } p = p->next; } return head; } };
链表中倒数第K个节点
剑指 Offer 22. 链表中倒数第k个节点 - 力扣(LeetCode) (leetcode-cn.com)
先遍历一次看有多少个节点,再计算一下倒数第k个节点对应正数第几个节点nums-k
,然后在遍历一次就行了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Solution {public : ListNode* getKthFromEnd (ListNode* head, int k) { int nums = 0 ,pos = 0 ; ListNode* p = head; while (p){ nums++; p = p->next; } p = head; pos = nums-k; if (pos<0 )return nullptr ; while (pos--){ p = p->next; } return p; } };
Day12
合并两个排序的链表
剑指 Offer 25. 合并两个排序的链表 - 力扣(LeetCode) (leetcode-cn.com)
就是归并排序的合并部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Solution {public : ListNode* mergeTwoLists (ListNode* l1, ListNode* l2) { ListNode * head = new ListNode (-1 ); ListNode*t = head,*p=l1,*q=l2; while (p&&q){ if (p->val<=q->val){ t->next = p; p = p->next; } else { t->next = q; q = q->next; } t = t->next; t->next = nullptr ; } if (p) t->next = p; if (q) t->next = q; return head->next; } };
两个链表的第一个公共节点
剑指 Offer 52. 两个链表的第一个公共节点 - 力扣(LeetCode) (leetcode-cn.com)
解法1 :使用哈希表存储每个节点出现的次数,如果出现了两次就直接返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Solution {public : ListNode *getIntersectionNode (ListNode *headA, ListNode *headB) { unordered_map<ListNode*,int > mp; int ans = 0 ; while (headA||headB){ if (headA){ if (mp[headA])return headA; mp[headA]++; headA = headA->next; } if (headB){ if (mp[headB])return headB; mp[headB]++; headB = headB->next; } } return nullptr ; } };
解法2 :就一直循环遍历两个链表,即使两个链表长度不一样,但经过有限次次循环后仍然能实现同时遍历到相交节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : ListNode *getIntersectionNode (ListNode *headA, ListNode *headB) { ListNode*p = headA,*q = headB; while (p!=q){ p = (p)?p->next:headA; q = (q)?q->next:headB; } return p; } };
Day13
调整数组顺序使奇数位于偶数前面
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面 - 力扣(LeetCode) (leetcode-cn.com)
感觉像快排的改进版
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution {public : vector<int > exchange (vector<int >& nums) { if (!nums.size ())return nums; int l = -1 ,r = nums.size (); while (l<r){ do l++; while (l<r&&nums[l]%2 ); do r--; while (l<r&&nums[r]%2 ==0 ); if (l<r) swap (nums[l],nums[r]); } return nums; } };
和为s的两个数字
剑指 Offer 57. 和为s的两个数字 - 力扣(LeetCode) (leetcode-cn.com)
解法1 :哈希大法
1 2 3 4 5 6 7 8 9 10 11 class Solution {public : vector<int > twoSum (vector<int >& nums, int target) { unordered_map<int ,int > mp; for (int a:nums){ if (a<target&&mp[target-a])return {target-a,a}; mp[a]++; } return {}; } };
解法2: :双指针法
双指针过程,左右加和比s大,右边左移,比s小,左边右移,等于返回
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution {public : vector<int > twoSum (vector<int >& nums, int target) { int l=0 ,r=nums.size ()-1 ; while (l<r){ int s = nums[l]+nums[r]; if (s>target)r--; else if (s<target)l++; else return {nums[l],nums[r]}; } return {}; } };
翻转单词顺序
剑指 Offer 58 - I. 翻转单词顺序 - 力扣(LeetCode) (leetcode-cn.com)
解法1 :直接翻转法
先把整个字符串翻转过来,然后从左往右遍历,如果是空格就停止,翻转这部分的字符串(但这个方法A不了,虽然没什么问题,为什么A不了?因为他要输出的结果太变态了)
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution {public : string reverseWords (string s) { reverse (s.begin (),s.end ()); int pos = 0 ,end; while ((end=s.find (' ' ,pos))!=string::npos){ reverse (s.begin ()+pos,s.begin ()+end); pos = end+1 ; } reverse (s.begin ()+pos,s.end ()); return s; } };
解法2 :上面的方法能A的版本
找个vector装起来吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution {public : string reverseWords (string s) { int l = 0 ; while (s[l]==' ' )l++; s = s.substr (l,s.size ()-l); vector<string> ans; string res; reverse (s.begin (),s.end ()); int pos = 0 ,end; while ((end=s.find (' ' ,pos))!=string::npos){ reverse (s.begin ()+pos,s.begin ()+end); if (s[pos]!=' ' ) ans.push_back (s.substr (pos,end-pos)); pos = end+1 ; } reverse (s.begin ()+pos,s.end ()); ans.push_back (s.substr (pos,s.size ()-pos)); res = ans[0 ]; for (int i = 1 ;i<ans.size ();i++) res = res+" " +ans[i]; return res; } };
解法3 :python有手就行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution (object ): def reverseWords (self, s ): """ :type s: str :rtype: str """ s = s.strip() data = s.split(' ' ) a = "" print (data) for i in range (1 ,len (data)+1 ): if data[len (data)-i]!='' : a = a+data[len (data)-i]+" " return a.strip()
Day14
矩阵中的路径
剑指 Offer 12. 矩阵中的路径 - 力扣(LeetCode) (leetcode-cn.com)
先确定起始位置,然后遍历位置的四个方向,确定是否与我们的字符串匹配,典型的dfs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Solution {public : bool st[210 ][210 ]; bool exist (vector<vector<char >>& board, string word) { for (int i = 0 ;i<board.size ();i++) for (int j = 0 ;j<board[0 ].size ();j++) if (word[0 ]==board[i][j]){ st[i][j] = true ; if (dfs (i,j,board,word.substr (1 ,word.size ()-1 ))) return true ; st[i][j] = false ; } return false ; } bool dfs (int x,int y,vector<vector<char >>& board,string word) { int dx[]={-1 ,0 ,1 ,0 },dy[]={0 ,1 ,0 ,-1 }; if (word=="" )return true ; for (int i = 0 ;i<4 ;i++){ int nx=x+dx[i],ny=y+dy[i]; if (nx>=0 &&ny>=0 &&nx<board.size ()&&ny<board[0 ].size ()&&!st[nx][ny]&&word[0 ]==board[nx][ny]){ st[nx][ny] = true ; if (dfs (nx,ny,board,word.substr (1 ,word.size ()-1 ))) return true ; st[nx][ny] = false ; } } return false ; } };
机器人的运动范围
剑指 Offer 13. 机器人的运动范围 - 力扣(LeetCode) (leetcode-cn.com)
这题也是dfs,先写个check函数,确保坐标位数之和小于k,然后深搜,如果当前位置没有来过,并且满足check,就总数+1,并且这个位置设置为true,然后遍历当前位置的四个方向,否则直接退出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Solution {public : int cnt; bool st[110 ][110 ]; int movingCount (int m, int n, int k) { dfs (0 ,0 ,m,n,k); return cnt; } void dfs (int x,int y,int m,int n,int k) { if (!st[x][y]&&check (x,y,k))cnt++,st[x][y]=true ; else return ; int dx[] = {-1 ,0 ,1 ,0 },dy[]={0 ,1 ,0 ,-1 }; for (int i = 0 ;i<4 ;i++){ int a = x+dx[i],b = y+dy[i]; if (a>=0 &&b>=0 &&a<m&&b<n&&!st[a][b]) dfs (a,b,m,n,k); } } bool check (int x,int y,int k) { int res = 0 ; while (x||y){ res = res+x%10 +y%10 ; x/=10 ; y/=10 ; } return res<=k; } };
Day15
二叉树中和为某一值的路径
剑指 Offer 34. 二叉树中和为某一值的路径 - 力扣(LeetCode) (leetcode-cn.com)
创建结果数组,和路径数组,然后深搜二叉树的路径,满足条件直接存入结果中即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class Solution {public : vector<vector<int >> ans; vector<int > path; vector<vector<int >> pathSum (TreeNode* root, int target) { dfs (root,0 ,target); return ans; } void dfs (TreeNode* root,int sum,int target) { if (root==nullptr )return ; path.push_back (root->val); sum+=root->val; if (root->left==nullptr &&root->right==nullptr ){ if (target==sum) ans.push_back (path); }else { dfs (root->left,sum,target); dfs (root->right,sum,target); } path.pop_back (); } };
二叉搜索树与双向链表
剑指 Offer 36. 二叉搜索树与双向链表 - 力扣(LeetCode) (leetcode-cn.com)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 class Solution {public : Node* treeToDoublyList (Node* root) { if (!root) return nullptr ; auto res = dfs (root); res.first->left = res.second; res.second->right = res.first; return res.first; } pair<Node*,Node*> dfs(Node* root){ if (root->left&&root->right){ auto l = dfs (root->left),r = dfs (root->right); l.second->right = root,root->left = l.second; root->right = r.first,r.first->left = root; return {l.first,r.second}; } if (root->left){ auto l = dfs (root->left); l.second->right = root,root->left = l.second; return {l.first,root}; } if (root->right){ auto r = dfs (root->right); root->right = r.first,r.first->left = root; return {root,r.second}; } return {root,root}; } };
二叉搜索树的第K大节点
剑指 Offer 54. 二叉搜索树的第k大节点 - 力扣(LeetCode) (leetcode-cn.com)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Solution {public : int res; int kthLargest (TreeNode* root, int k) { dfs (root,k); return res; } void dfs (TreeNode* root,int &k) { if (!root)return ; dfs (root->right,k); k--; if (!k)res = root->val; dfs (root->left,k); } };
Day16
把数组排成最小的数
剑指 Offer 45. 把数组排成最小的数 - 力扣(LeetCode) (leetcode-cn.com)
这题是一个·贪心题,排序的时候要满足一个要求,即AB的组合方式要比BA的组合方式小即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution {public : static bool cmp (int a,int b) { string A = to_string (a); string B = to_string (b); return A+B<B+A; } string minNumber (vector<int >& nums) { sort (nums.begin (),nums.end (),cmp); string res; for (int i = 0 ;i<nums.size ();i++) res += to_string (nums[i]); return res; } };
扑克牌中的顺子
剑指 Offer 61. 扑克牌中的顺子 - 力扣(LeetCode) (leetcode-cn.com)
模拟题,首先先去除0的数字,然后判断有没有两张牌相同,有就是false,反之则还要判断是否最大值和最小值之间的差值小于4,即可
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution {public : bool isStraight (vector<int >& nums) { if (nums.empty ())return false ; sort (nums.begin (),nums.end ()); int k =0 ; while (nums[k]==0 )k++; for (int i = k+1 ;i<nums.size ();i++) if (nums[i] == nums[i-1 ]) return false ; return nums.back ()-nums[k] <= 4 ; } };
Day17
最小的K个数
剑指 Offer 40. 最小的k个数 - 力扣(LeetCode) (leetcode-cn.com)
解法1 :暴力,直接排序输出o ( N l o g ( N ) + K ) o(Nlog(N)+K) o ( Nl o g ( N ) + K )
1 2 3 4 5 6 7 8 9 class Solution {public : vector<int > getLeastNumbers (vector<int >& arr, int k) { sort (arr.begin (), arr.end ()); if (k >= arr.size ()) return arr; vector<int > vec (arr.begin(), arr.begin()+k) ; return vec; } };
解法2 :用堆来存储,当堆中的数据大于k时,直接弹出最大值O ( N l o g ( K ) ) O(Nlog(K)) O ( Nl o g ( K ))
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution {public : vector<int > getLeastNumbers (vector<int >& arr, int k) { priority_queue<int > heap; for (int a:arr){ heap.push (a); if (heap.size ()>k)heap.pop (); } vector<int > res; while (heap.size ())res.push_back (heap.top ()),heap.pop (); return res; } };
数据流的中位数
剑指 Offer 41. 数据流中的中位数 - 力扣(LeetCode) (leetcode-cn.com)
解法1 :直接有序插入,然后取中间即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class MedianFinder {public : vector<int > res; MedianFinder () { } void addNum (int num) { if (res.empty ())res.push_back (num); else { int pos = 0 ; while (pos<res.size ()&&res[pos]<num)pos++; res.insert (res.begin ()+pos,num); } } double findMedian () { if (!res.size ())return 0 ; if (res.size ()%2 )return res[res.size ()/2 ]*1.0 ; else return (res[res.size ()/2 -1 ]+res[res.size ()/2 ])/2.0 ; } };
解法2 :用两个堆进行维护
举个例子,42315,小根堆存放45,大根堆存放321,那么他的中位数就是大根堆的堆顶
我们要保证大根堆比小根堆多1,或者相等,所以当我们要插入一个数6时
先插入大根堆,这时大根堆堆顶就是6,他比小根堆堆顶大,那就将两个交换一下
得到结果:小根堆存放56,大根堆存放4321
这时大根堆比小根堆多2了,为了维持平衡,就将大根堆堆顶放进小根堆中
得到结果:小根堆存放456,大根堆存放321
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class MedianFinder {public : 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 ()){ int a = max_heap.top (),b = min_heap.top (); max_heap.pop (),min_heap.pop (); max_heap.push (b),min_heap.push (a); } 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 ; } };
Day18
二叉树的深度
剑指 Offer 55 - I. 二叉树的深度 - 力扣(LeetCode) (leetcode-cn.com)
要实现一个输出二叉树深度的函数,那么当当前节点是NULL是输出0,否则,输出左子树和右子树深度的最大值,然后+1(当前节点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution {public : int maxDepth (TreeNode* root) { if (!root)return 0 ; return max (maxDepth (root->left),maxDepth (root->right))+1 ;; } };
平衡二叉树
剑指 Offer 55 - II. 平衡二叉树 - 力扣(LeetCode) (leetcode-cn.com)
就是左子树和右子树的最大深度不超过1,用上一题代码,再判断一下就行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution {public : bool ans = true ; bool isBalanced (TreeNode* root) { dfs (root); return ans; } int dfs (TreeNode* root) { if (!root)return 0 ; int left = dfs (root->left),right = dfs (root->right); if (abs (left-right)>1 )ans = false ; return max (left,right)+1 ; } };
Day19
求1+2+…+n
剑指 Offer 64. 求1+2+…+n - 力扣(LeetCode) (leetcode-cn.com)
解法1 :等差数列求和公式总会吧,但是这题不能用乘除法,我就不写代码了
解法2 :骤死性评估
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution {public : int ans; int sumNums (int n) { cal (n); return ans; } bool cal (int n) { ans += n; return n && cal (n-1 ); } };
二叉搜索树的最近公共祖先
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先 - 力扣(LeetCode) (leetcode-cn.com)
二叉搜索的节点满足如下性质:
该节点的左边的所有节点,一定比该节点小
该节点的右边的所有节点,一定比该节点大
如果,p,q在这个节点的两边,那么直接返回这个节点,如果都在左边就遍历左边,如果都在右边,就遍历右边
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution {public : TreeNode* lowestCommonAncestor (TreeNode* root, TreeNode* p, TreeNode* q) { if (!root)return NULL ; if ((p->val <= root->val&&q->val>=root->val)||(p->val >= root->val&&q->val<=root->val))return root; if (p->val < root->val&&q->val<root->val)return lowestCommonAncestor (root->left,p,q); return lowestCommonAncestor (root->right,p,q); } };
二叉树的最近公共祖先
剑指 Offer 68 - II. 二叉树的最近公共祖先 - 力扣(LeetCode) (leetcode-cn.com)
直接看代码吧,代码有解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Solution {public : TreeNode* lowestCommonAncestor (TreeNode* root, TreeNode* p, TreeNode* q) { if (!root)return nullptr ; if (root == p || root ==q)return root; TreeNode* left = lowestCommonAncestor (root->left,p,q),*right = lowestCommonAncestor (root->right,p,q); if (left&&right)return root; if (left)return left; return right; } };
Day20
重建二叉树
剑指 Offer 07. 重建二叉树 - 力扣(LeetCode) (leetcode-cn.com)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Solution {public : map<int ,int > mp; vector<int > P,I; TreeNode* buildTree (vector<int >& preorder, vector<int >& inorder) { P= preorder,I = inorder; for (int i = 0 ;i<I.size ();i++)mp[I[i]] = i; return dfs (0 ,P.size ()-1 ,0 ,I.size ()-1 ); } TreeNode* dfs (int pl,int pr,int il,int ir) { if (pl>pr)return NULL ; TreeNode* root = new TreeNode (P[pl]); int k = mp[root->val]; auto left = dfs (pl+1 ,pl+k-il,il,k-1 ); auto right = dfs (pl+k-il+1 ,pr,k+1 ,ir); root->left = left,root->right = right; return root; } };
数值的整数次方
剑指 Offer 16. 数值的整数次方 - 力扣(LeetCode) (leetcode-cn.com)
解法1 :递归解法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution {public : double myPow (double x, int n) { if (x == 0.0 )return 0 ; if (n == 0 ) return 1 ; if (n == -1 ) return 1 /x; if (n == 1 )return x; double res = myPow (x,n>>1 ); return n&1 ?res*res*x:res*res; } };
解法2 :快速幂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution {public : double myPow (double x, int n) { long a = n; if (a<0 ){ a = -a; x = 1 /x; } double res = 1 ; while (a){ if (a&1 )res*=x; x *= x; a>>=1 ; } return res; } };
二叉搜索树的后续遍历序列
剑指 Offer 33. 二叉搜索树的后序遍历序列 - 力扣(LeetCode) (leetcode-cn.com)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution {public : bool verifyPostorder (vector<int >& postorder) { return dfs (postorder,0 ,postorder.size ()-1 ); } bool dfs (vector<int >& postorder,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 (postorder,l,k-1 )&&dfs (postorder,k,r-1 ); } };
Day21
二进制中1的个数
剑指 Offer 15. 二进制中1的个数 - 力扣(LeetCode) (leetcode-cn.com)
1 2 3 4 5 6 7 8 9 10 11 class Solution {public : int hammingWeight (uint32_t n) { int cnt = 0 ; while (n){ if (n&1 )cnt++; n>>=1 ; } return cnt; } };
不用加减乘除做加法
剑指 Offer 65. 不用加减乘除做加法 - 力扣(LeetCode) (leetcode-cn.com)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution {public : int add (int a, int b) { while (b){ int s = a^b; int c = (unsigned int )(a&b)<<1 ; a = s,b=c; } return a; } };
Day22
数组中数字出现的次数
剑指 Offer 56 - I. 数组中数字出现的次数 - 力扣(LeetCode) (leetcode-cn.com)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution {public : vector<int > singleNumbers (vector<int >& nums) { int res=0 ,div,a=0 ,b=0 ; for (int i:nums) res ^= i; div = -res&res; for (int i:nums){ if (i&div)a^=i; else b^=i; } return {a,b}; } };
数组中数字出现的次数2
剑指 Offer 56 - II. 数组中数字出现的次数 II - 力扣(LeetCode) (leetcode-cn.com)
解法1 :哈希暴力
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution {public : int singleNumber (vector<int >& nums) { map<int ,int > hash; for (int i :nums) hash[i]++; int res; for (auto a=hash.begin ();a!=hash.end ();a++) if (a->second==1 ) {res = a->first;break ;} return res; } };
解法2 :自动机,把目标值每一位单独来看,因为计算机本身是32位并行运算的,位运算之间互不干扰,所以你对以为做运算,也就等于对其他位也做了运算,我也解释不出来,
假设当前位有一个输入1,那么从a,b初始化后从状态(0,0)
,获得状态a = (0^1)&~0=1&1=1
,b=(0^1)&~1=1&0=0
即(1,0)
,同理,如果再获得一个1从(1,0)
可以获得状态(a=(1^1)&~0=0&1=0,b=(0^1)&~0=1&1=1)
即(0,1)
,再获得一个1,就会得到状态(0,0)
,这是就满足,如果有3个1,就会回到最初的状态,这对应了题目中的出现3次的数字,如果出现0呢?(0,0)
输入0得到(a=(0^0)&~0=0,b=(0^0)&~0=0)
即(0,0)
,同理(1,0)
和(0,1)
分别得到一个0,就会得到(1,0)
和(0,1)
,因此获得0并不会影响结果。当把数组中的数字都经过这个自动机转化后,就能得到只出现一次的数字了。
1 2 3 4 5 6 7 8 9 10 11 class Solution {public : int singleNumber (vector<int >& nums) { int a=0 ,b=0 ; for (int n:nums){ a = (a^n)&~b; b = (b^n)&~a; } return a; } };
Day23
数组中出现次数超过一半的数字
剑指 Offer 39. 数组中出现次数超过一半的数字 - 力扣(LeetCode) (leetcode-cn.com)
解法1 :暴力哈希法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution {public : int majorityElement (vector<int >& nums) { unordered_map<int ,int > res; for (int i :nums) res[i]++; int t = 0 ; for (auto a = res.begin ();a!=res.end ();a++) if (a->second > nums.size ()/2 ){ t = a->first; break ; } return t; } };
解法2 :摩尔投票法
这题的思路就是,当前认为是众数的数字就在cnt上+1否则就-1,如果当前的cnt为0是,就替换众数
1 2 3 4 5 6 7 8 9 10 11 class Solution {public : int majorityElement (vector<int >& nums) { int cnt = 0 ,data = 0 ; for (int a:nums){ if (cnt == 0 )data = a; cnt += a==data?1 :-1 ; } return data; } };
构建乘积数组
剑指 Offer 66. 构建乘积数组 - 力扣(LeetCode) (leetcode-cn.com)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution {public : vector<int > constructArr (vector<int >& a) { if (a.empty ())return {}; int n = a.size (); vector<int > b (n) ; for (int i = 0 ,p=1 ;i<n;i++){ b[i] = p; p *= a[i]; } for (int i = n-1 ,p=1 ;~i;i--){ b[i] *= p; p *= a[i]; } return b; } };
Day24
剪绳子1
剑指 Offer 14- I. 剪绳子 - 力扣(LeetCode) (leetcode-cn.com)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Solution {public : int cuttingRope (int n) { if (n<=3 )return 1 *(n-1 ); int res = 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; } };
和为s的连续正数序列
剑指 Offer 57 - II. 和为s的连续正数序列 - 力扣(LeetCode) (leetcode-cn.com)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution {public : vector<vector<int >> findContinuousSequence (int target) { vector<vector<int >> res; for (int i = 1 ,j=1 ,s=1 ;i<=target/2 +1 ;i++){ while (s < target)s+=++j; if (s==target&&j-i>=1 ){ vector<int > a; for (int k = i ;k<=j;k++)a.push_back (k); res.push_back (a); } s -= i; } return res; } };
圆圈中最后剩下的数字
剑指 Offer 62. 圆圈中最后剩下的数字 - 力扣(LeetCode) (leetcode-cn.com)
经典约瑟夫问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution {public : int lastRemaining (int n, int m) { if (n==1 )return 0 ; return (lastRemaining (n-1 ,m)+m)%n; } };
Day 25
顺时针打印矩阵(据说面试必考)
剑指 Offer 29. 顺时针打印矩阵 - 力扣(LeetCode) (leetcode-cn.com)
解法1 :我的暴力模拟方法(我是笨比)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 class Solution {public : vector<int > spiralOrder (vector<vector<int >>& matrix) { if (matrix.empty ())return {}; vector<int > res; int l = 0 ,r=matrix[0 ].size ()-1 ,t=0 ,b=matrix.size ()-1 ; int x=0 ,y=0 ; int mode = 0 ,dx,dy; while (x>=t&&x<=b&&y>=l&&y<=r){ res.push_back (matrix[x][y]); if (mode == 0 ){ dy=1 ; if (y+dy>r){ mode = 1 ; t++; x++; continue ; } y+=dy; }else if (mode == 1 ){ dx = 1 ; if (x+dx>b){ mode = 2 ; r--; y--; continue ; } x+=dx; }else if (mode == 2 ){ dy = -1 ; if (y+dy<l){ mode = 3 ; b--; x--; continue ; } y+=dy; }else if (mode==3 ){ dx = -1 ; if (x+dx<t){ mode = 0 ; l++; y++; continue ; } x+=dx; } } return res; } };
解法2 :巧妙模拟
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Solution {public : vector<int > spiralOrder (vector<vector<int >>& matrix) { vector<int > res; int n = matrix.size (); if (!n)return {}; int m = matrix[0 ].size (); vector<vector<bool >> st (n,vector<bool >(m,false )); int dx[4 ] = {-1 ,0 ,1 ,0 },dy[4 ]={0 ,1 ,0 ,-1 }; int x = 0 ,y=0 ,d=1 ; for (int i = 0 ;i<n*m;i++){ res.push_back (matrix[x][y]); st[x][y] = true ; int a = x+dx[d],b = y+dy[d]; if (a<0 ||a>=n||b<0 ||b>=m||st[a][b]){ d = (d+1 )%4 ; a = x+dx[d],b = y+dy[d]; } x=a,y= b; } return res; } };
栈的压入,弹出序列
剑指 Offer 31. 栈的压入、弹出序列 - 力扣(LeetCode) (leetcode-cn.com)
模拟入栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution {public : bool validateStackSequences (vector<int >& pushed, vector<int >& popped) { if (pushed.size () != popped.size ())return false ; stack<int > st; int p = 0 ; for (auto x:pushed){ st.push (x); while (st.size ()&&st.top ()==popped[p])st.pop (),p++; } return st.empty (); } };
Day26
表示数字的字符串
剑指 Offer 20. 表示数值的字符串 - 力扣(LeetCode) (leetcode-cn.com)
解法1 :直接模拟
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Solution {public : bool isNumber (string s) { int i = 0 ,j=s.size ()-1 ; while (i<=j&&s[i]==' ' )i++; while (i<=j&&s[j]==' ' )j--; if (i>j)return false ; s = s.substr (i,j-i+1 ); if (s[0 ]=='+' ||s[0 ]=='-' )s=s.substr (1 ); if (s.empty ()||(s[0 ]=='.' &&s.size ()==1 ))return false ; int dot = 0 ,e=0 ; for (int i = 0 ;i<s.size ();i++){ if (s[i]>='0' &&s[i]<='9' ); else if (s[i]=='.' ){ dot++; if (dot>1 ||e)return false ; } else if (s[i]=='e' ||s[i]=='E' ){ e++; if (!i||i+1 ==s.size ()||e>1 ||s[i-1 ]=='.' &&i==1 )return false ; if (s[i+1 ]=='+' ||s[i+1 ]=='-' ){ if (i+2 ==s.size ())return false ; i++; } } else return false ; } return true ; } };
解法2 :正则表达式,C++不太友好,反正我超时了,我崩溃了,写了好久的正则,呜呜呜,我是笨蛋
解法3 :状态自动机,可以根据写好的正则式转化成NFA再转化成DFA(编译原理内容),然后根据建立好的DFA进行模拟,如果能够进入终态就是符合的,我就不写代码了,后面有时间再看看
把字符串转换成整数
剑指 Offer 67. 把字符串转换成整数 - 力扣(LeetCode) (leetcode-cn.com)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Solution {public : int strToInt (string str) { if (!str.size ())return 0 ; bool fu=false ; long long num =0 ; int k = 0 ; while (k<str.size ()&&str[k]==' ' )k++; if (str[k]=='+' )k++; else if (str[k]=='-' )k++,fu=true ; while (k<str.size ()&&str[k]>='0' &&str[k]<='9' ){ num = num * 10 +str[k]-'0' ; if (fu&&-1 *num<INT_MIN){num = INT_MIN,fu=false ;break ;} if (!fu&&num>INT_MAX){num = INT_MAX;break ;} k++; } if (fu) num*=-1 ; if (INT_MAX<=num)return INT_MAX; if (INT_MIN>=num)return INT_MIN; return num; } };
Day 27
滑动窗口的最大值
剑指 Offer 59 - I. 滑动窗口的最大值 - 力扣(LeetCode) (leetcode-cn.com)
经典的单调队列算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution {public : int q[100010 ]; vector<int > maxSlidingWindow (vector<int >& nums, int k) { vector<int > res; int hh = 0 ,tt=-1 ,n=nums.size (); for (int i = 0 ;i<n;i++){ if (hh<=tt&&i-k+1 >q[hh])hh++; while (hh<=tt&&nums[q[tt]] <= nums[i])tt--; q[++tt] = i; if (i>=k-1 )res.push_back (nums[q[hh]]); } return res; } };
队列的最大值
剑指 Offer 59 - II. 队列的最大值 - 力扣(LeetCode) (leetcode-cn.com)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class MaxQueue {public : queue<int > q; deque<int > mq; MaxQueue () { } int max_value () { return mq.size ()?mq.front ():-1 ; } void push_back (int value) { while (mq.size ()&&mq.back () <= value) mq.pop_back (); mq.push_back (value); q.push (value); } int pop_front () { if (!mq.size ())return -1 ; if (q.front ()==mq.front ()) mq.pop_front (); int res = q.front (); q.pop (); return res; } };
Day28
序列化二叉树
剑指 Offer 37. 序列化二叉树 - 力扣(LeetCode) (leetcode-cn.com)
解法1 :自欺欺人写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Codec {public : TreeNode* res; string serialize (TreeNode* root) { res = root; return "" ; } TreeNode* deserialize (string data) { return res; } };
解法2 :前序遍历序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 class Codec {public : string serialize (TreeNode* root) { string res; dfs_s (root,res); return res; } 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* deserialize (string data) { int u = 0 ; return dfs_d (data,u); } TreeNode* dfs_d (string data,int &u) { if (u==data.size ())return NULL ; int k = u; while (data[k]!=' ' )k++; if (data[u]=='n' ){ u = k+1 ; return NULL ; } int val = 0 ,sign = 0 ; if (data[u]=='-' ) sign = 1 ; for (int i = u+sign;i<k;i++) val = val*10 +data[i]-'0' ; if (sign) val *= -1 ; u = k+1 ; auto root =new TreeNode (val); root->left = dfs_d (data,u); root->right = dfs_d (data,u); return root; } };
字符串的排列
剑指 Offer 38. 字符串的排列 - 力扣(LeetCode) (leetcode-cn.com)
解法1 :爆搜+set去重
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution {public : set<string> res; vector<string> permutation (string s) { int n = s.size (); vector<bool > st (n,false ) ; dfs (s,st,0 ,"" ); vector<string> ress; for (auto i = res.cbegin ();i != res.cend ();i++) ress.push_back (*i); return ress; } void dfs (string s,vector<bool > &st,int u,string a) { if (u==s.size ()){ res.insert (a); return ; } for (int i = 0 ;i<s.size ();i++){ if (st[i])continue ; st[i] = true ; dfs (s,st,u+1 ,a+s[i]); st[i] = false ; } } };
解法2 :STL的魔法
1 2 3 4 5 6 7 8 9 10 11 class Solution {public : vector<string> permutation (string s) { vector<string> res; sort (s.begin (),s.end ()); do { res.push_back (s); } while (next_permutation (s.begin (), s.end ())); return res; } };
Day29
正则表达式匹配
面试题19. 正则表达式匹配 - 力扣(LeetCode) (leetcode-cn.com)
这题我好像写过了,可以全局搜一下
丑数
剑指 Offer 49. 丑数 - 力扣(LeetCode) (leetcode-cn.com)
假设丑数序列为: S = a1,a2,a3,a4…
有序列(2的倍数) S1 = b1,b2,b3,b4…
有序列(3的倍数) S2 = c1,c2,c3,c4…
有序列(5的倍数) S3 = d1,d2,d3,d4…
S = 1,2,3,4,5,6,8,9,…=1并S1并S2并S3
并且S1/2= S,S2/3=S,S3/5=S
因此我们想把1放进数组里,然后用三个指针指向1,三个指针分别乘2,3,5,每次插入他们乘积的最小值,然后如果他们的乘积等于最小值,就移动这个指针,移动n-1次即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution {public : int nthUglyNumber (int n) { vector<int > f (1 ,1 ) ; int a=0 ,b=0 ,c=0 ; while (--n){ int t = min (f[a]*2 ,min (f[b]*3 ,f[c]*5 )); f.push_back (t); if (f[a]*2 ==t)a++; if (f[b]*3 ==t)b++; if (f[c]*5 ==t)c++; } return f.back (); } };
n个骰子的点数
剑指 Offer 60. n个骰子的点数 - 力扣(LeetCode) (leetcode-cn.com)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution {public : vector<double > dicesProbability (int n) { vector<vector<int >> f (n+1 ,vector<int >(n*6 +1 )); f[0 ][0 ] = 1 ; for (int i = 1 ;i<=n;i++) for (int j = 1 ;j<=i*6 ;j++) for (int k = 1 ;k<=min (j,6 );k++) f[i][j] += f[i-1 ][j-k]; vector<double > res; double total = (double )pow (6 ,n); for (int i = n;i<=n*6 ;i++)res.push_back (f[n][i]/total); return res; } };
Day30
打印从1到最大的n位数
剑指 Offer 17. 打印从1到最大的n位数 - 力扣(LeetCode) (leetcode-cn.com)
写了这么多奇奇怪怪的题,突然碰到这题,感觉自己受到侮辱
1 2 3 4 5 6 7 8 9 10 11 class Solution {public : vector<int > printNumbers (int n) { vector<int > res; long big = pow (10 ,n); int tmp = 1 ; while (tmp<big) res.push_back (tmp++); return res; } };
数组中的逆序对
剑指 Offer 51. 数组中的逆序对 - 力扣(LeetCode) (leetcode-cn.com)
归并排序的思想,首先把数组分成两段,理想应该左边的数据都比右边的大
那么就双指针遍历两头,如果左边端的数字比右边端的数字大,那么就下一个左边段的数字
但是如果有一个右边端的数字j比左边段的数字小,那么,从当前位置i,到mid的数字都会会右边段的数字j形成逆序对,即mid-i+1个
所以归并+状态记录就结束了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Solution {public : int merge (vector<int >& a,int l,int r) { if (l>=r)return 0 ; int mid = l+r>>1 ; vector<int > tmp; int i = l,j = mid+1 ; int res = merge (a,l,mid)+merge (a,mid+1 ,r); while (i<=mid&&j<=r){ if (a[i]<=a[j])tmp.push_back (a[i++]); else { tmp.push_back (a[j++]); res+=mid-i+1 ; } } while (i<=mid)tmp.push_back (a[i++]); while (j<=r)tmp.push_back (a[j++]); i = l; for (int x:tmp)a[i++] = x; return res; } int reversePairs (vector<int >& nums) { return merge (nums,0 ,nums.size ()-1 ); } };
Day31(最后一天)
剪绳子2
剑指 Offer 14- II. 剪绳子 II - 力扣(LeetCode) (leetcode-cn.com)
和第24天的剪绳子一样的题,只是这次要取一个膜
1 2 3 4 5 6 7 8 9 10 11 12 class Solution {public : int cuttingRope (int n) { const int mod = 1e9 +7 ; if (n<=3 )return n-1 ; int res = 1 ; if (n%3 ==1 )res *=4 ,n-=4 ; if (n%3 ==2 )res*=2 ,n-=2 ; while (n)res= res *1ll * 3 %mod,n-=3 ; return res; } };
1~n整数中1出现的次数
剑指 Offer 43. 1~n 整数中 1 出现的次数 - 力扣(LeetCode) (leetcode-cn.com)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Solution {public : int countDigitOne (int n) { if (!n)return 0 ; vector<int > number; while (n) number.push_back (n%10 ),n/=10 ; int res = 0 ; for (int i = number.size ()-1 ;i>=0 ;i--) { auto left = 0 ,right = 0 ,t = 1 ; for (int j = number.size ()-1 ;j>i;j--) left = left*10 +number[j]; for (int j = i-1 ;j>=0 ;j--)right = right*10 +number[j],t*=10 ; res+=left*t; if (number[i]==1 )res+=right+1 ; else if (number[i]>1 )res+=t; } return res; } };
数字序列中某一位的数字
剑指 Offer 44. 数字序列中某一位的数字 - 力扣(LeetCode) (leetcode-cn.com)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Solution {public : int findNthDigit (int n) { long long i = 1 ,s = 9 ,base=1 ; while (n>i*s){ n-=i*s; i++; s*=10 ; base *=10 ; } int number = base + (n+i-1 )/i-1 ; int r = n%i?n%i:i; for (int j = 0 ;j<i-r;j++)number/=10 ; return number%10 ; } };
结语
31天终于写完了,感觉之前写过了的又忘了,感觉自己还是什么都不会,但是学这种东西就是得不断重复的,所以加油吧!