目录
- class Solution {
- public:
- int search(vector<int>& nums, int target) {
- int l=0,r=nums.size()-1;
- while(l
- {
- int mid=l+r>>1;
- if(nums[mid]>=target) r=mid;
- else l=mid+1;
- }
- if(nums[l]!=target) return -1;
- else return l;
- }
- };
278.第一个错误版本
直接mid = l+r >> 1会溢出,要改变一下,mid=l+(r-l) / 2
- class Solution {
- public:
- int firstBadVersion(int n) {
- int l=1,r=n;
- while(l
- {
- int mid=l+(r-l)/2;
- if(isBadVersion(mid)==true) r=mid;
- else l=mid+1;
- }
- return l;
- }
- };
!35.搜索插入位置
需要加个特判,如果这个数比最终查找位置大,返回位置应该+1
如果比最终位置小,直接返回该数下标即可
例如:1 3 5 6 插入0 输出:0
1 3 5 6 插入7 输出:4
- class Solution {
- public:
- int searchInsert(vector<int>& nums, int target) {
- int l=0,r=nums.size()-1;
- while(l
- {
- int mid=l+r+1>>1;
- if(nums[mid]<=target) l=mid;
- else r=mid-1;
- }
- if(nums[l]
return l+1; - return l;
- }
- };
DAY2.双指针
977.有序数组的平方
- class Solution {
- public:
- vector<int> sortedSquares(vector<int>& nums) {
- int n=nums.size();
- int l=0,r=n-1;
- int cnt=n-1;
- vector<int>res(n);
- while(l<=r)
- {
- if(nums[l]*nums[l]>nums[r]*nums[r])
- res[cnt--]=nums[l]*nums[l],l++;
- else res[cnt--]=nums[r]*nums[r],r--;
- }
- return res;
- }
- };
189.轮转数组
直接定位法
- class Solution {
- public:
- void rotate(vector<int>& nums, int k) {
- int n=nums.size();
- vector<int>res(n);
- for(int i=0;i
- res[(i+k)%n]=nums[i];
- nums=res;
- }
- };
翻转数组法:
例如:1 2 3 4 5
第一步先翻转整个数组 5 4 3 2 1
第二步按k划分 5 4 3 2 1
第三步各自翻转 3 4 5 1 2
- class Solution {
- public:
- void reverse(vector<int>& nums,int l,int r)
- {
- while(l
- {
- swap(nums[l],nums[r]);
- l++;
- r--;
- }
- }
-
- void rotate(vector<int>& nums, int k) {
- int n=nums.size();
- k%=n;
- reverse(nums,0,n-1);
- reverse(nums,0,k-1);
- reverse(nums,k,n-1);
- }
- };
DAY3.双指针
!283.移动零
左指针指向为0的数
右指针指向不为0的数
- class Solution {
- public:
- void moveZeroes(vector<int>& nums) {
- int n=nums.size();
- int l=0,r=0;
- while(r
- {
- if(nums[r]) swap(nums[l],nums[r]),l++;
- r++;
- }
- }
- };
!167.两数之和 2 - 输入有序数组
双指针法
- class Solution {
- public:
- vector<int> twoSum(vector<int>& numbers, int target) {
- int n=numbers.size();
- int l=0,r=n-1;
- while(l
- {
- if(numbers[l]+numbers[r]>target) r--;
- else if(numbers[l]+numbers[r]
- else return{l+1,r+1};
- }
- return {-1,-1};
- }
- };
二分法
可以先固定一个数i,再利用二分,在比i大的区间里找另一个数满足n[i]+n[l]==target
- class Solution {
- public:
- vector<int> twoSum(vector<int>& numbers, int target) {
- int n=numbers.size();
- for(int i=0;i
- {
- int l=i+1,r=n-1;
- while(l
- {
- int mid=l+r>>1;
- if(numbers[i]+numbers[mid]>=target) r=mid;
- else l=mid+1;
- }
- if(numbers[i]+numbers[r]==target) return{i+1,r+1};
- }
- return {-1,-1};
- }
- };
DAY4.双指针
557.反转字符串中的单词3
双指针法
- class Solution {
- public:
- string reverseWords(string s) {
- int n=s.size();
- int l=0,r;
- for(int i=0;i<=n;i++)
- if(s[i]==' '||s[i]=='\0')
- {
- for(r=i-1;r>l;r--,l++) swap(s[l],s[r]);
- l=i+1;
- }
- return s;
- }
- };
- class Solution {
- public:
- string reverseWords(string s) {
- stringstream ss(s);
- string str;
- string res;
- bool f=false;
- while(ss>>str)
- {
- reverse(str.begin(),str.end());
- if(f)res+=" ";
- f=true;
- res+=str;
- }
- return res;
- }
- };
DAY5.双指针
876.链表的中间结点
暴力遍历
先求链表长度 再找len/2的位置
- class Solution {
- public:
- ListNode* middleNode(ListNode* head) {
- int len=0;
- ListNode *t=head;
- while(t!=NULL)
- {
- len++;
- t=t->next;
- }
- int i=0;
- while(i
2) - {
- head=head->next;
- i++;
- }
- return head;
- }
- };
快慢指针
快指针一次走两步 慢指针一次走一步
当快指针走到末尾时 慢指针恰好走到中间位置
- 奇数情况:如1 2 3 4 5 当慢指针在中点3时,快指针在5位置,即high->next=NULL
- 偶数情况:如1 2 3 4 5 6 当慢指针在4时,快指针在6的后一位置,即high=NULL
如果high指针满足以上两个条件的任意一个,则不需要移动指针,而返回慢指针
- class Solution {
- public:
- ListNode* middleNode(ListNode* head) {
- ListNode *slow=head,*high=head;
- while(high!=NULL&&high->next!=NULL)
- {
- high=high->next->next;
- slow=slow->next;
- }
- return slow;
- }
- };
19.删除链表的倒数第n个结点
暴力遍历
需要注意特判 链表长度为1 和 删除的是头结点的情况(即len==n)
- class Solution {
- public:
- ListNode* removeNthFromEnd(ListNode* head, int n) {
- int len=0;
- ListNode *t=head;
- while(t!=NULL)
- {
- t=t->next;
- len++;
- }
- if(len==1||len==n) return head->next; //如果链表只有一个元素则返回NULL 如果要删除头结点 直接返回头结点之后的链表
- ListNode *p=head,*q=NULL;
- for(int i=1;i
next; - q=p->next;
- p->next=q->next;
- return head;
- }
- };
快慢指针
快指针先走n个结点 然后快慢指针同时走
当快指针走到末尾时 慢指针恰好指向倒数第n个节点的前驱结点
- class Solution {
- public:
- ListNode* removeNthFromEnd(ListNode* head, int n) {
- if(head->next==NULL) return head->next;
- ListNode *high=head,*slow=head;
- for(int i=0;i
next; - if(!high) return head->next; //如果快指针指出范围 说明要删除的是头结点
- while(high->next)
- {
- high=high->next;
- slow=slow->next;
- }
- slow->next=slow->next->next;
- return head;
- }
- };
DAY6.滑动窗口
3.无重复字符的最长子串
unordered_set
该容器中不包含重复元素
mp.find() 查找对应元素,成功返回对应的迭代器,失败返回最后一个元素迭代器(即 mp.end() )
- class Solution {
- public:
- int lengthOfLongestSubstring(string s) {
- if(s.size()==0) return 0;
- int maxl=-1,l;
- unordered_set<char>mp;
- for(int i=0;i
size();i++) - {
- while(mp.find(s[i])!=mp.end())//如果在窗口中找到重复元素 则擦除左端 移动窗口
- {
- mp.erase(s[l]);
- l++;
- }
- maxl=max(maxl,i-l+1);
- mp.insert(s[i]);
- }
- return maxl;
- }
- };
567.字符串的排列
- class Solution {
- public:
- bool checkInclusion(string s1, string s2) {
- if(s1.size()>s2.size()) return false;
- vector<int>v1(26,0);
- vector<int>v2(26,0);
- for(int i=0;i
size();i++) //因为不要求顺序 所以只要统计字母的出现次数就可以 - {
- v1[s1[i]-'a']++;
- v2[s2[i]-'a']++;
- }
- if(v1==v2) return true;
-
- for(int i=s1.size();i
size();i++) - {
- v2[s2[i]-'a']++;
- v2[s2[i-s1.size()]-'a']--; //保持窗口长度为s1的长度
- if(v1==v2) return true;
- }
- return false;
- }
- };
DAY7. DFS/BFS
733.图像渲染
方法一:dfs
我们从给定的起点开始,进行深度优先搜索。
每次搜索到一个方格时,如果其与初始位置的方格颜色相同,就将该方格的颜色更新,以防止重复搜索;如果不相同,则进行回溯。
- class Solution {
- public:
- int dx[4]={0,0,-1,1};
- int dy[4]={1,-1,0,0};
-
- void dfs(vector
int >>&g,int x,int y,int cur,int color) - {
- int m=g.size(),n=g[0].size();
- if(g[x][y]==cur)
- {
- g[x][y]=color;
- for(int i=0;i<4;i++)
- {
- int a=x+dx[i],b=y+dy[i];
- if(a<0||a>m-1||b<0||b>n-1) continue;
- dfs(g,a,b,cur,color);
- }
- }
- }
-
- vector
int>> floodFill(vectorint>>& g, int sr, int sc, int color) { - int cur=g[sr][sc];
- if(cur!=color)
- dfs(g,sr,sc,cur,color);
- return g;
- }
- };
方法二:bfs
我们从给定的起点开始,进行广度优先搜索。
每次搜索到一个方格时,如果其与初始位置的方格颜色相同,就将该方格加入队列,并将该方格的颜色更新,以防止重复入队。
注意:因为初始位置的颜色会被修改,所以我们需要保存初始位置的颜色,以便于之后的更新操作。
- typedef pair<int,int> PII;
- class Solution {
- public:
- int dx[4]={0,0,-1,1};
- int dy[4]={1,-1,0,0};
-
- void bfs(vector
int >>&g,int x,int y,int cur,int color) - {
- int m=g.size(),n=g[0].size();
- g[x][y]=color;
- queue
q; - q.push({x,y});
- while(!q.empty())
- {
- auto t=q.front();
- int a=t.first,b=t.second;
- q.pop();
- for(int i=0;i<4;i++)
- {
- int aa=a+dx[i],bb=b+dy[i];
- if(aa<0||aa>m-1||bb<0||bb>n-1) continue;
- if(g[aa][bb]==cur) g[aa][bb]=color,q.push({aa,bb});
- }
- }
- }
-
- vector
int>> floodFill(vectorint>>& g, int sr, int sc, int color) { - int cur=g[sr][sc];
- if(cur!=color)
- bfs(g,sr,sc,cur,color);
- return g;
- }
- };
695.岛屿的最大面积
方法一:dfs
- class Solution {
- public:
- int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
-
- int dfs(vector
int >>&g,int x,int y) - {
- int m=g.size(),n=g[0].size();
- if(x<0||x>m-1||y<0||y>n-1) return 0;
- if(g[x][y]==0) return 0;
- g[x][y]=0;
- int res=1;
- for(int i=0;i<4;i++)
- {
- int a=x+dx[i],b=y+dy[i];
- res+=dfs(g,a,b);
- }
- return res;
- }
-
- int maxAreaOfIsland(vector
int >>& g) { - int m=g.size(),n=g[0].size();
- int res=0;
- for(int i=0;i
- for(int j=0;j
- res=max(res,dfs(g,i,j));
- return res;
- }
- };
方法二:bfs
- typedef pair<int,int> PII;
- class Solution {
- public:
- int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
-
- int bfs(vector
int >>&g,int x,int y) - {
- int m=g.size(),n=g[0].size();
- queue
q; - q.push({x,y});
- g[x][y]=0;
- int res=1;
- while(!q.empty())
- {
- auto t=q.front();
- q.pop();
- int a=t.first,b=t.second;
- for(int i=0;i<4;i++)
- {
- int aa=a+dx[i],bb=b+dy[i];
- if(aa<0||aa>m-1||bb<0||bb>n-1) continue;
- if(g[aa][bb]==1) q.push({aa,bb}),res++,g[aa][bb]=0;
- }
- }
- return res;
- }
-
- int maxAreaOfIsland(vector
int >>& g) { - int m=g.size(),n=g[0].size();
- int res=0;
- for(int i=0;i
- for(int j=0;j
- if(g[i][j]==1)
- res=max(res,bfs(g,i,j));
- return res;
- }
- };
DAY8.DFS/BFS
617.合并二叉树
- class Solution {
- public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
- if(t1==null) return t2;
- if(t2==null) return t1;
-
- //如果都存在结点 则创建一个新结点储存两个结点值之和
- TreeNode newroot=new TreeNode(t1.val+t2.val);
- //递归合并左子树
- newroot.left=mergeTrees(t1.left,t2.left);
- //递归合并右子树
- newroot.right=mergeTrees(t1.right,t2.right);
-
- return newroot;
- }
- }
!116.填充每个节点的下一个右侧节点指针
- 如果结点为NULL或者左右子树均为NULL:没有需要做的,直接返回结点本身。
- 对于左右子树均存在的结点:
- 首先左子树的next一定不会指向NULL,直接设置指向右子树即可;
- 对于右子树,他的next是要指向父结点右兄弟的左子树的,可以通过root->next->left找到这个结点,但只有一种情况下例外,就是父结点没有右兄弟(root->next == NULL)
- class Solution {
- public Node connect(Node root) {
- if(root==null||root.left==null) return root;
- root.left.next=root.right; //左子树的next肯定不为空 直接指向右子树
- if(root.next!=null) //如果父结点有右兄弟
- root.right.next=root.next.left;//指向父节点右兄弟的左子树
- connect(root.left);
- connect(root.right);
- return root;
- }
- }
DAY9. DFS/BFS
542.01矩阵
bfs:

首先把每个源点 0入队,然后从各个 0 同时开始一圈一圈的向 1 扩散(每个 1 都是被离它最近的 0 扩散到的 )
- class Solution {
- public int[][] updateMatrix(int[][] g) {
- int m=g.length,n=g[0].length;
- Queue<int[]> q=new LinkedList<>();
- //将所有0入队,并将1的位置设成-1 标记为未访问
- for(int i=0;i
- for(int j=0;j
- if(g[i][j]==0) q.offer(new int[] {i,j});
- else g[i][j]=-1;
-
- int[] dx=new int[]{0,0,-1,1},dy=new int[]{1,-1,0,0};
- while(!q.isEmpty())
- {
- int[] t=q.poll();
- int x=t[0],y=t[1];
- for(int i=0;i<4;i++)
- {
- int a=dx[i]+x,b=dy[i]+y;
- if(a>=0&&a
=0&&b1) - {
- g[a][b]=g[x][y]+1;
- q.offer(new int[] {a,b});
- }
- }
- }
- return g;
- }
- }
994.腐烂的橘子
bfs:
让腐烂的橘子入队 遍历第一次腐烂的橘子 逐一感染
- class Solution {
- public int orangesRotting(int[][] g) {
- int m=g.length,n=g[0].length;
- int res=0;
- Queue<int[]> q=new LinkedList<>();
- for(int i=0;i
- for(int j=0;j
- if(g[i][j]==2) q.offer(new int[] {i,j});
-
- int[] dx=new int[]{0,0,1,-1},dy=new int[]{1,-1,0,0};
- while(!q.isEmpty())
- {
- int num=q.size();//统计本轮腐烂橘子的个数
- for(int i=0;i
- {
- int[] t=q.poll();
- int x=t[0],y=t[1];
- for(int j=0;j<4;j++)
- {
- int a=dx[j]+x,b=dy[j]+y;
- if(a<0||a>=m||b<0||b>=n) continue;
- if(g[a][b]==1)
- {
- g[a][b]=2;
- q.offer(new int[] {a,b});
- }
- }
- }
- if(!q.isEmpty()) res++;//如果一开始就感染完了 res=0
- }
- //检查还有没有新鲜橘子
- for(int i=0;i
- for(int j=0;j
- if(g[i][j]==1) return -1;
- return res;
- }
- }
DAY10.递归/回溯
21.合并两个有序链表
- class Solution {
- public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
- ListNode head=new ListNode(-1);
- ListNode p=head;
- while(l1!=null&&l2!=null)
- {
- if(l1.val>l2.val)
- {
- p.next=l2;
- l2=l2.next;
- }else{
- p.next=l1;
- l1=l1.next;
- }
- p=p.next;
- }
- p.next= l1==null?l2:l1;
- return head.next;
- }
- }
206.反转链表
- class Solution {
- public ListNode reverseList(ListNode head) {
- ListNode p=null;
- ListNode cur=head;
- while(cur!=null)
- {
- ListNode t=cur.next;
- cur.next=p;
- p=cur;
- cur=t;
- }
- return p;
- }
- }
- class Solution {
- public ListNode reverseList(ListNode head) {
- if(head==null||head.next==null) return head;
- ListNode cur=reverseList(head.next);
- head.next.next=head;
- head.next=null;
- return cur;
- }
- }
DAY11.递归/回溯
77.组合
思路:

关于剪枝:
- 要组成大小为k的v,此时还需要k - v.size()个数,如果[i, n]这个区间内(有n - i + 1个数)不足这么多,则肯定没有结果,直接剪枝。
- 因此剪枝条件为:n - i + 1 < k - v.size(),推出来上界为i = n - k + v.size() + 1
- class Solution {
- List
> res=new ArrayList<>();
- List
v=new ArrayList<>(); - public List
> combine(int n, int k) {
- dfs(1,n,k);
- return res;
- }
-
- public void dfs(int start,int n,int k)
- {
- if(v.size()==k) //如果一组已经存满了 则把结果存入res
- {
- res.add(new ArrayList<>(v));//深拷贝 对象中开辟一个新地址,存放的内容为list链表,所以后续不会被影响
- return;
- }
-
- for(int i=start;i<=n-k+v.size()+1;i++)
- {
- v.add(i);
- dfs(i+1,n,k);
- v.remove(v.size()-1);//回溯操作(回退处理结点[1, 2] 撤销2 ,[1, 3] 撤销3),然后继续遍历
- }
- }
- }
46.全排列
思路:

- class Solution {
- List
> res = new ArrayList<>();
- List
v=new ArrayList<>(); -
- public List
> permute(int[] nums) {
-
- int[] visited = new int[nums.length];
- backtrack(nums,visited);
- return res;
- }
-
- private void backtrack(int[] nums, int[] visited) {
- if(v.size()==nums.length) {
- res.add(new ArrayList<>(v));
- return;
- }
- for(int i=0;i
- {
- if(visited[i]==1) continue;
- v.add(nums[i]);
- visited[i]=1;
- backtrack(nums,visited);
- visited[i]=0;
- v.remove(v.size()-1);
- }
- }
- }
784.字母大小写全排列
- class Solution {
- List
res=new ArrayList<>(); -
- public List
letterCasePermutation(String s) { -
- char[] ch=s.toCharArray();
- int n=s.length();
- dfs(ch,n,0);
- return res;
- }
-
- private void dfs(char[] ch,int n,int idx)
- {
- res.add(new String(ch));
- for(int i=idx;i
- if(!Character.isDigit(ch[i]))
- {
- char t=ch[i];
- ch[i]=(char)((ch[i]-'a'>=0)? ch[i]-32:ch[i]+32);
- dfs(ch,n,i+1);
- ch[i]=t; //恢复现场
- }
- }
- }
DAY12.动态规划
70.爬楼梯
本问题其实常规解法可以分成多个子问题,爬第n阶楼梯的方法数量,等于 2 部分之和
- 爬上 n-1 阶楼梯的方法数量。因为再爬1阶就能到第n阶
- 爬上 n-2 阶楼梯的方法数量。因为再爬2阶就能到第n阶
所以我们得到公式 dp[n] = dp[n-1] + dp[n-2]
同时需要初始化 dp[0]=1和dp[1]=1
第0阶台阶1种方法 第1阶台阶1种方法
- class Solution {
- public int climbStairs(int n) {
- int[] dp=new int[n+1];
- dp[0]=1;
- dp[1]=1;
- for(int i=2;i<=n;i++) dp[i]=dp[i-1]+dp[i-2];
- return dp[n];
- }
- }
斐波那契数列
其实就是斐波那契数列
1 2 3 5 8……
- class Solution {
- public int climbStairs(int n) {
- if(n<=2) return n;
- int a=1,b=2,t;
- for(int i=3;i<=n;i++)
- {
- t=a+b;
- a=b;
- b=t;
- }
- return b;
- }
- }
198.打家劫舍
dp[i] 第i间房可盗窃的最大值
因为不可以闯入相邻的房间 所以在第n间房可以盗窃的最大值=
- 不盗取第n间房,所以盗取第n-1间房 dp[n]=dp[n-1]
- 盗取第n间房,所以不能盗窃第n-1间房 dp[n]=dp[n-2]+当前房的值num
所以动态规划方程:dp[n] = max( dp[n-1],dp[n-2]+num )
eg:
1号房可盗窃最大值为3 dp[1]=3
2号房可盗窃最大值为4 dp[2]=4
3号房值为2 num=2 dp[3]=max(dp[2],dp[1]+num)=max(4,3+2)=5
- class Solution {
- public int rob(int[] nums) {
- int n=nums.length;
- int[] dp=new int[n+1]; //开辟的是0~n的空间
- dp[0]=0;
- dp[1]=nums[0];
- for(int i=2;i<=n;i++)
- dp[i]=Math.max(dp[i-1],dp[i-2]+nums[i-1]);
- return dp[n];
- }
- }
120.三角形最小路径和
dp[i][j] 表示从点 (i, j) 到底边的最小路径和
自下而上算
dp[i][j]=min(下一行的左右数值)+自己的值
最后返回顶上的值就是最小路径和
- class Solution {
- public int minimumTotal(List
> t)
{ - int n=t.size();
- int[][] dp=new int[n+1][n+1];
- for(int i=n-1;i>=0;i--) // 最后一行跳过 从倒数第二行开始算
- for(int j=0;j<=i;j++)
- dp[i][j]=Math.min(dp[i+1][j],dp[i+1][j+1])+t.get(i).get(j);
- return dp[0][0];
- }
- }
DAY13.位运算
231.2的幂

- class Solution {
- public boolean isPowerOfTwo(int n) {
- return n>0 && (n&(n-1))==0;
- }
- }
191.位1的个数

- public class Solution {
- public int hammingWeight(int n) {
- int res=0;
- while(n!=0)
- {
- n&=n-1;
- res++;
- }
- return res;
- }
- }
Integer.bitCount(n) 返回1的个数
- public class Solution {
- public int hammingWeight(int n) {
- return Integer.bitCount(n);
- }
- }
DAY14.位运算
190.颠倒二进制位
- public class Solution {
- public int reverseBits(int n) {
- int res=0;
- for(int i=0;i<32;i++)
- {
- int t=n&1; //获取最后的数字
- n>>=1;
- res<<=1; //结果数字左移,为新到来的数字腾出空间
- res|=t; //或运算,将新到来的数字加到原来的数字上
- }
- return res;
- }
- }
Integer.reverse(n); 反转
- public class Solution {
- public int reverseBits(int n) {
- return Integer.reverse(n);
- }
- }
136.只出现一次的数字
- class Solution {
- public int singleNumber(int[] a) {
- int res=0;
- for(int x:a) res^=x;
- return res;
- }
- }
-
相关阅读:
JavaScript中的设计原则
使用 FasterTransformer 和 Triton 推理服务器部署 GPT-J 和 T5
基于核心素养劳动教育与学科教学融合研究结题报告
关于HTTP模块访问之后响应网页
Java 性能优化实战案例分析:多线程锁的优化
景联文科技提供全方位图像标注服务
TouchGFX之缓存位图
NOWAIT及SKIP LOCKED的使用
GRU 浅析
Oracle中的Rollup 使用方法
-
原文地址:https://blog.csdn.net/weixin_61639349/article/details/126858112