力扣33题 搜索旋转排序数组
设x=nums[mid]是我们现在二分取到的数,需要判断x和target的位置关系。
(1)如果x和target在不同的递增段:
1.如果target在第一段,x在第二段,说明target在x在左边。
2.如果x在第一段,target在第二段,说明target在x在右边。
(2)如果x和target在相同的递增段:
和lowerbound函数一样,比较x和target的大小即可。
下面代码用的开区间二分,用其他二分写法也是可以的。
二分的范围可以是(-1,n-1),也就是闭区间[0,n-2]。
如果target=nums[n-1],那么下面代码每次循环更新的都是left,而right始终不变。循环结束后,答案自然就是n-1了。
class Solution {
public:
int search(vector<int>& nums, int target) {
int last = nums.back();
int left = -1,right = nums.size()-1; //开闭间(-1,n-1)
while(left+1<right){ //开区间不为空
int mid = left+(right-left)/2;
int x = nums[mid];
if(target>last && x<=last){ //target在第一段,x在第二段
right = mid; //下轮循环去左边找
}else if(x>last && target <= last){ //x在第一段,target在第二段
left = mid; //下轮循环去右边找
}else if(x>=target){ //否则,x和target在同一段
right = mid;
}else{
left = mid;
}
}
return nums[right] == target ? right : -1;
}
};
力扣64题 最小路径和
动规五部曲
本题思路和力扣62题差不多
需要的是最小的路径和,所以状态转移方程就是dp[i][j]=min(dp[i-1][j]+grid[i][j],dp[i][j-1]+grid[i][j])
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
vector<vector<int>>dp(grid.size(),vector<int>(grid[0].size()));
dp[0][0] = grid[0][0];
for(int i=1;i<grid.size();i++) dp[i][0] = dp[i-1][0]+grid[i][0];
for(int i=1;i<grid[0].size();i++) dp[0][i] = dp[0][i-1]+grid[0][i];
for(int i=1;i<grid.size();i++){
for(int j=1;j<grid[0].size();j++){
dp[i][j] = min(dp[i-1][j]+grid[i][j],dp[i][j-1]+grid[i][j]);
}
}
return dp[grid.size()-1][grid[0].size()-1];
}
};
力扣75题 颜色分类
需要进行两次遍历,使用两个指针分别用来交换0和1。
用指针p0来交换0,p1来交换1,初始值为0。
当我们从左向右遍历整个数组的时候。
如果找到了1,那么将其与nums[p1]进行交换,并将p1向后移动一个位置,与方法一相同。
如果找到了0,那么将其与nums[p0]进行交换,并将p0向后移动一个位置。连续的0之后是连续的1,因此如果将0
与nums[p0]进行交换的话,那么可能会把一个1交换出去。当p0<p1时,已经将一些1连续地放在头部,此时一定会把一个1交换出去,导致答案错误。
如果p0<p1,需要再将nums[i]与nums[p1]进行交换,其中i时当前遍历到的位置,在进行第一次交换后,nums[i]的值为1,需要将这个1放在头部的末端。
在最后,无论是否有p0<p1,需要将p0和p1均向后移动一个位置,而不是仅将p0向后移动一个位置。

class Solution {
public:
void sortColors(vector<int>& nums) {
int n = nums.size();
int p0 = 0,p1 = 0;
for(int i=0;i<n;i++){
if(nums[i]==1){
swap(nums[i],nums[p1]);
++p1;
}else if(nums[i]==0){
swap(nums[i],nums[p0]);
if(p0<p1){
swap(nums[i],nums[p1]);
}
++p0;
++p1;
}
}
}
};
力扣78题 子集
组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点!
子集问题是一种组合问题,因为集合是无序的,因此写回溯算法的时候,for就要从startindex开始,而不是从0开始。
1.递归函数参数
vector<vector<int>>result;
vector<int>path;
void backtracing(vector<int>&nums,int startindex){
2.递归终止条件
startindex已经大于数组的长度了,就终止了。
if(startindex>=nums.size()){
return;
}
3.单层搜索逻辑
求取子集问题,不需要任何剪枝!因为子集就是要遍历整棵树。
for(int i=startindex;i<nums.size();i++){
path.push_back(nums[i]);
backtracing(nums,i+1);
path.pop_back();
}
整体代码:
class Solution {
public:
vector<vector<int>>result;
vector<int>path;
void backtracing(vector<int>&nums,int startindex){
result.push_back(path);
if(startindex>=nums.size()){
return;
}
for(int i=startindex;i<nums.size();i++){
path.push_back(nums[i]);
backtracing(nums,i+1);
path.pop_back();
}
}
vector<vector<int>> subsets(vector<int>& nums) {
result.clear();
path.clear();
backtracing(nums,0);
return result;
}
};
力扣148题 排序链表
思想就是以下两个题目
力扣876题 链表的中间结点
力扣21题 合并两个有序链表
归并排序(分治)
1.找到链表的中间结点head2的前一个节点,并断开head2与其前一个节点的连接。就把原链表均分成了两段更短的链表,。
2.分治,递归调用sortlist,分别排序head和head2。
3.排序后,得到了两个有序链表,合并两个有序链表,得到排序后的链表,返回链表头节点。
class Solution {
public:
//876 链表的中间结点(快慢指针)
ListNode *middleNode(ListNode *head){
ListNode *pre = head;
ListNode *slow = head;
ListNode *fast = head;
while(fast && fast->next){
pre = slow;
slow = slow->next;
fast = fast->next->next;
}
pre->next = nullptr;
return slow;
}
// 合并两个有序链表
ListNode *mergeTwoLists(ListNode *list1,ListNode *list2){
ListNode dummy;
ListNode *cur = &dummy;
while(list1 && list2){
if(list1->val < list2->val){
cur->next = list1;
list1 = list1->next;
}else{
cur->next = list2;
list2 = list2->next;
}
cur = cur->next;
}
cur->next = list1 ? list1:list2;
return dummy.next;
}
ListNode* sortList(ListNode* head) {
if(head==nullptr || head->next == nullptr){
return head;
}
ListNode * head2 = middleNode(head);
head = sortList(head);
head2 = sortList(head2);
return mergeTwoLists(head,head2);
}
};
力扣215题.数组中的第k个最大元素
使用编程语言的内置排序算法对数组nums进行排序,然后返回第N-k个元素即可。
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
sort(nums.begin(),nums.end());
return nums[nums.size()-k];
}
};
力扣221题最大正方形
思路和力扣72题编辑距离,还有力扣5题最长回文子串类似。
class Solution {
public:
int maximalSquare(vector<vector<char>>& matrix) {
int m = matrix.size(),n = matrix[0].size(),ans = 0;
vector<vector<int>>dp(m,vector<int>(n));
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(matrix[i][j] =='1'){
if(i==0 || j==0){
dp[i][j] = 1;
}else{
dp[i][j] = min(dp[i-1][j],min(dp[i][j-1],dp[i-1][j-1]))+1;
}
ans = max(ans,dp[i][j]);
}
}
}
return ans * ans;
}
};
力扣287题 寻找重复数
使用哈希表法记录数组的各个数字,当查找重复数字则直接返回。
1.初始化:新建HashSet,记为hmap。
2.遍历数组nums中的每个数字num:
(1)当num在hmap中,说明重复,直接返回num。
(2)将num添加至hmap中。
3.返回-1。本题中一定有重复数字,因此这里返回多少都可以。
class Solution {
public:
int findDuplicate(vector<int>& nums) {
unordered_map<int,bool>map;
for(int num:nums){
if(map[num]) return num;
map[num] = true;
}
return -1;
}
};
力扣 437题.路径总和III
思想和112题,113题很类似
思路:
1.先序递归 遍历每个节点
2.以每个节点作为起始节点DFS寻找满足条件的路径
先调用dfs函数从root开始查找路径,再调用pathsum函数到root左右子树开始查找。
class Solution {
public:
int rootSum(TreeNode *root,long long targetSum){
if(root==nullptr){
return 0;
}
int ret = 0;
if(root->val==targetSum){
ret++;
}
ret += rootSum(root->left,targetSum-root->val);
ret += rootSum(root->right,targetSum-root->val);
return ret;
}
int pathSum(TreeNode* root, int targetSum) {
if(root==nullptr){
return 0;
}
int ret = rootSum(root,(long long)targetSum);
ret += pathSum(root->left,targetSum);
ret += pathSum(root->right,targetSum);
return ret;
}
};