C++ leetcode
/**
* 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* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* p=new ListNode(-1);//头结点,为了方便后面用new
ListNode* re=p;//结果结点
int jin=0;//进位
while(l1!=NULL||l2!=NULL){//l1和l2中有一者不为空
if(l1==NULL)
l1=new ListNode(0);//l1比l2短,所以将l1补0
if(l2==NULL)
l2=new ListNode(0);//l2比l1短,所以将l2补0
int n=l1->val+l2->val+jin;//计算l1和l2的和
int realNum=n%10;//记录到新结点中的数字
jin=n/10;//进位
p->next=new ListNode(realNum);将新节点添加进来
p=p->next;//p后移
l1=l1->next;//l1后移
l2=l2->next;//l2后移
}
if(jin>0)//如果加到最后一位仍然有进位的时候
p->next=new ListNode(jin);//把进位直接补全到最后
return re->next;//由于一开始有头结点,所以答案是re->next
}
};
/**
* 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* addTwoNumbers(ListNode* l1, ListNode* l2)
{
//创建一个岗哨
ListNode* temp =new ListNode(-1);
ListNode* temp1 = temp;
int jin =0; //创建一个标量保存进位
while(l1!=NULL || l2!=NULL) //退出条件就是两个同时为空
{
//特殊处理 为空就挂上一个1
if(l1 == NULL)
{
l1= new ListNode(0);
}
if(l2 == NULL)
{
l2 =new ListNode(0);
}
//求和(包括上一次的进位)
int n = l1->val+l2->val+jin;
//求进位 和实际放入的值 下一次用
jin = n/10;
int realsum = n % 10;
//挂入新的节点
temp->next = new ListNode(realsum);
temp=temp->next;//后移 保存下一个节点
//全部后移
l1=l1->next;
l2=l2->next;
}
if(jin>0)
{
temp->next=new ListNode(1);
}
return temp1->next;
}
};
/**
* 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* addTwoNumbers(ListNode* l1, ListNode* l2)
{
//首先 创建 岗哨
//一个变量表示进位 当前节点和表示 两个节点和相加+进位数
//分情况如果两个不为空 这样
//如果如果单个为空 继续
//如果两个为空 终止
ListNode*temp=new ListNode(-1);
ListNode*temp1=temp;
int sum=0;//表示综合
int jin=0;//表示进位
while(l1!=nullptr || l2!=nullptr)
{
if(l1!=nullptr && l2!=nullptr)
{
sum=l1->val+l2->val+jin;
if(sum >=10)
{
sum=sum%10;
jin=1;
}
else
{
jin=0;
}
temp1->next=new ListNode(sum);
temp1=temp1->next;
l1=l1->next;
l2=l2->next;
}
else if(l1!=nullptr && l2 ==nullptr)
{
sum = l1->val+jin;
if(sum>=10)
{
sum=sum%10;
jin=1;
}
else
{
jin=0;
}
temp1->next= new ListNode(sum);
temp1=temp1->next;
l1=l1->next;
}
else//l1为空 l2 不为空
{
sum = l2->val+jin;
if(sum>=10)
{
sum=sum%10;
jin=1;
}
else
{
jin=0;
}
temp1->next= new ListNode(sum);
temp1=temp1->next;
l2=l2->next;
}
}
if(jin==1)
{
temp1->next=new ListNode(1);
}
return temp->next;
}
};
很容易想到最快的方法是双指针
分析过程
其他方法有1计算长度 2 通过栈的方式很容易找到要删除的节点和前驱节点。时间复杂度和空间复杂度都是o(n)
/**
* 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* removeNthFromEnd(ListNode* head, int n)
{
//我觉的先创建一个岗哨 如果删除的是第一个节点也好返回
ListNode* temp = new ListNode(-1);
temp->next = head;
ListNode* temp1=temp;
ListNode* temp2=temp;
//举个例子 这次我不是找到那个节点 我是要找到删除节点的前一个方便我删除所以n-1
for(int i = 0;i<n+1;i++)
{
temp2=temp2->next;
}
while(temp2!=NULL && temp1!=NULL)
{
temp2=temp2->next;
temp1=temp1->next;
}
temp1->next=temp1->next->next;
ListNode* ans =temp->next;
delete temp;
return ans;
}
};
第三步和第四步可以合并写在一起,如下代码
比较重要的是我们根据while设置两个退出条件 退出后记得判断哪一种退出的
class Solution {
public:
void nextPermutation(vector<int>& nums)
{
//1第一步 从后向前 找升序
int i =nums.size()-2;//如果用i 和 i--要用i--进行越界判断。 所以我们用i 和 i++ 对i进行判断就好
while(i>=0 && nums[i] >= nums[i+1])
{
i--;//移动到下一个
}
//跳出循环两种可能 1越界了 2要么找到了
if(i>=0)
{
int k = nums.size()-1;
//这边不同判断k的边界 最坏的情况就是k=j
while(nums[k]<=nums[i])
{
k--;
}
//第二部
swap(nums[i],nums[k]);
//第三步
reverse(nums.begin()+i+1,nums.end());
}
else
{
//第四部 特殊情况
reverse(nums.begin(),nums.end());
}
}
};
class Solution {
public:
void nextPermutation(vector<int>& nums)
{
//1第一步 从后向前 找升序
int i =nums.size()-2;//如果用i 和 i--要用i--进行越界判断。 所以我们用i 和 i++ 对i进行判断就好
while(i>=0 && nums[i] >= nums[i+1])
{
i--;//移动到下一个
}
//跳出循环两种可能 1越界了 2要么找到了
if(i>=0)
{
int k = nums.size()-1;
//这边不同判断k的边界 最坏的情况就是k=j
while(nums[k]<=nums[i])
{
k--;
}
//第二部
swap(nums[i],nums[k]);
//第三步
}
reverse(nums.begin()+i+1,nums.end());
}
};
第一个想法就是回溯,非a就是b 如果和超过了那就回退 并且for循环后面的就不走了,提前排序
看了题解发现想法一样
第二个就是可以重复!!! 所以start从i开始而不是从i+1开始
特别注意第二个代码,我对题解进行的修改,用时更少,把终止条件放在for循环 及时退出(特殊)。特别注意退出前把当前位置的sum回溯。一个for循环控制的是一个格子,所有退出吧当前格子的数减去 返回上一个格子继续考虑
backtracking(candidates,target,sum+candidates[i],i);
class Solution {
public:
vector<vector<int>> result;
vector<int> path;
void backtracking(vector<int>& candidates,int& target,int& sum,int start)
{
//递归终止条件 2种
if(sum == target)
{
result.push_back(path);
}
if(sum > target)
{
return;
}
//递归回溯
for(int i =start;i<candidates.size();i++)
{
sum = sum+candidates[i];
path.push_back(candidates[i]);
backtracking(candidates,target,sum,i);//特别注意这边传入的是i
sum =sum-candidates[i];//两种情况 符合条件条件如result 回溯 不符合 一样回溯
path.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target)
{
result.clear();
path.clear();
//sort(candidates.begin(),candidates.end());
int sum = 0;
backtracking(candidates,target,sum,0);
return result;
}
};
class Solution {
public:
vector<vector<int>> result;
vector<int> path;
void backtracking(vector<int>& candidates,int& target,int& sum,int start)
{
//递归终止条件 2种
if(sum == target)
{
result.push_back(path);
}
//递归回溯
for(int i =start;i<candidates.size();i++)
{
sum = sum+candidates[i];
if (sum > target)
{
sum = sum - candidates[i];
break;
}
path.push_back(candidates[i]);
backtracking(candidates,target,sum,i);//特别注意这边传入的是i
sum =sum-candidates[i];//两种情况 符合条件条件如result 回溯 不符合 一样回溯
path.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target)
{
result.clear();
path.clear();
sort(candidates.begin(),candidates.end());
int sum = 0;
backtracking(candidates,target,sum,0);
return result;
}
};
第一个想法就是 从坐标中找到规律进行移动,或者多次翻转(但是没找到规律啊)
看了题解:顺时针90度应该是左上/右下对角线翻转+左右翻转,或者右上/左下对角线翻转+上下翻转。时间复杂度n^2
class Solution {
public:
void rotate(vector<vector<int>>& matrix)
{
int n =matrix.size();
//左上 右下进行对折翻转 举个例子就是坐标对调 遍历一个三角就好
for(int i =0;i < n ;i++)
{
for(int j =0;j<i;j++)
{
swap(matrix[i][j],matrix[j][i]);
}
}
// 再沿竖线进行左右翻转 行数不变 列数 = n-i 和i
for(int i =0;i<n;i++)
{
for(int j = 0, k = n - 1; j < k ; j++, k--) //类似于双指针,由两端向中心靠齐
swap(matrix[i][j],matrix[i][k]);
}
}
};
class Solution {
public:
void rotate(vector<vector<int>>& matrix)
{
int n =matrix.size();
//左上 右下进行对折翻转 举个例子就是坐标对调 遍历一个三角就好
for(int i =0;i < n ;i++)
{
for(int j =0;j<i;j++)
{
swap(matrix[i][j],matrix[j][i]);
}
}
// 再沿竖线进行左右翻转 行数不变 列数 = n-i 和i
for(int i =0;i<n;i++)
{
//for(int j = 0, k = n - 1; j < k ; j++, k--) //类似于双指针,由两端向中心靠齐
// swap(matrix[i][j],matrix[i][k]);
for(int j =0;j<= (n-1)/2;j++)
{
swap(matrix[i][j],matrix[i][n-1-j]);
}
}
}
};
class Solution {
public:
void rotate(vector<vector<int>>& matrix)
{
int n=matrix.size();
//我先左上到右下翻转把
//就是先遍历一个三角
for(int i=0;i<n;i++)//遍历第几行
{
for(int j=0;j<i;j++)//固定每行几个
{
swap(matrix[i][j],matrix[j][i]);
}
}
//左右翻转 遍历所有的行 一半的列
for(int i=0;i<n;i++)
{
for(int j=0;j<n/2;j++)
{
swap(matrix[i][j],matrix[i][n-j-1]);
}
}
}
};
class Solution {
public:
int minPathSum(vector<vector<int>>& grid)
{
int n = grid.size();
int m=grid[0].size();
vector<vector<int>> dp(n,vector<int>(m));
//初始化
dp[0][0] =grid[0][0];
//初始化第一列
for(int i = 1;i<n;i++)
{
dp[i][0]=grid[i][0]+dp[i-1][0];
}
for(int i = 1;i<m;i++)
{
dp[0][i]=grid[0][i]+dp[0][i-1];
}
for(int i =1;i<n;i++)
{
for(int j=1;j<m;j++)
{
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j];
}
}
return dp[n-1][m-1];
}
};
不存在奇数换奇数 因为k都是经过i走过的路 k走过的都会变成偶数或者自己和自己更换
) 只是进行了两次而已 o(n)就是放在结尾的你需要考虑2替换2的情况 所以把原先if更换为while进行遍历替换 这个我没想到 这个写法最简便
while (i <= p2 && nums[i] == 2)
swap(nums[i], nums[p2--]);
class Solution {
public:
void sortColors(vector<int>& nums)
{
int n =nums.size();
int k=0;
for(int i =0;i<n;i++)
{
if(nums[i]==0)
{
swap(nums[k++],nums[i]);
}
}
for(int i =k;i<n;i++)
{
if(nums[i]==1)
{
swap(nums[k++],nums[i]);
}
}
}
};
class Solution {
public:
void sortColors(vector<int>& nums)
{
int n =nums.size();
int k=0;
int k2=n-1;
for(int i =0;i<=k2;i++)
{
if(nums[i]==0)
{
swap(nums[i],nums[k++]);
}
if(nums[i]==2)
{
//找到一个不是2的位置
while(i<k2 && nums[k2]==2) k2--;
if(i<k2)
{
swap(nums[i--],nums[k2--]);
}
}
}
}
};
class Solution
{
public:
void sortColors(vector<int> &nums)
{
int n = nums.size();
int p0 = 0, p2 = n - 1;
for (int i = 0; i <= p2; i++)
{
while (i <= p2 && nums[i] == 2)//防止p2位置原本就是2,交换后2被遗漏在了前面
swap(nums[i], nums[p2--]);
if (nums[i] == 0)
swap(nums[i], nums[p0++]);
}
}
};
class Solution {
public:
void sortColors(vector<int>& nums)
{
int n =nums.size();
int k=0;
for(int i=0;i<n;i++)
{
if(nums[i]==0)
{
swap(nums[i],nums[k++]);
}
}
for(int i=k;i<n;i++)
{
if(nums[i]==1)
{
swap(nums[i],nums[k++]);
}
}
}
};
看到题目属实没啥思路…难道思考每次多一个数会多几种变化?
属实有点难想了
class Solution {
public:
int numTrees(int n)
{
vector<int> dp(n + 1, 0);//下标代表整数 所以n+1 最大值+1
dp[0] = 1;
dp[1] = 1;
//外层循环从2开始 0和1初始化了
for(int i =2;i<=n;i++)
{
//内层循环控制跟f累加
for(int j = 1;j<=i;j++)
{
dp[i]=dp[i]+dp[j-1]*dp[i-j];
}
}
return dp[n];
}
};
/**
/**
* 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<TreeNode*> ans;
void dfs(TreeNode* root)
{
//递归终止条件
if(root == nullptr) return;
//先序遍历 跟左右
//逻辑处理
ans.push_back(root);
dfs(root->left);
dfs(root->right);
}
void flatten(TreeNode* root)
{
if(root ==NULL) return;
dfs(root);
for(int i=0;i<ans.size()-1;i++)//前后挂钩
{
ans[i]->right=ans[i+1];
ans[i]->left=NULL;
}
}
};
利用前驱判断 剩下很多功夫
不知道为啥 auto的方式更省时间
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> hash;
for(auto x : nums) hash.insert(x); //放入hash表中
int res = 0;
for(auto x : hash)
{
if(!hash.count(x-1))
{
int y = x; //以当前数x向后枚举
while(hash.count(y + 1)) y++;
res = max(res, y - x + 1); //更新答案
}
}
return res;
}
};
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> set;
//全部插入 取出重复
for(int i =0;i<nums.size();i++)
{
set.insert(nums[i]);
}
int result=0;//保存最大长度
for(int i =0;i<nums.size();i++)
{
if(set.count(nums[i]-1)==0)//表示前驱不存在
{
int x =nums[i];//保存第一个值
int y = x;
while(set.count(y)==1) y++;//退出时指向第一个不存在的下标
result=max(result,y-x);
}
}
return result;
}
};
class Solution {
public:
int longestConsecutive(vector<int>& nums)
{
unordered_set<int> hash;
for(auto x:nums) hash.insert(x);
int result=0;
for(auto x:hash)
{
if(hash.count(x-1)==0)
{
int y=x;
while(hash.count(y)==1) y++;//最后退出指向一个不存在的
int ans = y-x;
result=max(result,ans);
}
}
return result;
}
};
位运算求解 或者一次循环判断求解,但是第二个方法注意就是需要先排序,题目没有排序...如果题目又说排序 第二种方法可能更容易(所以第一种方法更快)
class Solution {
public:
int singleNumber(vector<int>& nums)
{
int result =0;
for( auto x : nums)
{
result=result^x;
}
return result;
}
};
class Solution {
public:
int singleNumber(vector<int>& nums)
{
sort(nums.begin(),nums.end());
for(int i =1;i<nums.size();i+=2)
{
if(nums[i]!=nums[i-1])
return nums[i-1];//一定是前面那个数 i移动是偶数上 不同的数在奇数上
}
//循环走完了 还没有退出 那就是出现在最后一个位置了
return nums[nums.size()-1];
}
};
对于乘法,我们需要注意,负数乘以负数,会变成正数,所以解这题的时候我们需要维护两个变量,当前的最大值,以及最小值,最小值可能为负数,但没准下一步乘以一个负数,当前的最大值就变成最小值,而最小值则变成最大值了
dp_max[i]=max(max(dp_max[i-1]*nums[i],nums[i]),dp_min[i-1]*nums[i]);
dp_min[i]=min(min(dp_min[i-1]*nums[i],nums[i]),dp_max[i-1]*nums[i]);
看了题解思路写了代码 有一个地方写错了就是result最大值初始化为nums[0]忘记了
当然可以空间优化 因为每次只用到前一个值,第二个代码有简介版本
class Solution {
public:
int maxProduct(vector<int>& nums)
{
int n =nums.size();
if(n == 0) return 0;
else if(n == 1) return nums[0];
vector<int> dp_min(n);
vector<int> dp_max(n);
dp_min[0]=nums[0];
dp_max[0]=nums[0];
int result=nums[0];
for(int i=1;i<n;i++)
{
dp_max[i]=max(max(dp_max[i-1]*nums[i],nums[i]),dp_min[i-1]*nums[i]);
dp_min[i]=min(min(dp_min[i-1]*nums[i],nums[i]),dp_max[i-1]*nums[i]);
result=max(result,dp_max[i]);
}
return result;
}
};
class Solution {
public:
ListNode* middleNode(ListNode* head) {
ListNode* slow = head;
ListNode* fast = head;
while (fast != NULL && fast->next != NULL) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
};
关于下一题的重要补充
认真总结
自己写了一遍如下,特别注意链表的断开 和 快慢指针的设置
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptrptr) {}
* ListNode(int x) : val(x), next(nullptrptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* sortList(ListNode* head)
{
//1递归终止条件 只有一个节点 或者一个节点没有
if (head == nullptr || head->next == nullptr)
{
return head;
}
//2当前层的逻辑 一分为二 把一个链表彻底分为两个链表
ListNode* midNode = middleNode(head);
ListNode* rightHead = midNode->next;//下一个节点为第二个链表的头节点
midNode->next = nullptr;//第一个链表的尾部设置为NULL为了后面有序链表的合并
//3递归调用
ListNode* left = sortList(head);
ListNode* right = sortList(rightHead);
// 4 归并 有序链表的合并
return mergeTwoLists(left, right);
}
// 找到链表中间节点(876. 链表的中间结点)
ListNode* middleNode(ListNode* head)
{
//切记 我们要返回的是第一个链表的最后一个节点slow
//所以不能用fast=head 对于偶数个节点 会分成3 和 1
//只能用 fast=head->next =head->next->next
ListNode* slow = head;
ListNode* fast = head->next->next;//这样确保所得中是偏向前的
//ListNode* fast = head;
while (fast != nullptr && fast->next != nullptr) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
// 合并两个有序链表(21. 合并两个有序链表)
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* temp = new ListNode(-1);
ListNode* temp1 = temp;
while(l1 != nullptr && l2 != nullptr) {
if(l1->val < l2->val)
{
temp->next = l1;
l1 = l1->next;
temp = temp->next;
} else
{
temp->next = l2;
l2 = l2->next;
temp = temp->next;
}
}
if(l1!=nullptr)
{
temp->next=l1;
}
if(l2!=nullptr)
{
temp->next=l2;
}
return temp1->next;
}
};
第二次遇到,写错了,错误代码如下,原因在于如果a走到了空,此时a=b就算一步,不能a=b 然后 next连续走两步。第二段代码正确
/**
* 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)
{
if (headA == nullptr || headB == nullptr)
{
return nullptr;
}
ListNode* pa=headA;
ListNode* pb=headB;
while(headA!=headB)
{
if(headA==NULL)
{
headA=pb;
}
if(headB==NULL)
{
headB=pa;
}
headA=headA->next;
headB=headB->next;
}
return headA;
}
};
/**
* 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)
{
if (headA == nullptr || headB == nullptr)
{
return nullptr;
}
ListNode* pa=headA;
ListNode* pb=headB;
while(headA!=headB)
{
if(headA==NULL)
{
headA=pb;
}
else
headA=headA->next;
if(headB==NULL)
{
headB=pa;
}
else
headB=headB->next;
}
return headA;
}
};
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
sort(nums.begin(),nums.end());
int l=nums.size();
return nums[l-k];
}
};
class Solution {
public:
vector<int> t;
void merge_sort(vector<int>& a,int l,int r)
{
//递归终止条件
if(l == r) return;
//处理当前层的逻辑
int mid = (l+r)/2;
//进入下一层
merge_sort(a,l,mid);
merge_sort(a,mid+1,r);
//汇总结果
int i=l;
int j=mid+1;
for(int k=l;k<=r;k++)
{
//2 种情况 //注意
if((j > r|| a[i]<=a[j] && i<=mid) )
{
t[k] = a[i];
i++;
}
else
{
t[k]=a[j];
j++;
}
}
for(int k=l;k<=r;k++)
{
a[k]=t[k];
}
}
int findKthLargest(vector<int>& nums, int k)
{
vector<int> t_new(nums.size());
t =t_new;
merge_sort(nums,0,nums.size()-1);
return nums[nums.size()-1-k+1];
}
};
#include
using namespace std;
void quickSort(int s[], int l, int r)
{
//终止条件
if (l>= r) //元素个素只有一个
{
return;
}
//处理当前层的逻辑
int i = l, j = r, x = s[l];//枢纽
while (i < j)//注意是从右边开始 因为我们是取左边第一个为枢纽 那个值可以填入
{
while(i < j && s[j]>= x) // 从右向左找第一个小于x的数
j--;//下一个待比较的值
if(i < j && s[j]<x ) //右边第一个小于枢纽的值 填入到 当前i的位置
{
s[i]=s[j];
i++;//i值被覆盖了 指向下一个待比较的数字
}
while(i < j && s[i]< x) // 从左向右找第一个大于等于x的数
i++;
if(i < j)
{
s[j]=s[i];
j--;//指向下一个待比较的数字
}
}
s[i] = x;
//进入下一层
quickSort(s, l, i - 1); // 递归调用
quickSort(s, i + 1, r);
}
int main()
{
int array[]={34,65,12,43,67,5,78,10,3,70},k;
int len=sizeof(array)/sizeof(array[0]);
cout<<"The orginal arrayare:"<<endl;
for(k=0;k<len;k++)
cout<<array[k]<<",";
cout<<endl;
quickSort(array,0,len-1);
cout<<"The sorted arrayare:"<<endl;
for(k=0;k<len;k++)
cout<<array[k]<<",";
cout<<endl;
system("pause");
return 0;
}
class Solution {
public:
int maximalSquare(vector<vector<char>>& matrix) {
int m=matrix.size(),n=matrix[0].size();
vector<vector<int>> dp(m,vector<int>(n,0));
int max=0;
for(int i=0;i<m;i++)
{
dp[i][0]=matrix[i][0]-'0';
//这个方法好 就修改一次
if(max==0&&matrix[i][0]=='1')
max=1;
}
for(int j=0;j<n;j++)
{
dp[0][j]=matrix[0][j]-'0';
if(max==0&&matrix[0][j]=='1')
max=1;
}
for(int i=1;i<m;i++)
{
for(int j=1;j<n;j++)
{
if(matrix[i][j]=='1')
{
dp[i][j]=min(min(dp[i-1][j-1],dp[i-1][j]),dp[i][j-1])+1;
if(dp[i][j]>max) max =dp[i][j];
}
else
dp[i][j]=0;
}
}
return max*max;
}
};
题解有一个方法很巧妙 就是快慢指针找到中间节点,然后翻转前半段,最后比较(一般就会要求这种解法)
自己写了一下翻转的写法,有几个注意的点 一个是翻转后半段方便比较,因为如果奇数个此时翻转前半段必然可能第一个值不想等(还需要考虑奇数偶数的关旭)第二个就是快慢指针找中间节点 我这边固定前半段《=后半段,这样后面比较的时候只要比较前半段长度的次数就好,无需判断奇数偶数节点(这个方法无需额外的空间也更快 0(n))
/**
* 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:
bool isPalindrome(ListNode* head)
{
vector<int> vec;
while(head!=nullptr)
{
vec.push_back(head->val);
head=head->next;
}
int n= vec.size()-1;
for(int i =0,j=n;i<=j;i++,j--)
{
if(vec[i]!=vec[j])
{
return false;
}
}
return true;
}
};
/**
* 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* reverselist(ListNode* head)
{
//翻转链表 3个一组
ListNode* pre=nullptr;
ListNode*cur=head;
ListNode*next =nullptr;
while(cur!=nullptr)
{
next=cur->next;//保存要修改的指向
cur->next=pre;
pre=cur;
cur=next;
}
return pre;
}
bool isPalindrome(ListNode* head)
{
if(head==nullptr||head->next==nullptr) return true;
//我觉的喜欢前半段长度 <=后半段长度还是用如下的方式
ListNode* slow =head;
ListNode* quick=head->next->next;
while(quick!=nullptr && quick->next!=nullptr)
{
//(参考链表排序哪一章节)
//偶数节点 slow会指向中间一组的前一个
//奇数节点 slow会指向中间的前一个
slow=slow->next;
quick=quick->next->next;
}
ListNode* head1=slow->next;//保存一下后半段的头节点
slow->next=nullptr;
ListNode* cur1 =head;//前半段
ListNode* cur2 =reverselist(head1);
//这边特别注意 我们前半段固定小于=后半段 所以以前半段长度为基准
while(cur1!=nullptr)
{
if(cur1->val!=cur2->val) return false;
cur1 =cur1->next;
cur2=cur2->next;
}
return true;
}
};
之前做过类似的很容易想到用类似dp的方法去做这个题,但是要想想用常数的空间(看了题解发现之前的方法就是常数(因为输出的数组不算辅助空间,差点忘记了))
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums)
{
int n =nums.size();
vector<int> dp(n);//从第0行开始
dp[0]=1;//
//注意 第i行用的是第i-1的数 比如第二行用第一个数 所以是i-1
for(int i=1;i<n;i++)
{
dp[i]=dp[i-1]*nums[i-1];
}
//从后向前 处理另外一个三角形
//从倒数第二行还是
int temp=1;//需要一个中间值保存
//倒数第二行 用倒数第一个数 +1
for(int i =n-2;i>=0;i--)
{
temp=temp*nums[i+1];
dp[i]=dp[i]*temp;
}
return dp;
}
};
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target)
{
int n =matrix.size();
if(n == 0)
return false;
int m=matrix[0].size();
int i= 0;
int j =m-1;
while(i<n && j>=0)
{
if(matrix[i][j] < target)
{
//去掉一行
i++;
}
else if(matrix[i][j] > target)
{
//去掉列
j--;
}
else
{
return true;
}
}
return false;
}
};
重新归纳一下背包问题的动态规划
// 版本一
class Solution {
public:
int numSquares(int n) {
vector<int> dp(n + 1, INT_MAX);
dp[0] = 0;
for (int i = 0; i <= n; i++) { // 遍历背包
for (int j = 1; j * j <= i; j++) { // 遍历物品
dp[i] = min(dp[i - j * j] + 1, dp[i]);
}
}
return dp[n];
}
};
总结:数组中查找某一个数 可以在二分上思考一下下
出现这种问题找个边界上的测试 就行了 1233
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int fast = 0, slow = 0;
while(true){
fast = nums[nums[fast]];
slow = nums[slow];
if(fast == slow)
break;
}
int finder = 0;
while(true){
finder = nums[finder];
slow = nums[slow];
if(slow == finder)
break;
}
return slow;
}
};
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int n=nums.size()-1;
int left=1,right=n;
while(left<right){
int mid=left+(right-left)/2;
int count=0;
for(int num:nums)if(num<=mid)count++;//统计小于等于这个数的
if(count>mid)right=mid;//如果这个数量大于这个mid说明就在小于等于这边否则在另外一边 包含mid
else left=mid+1;
}
return left;
}
};
时间复杂度是nlogn 空间复杂度o1
bool cmp(const pair<int,int> &p1,const pair<int,int> &p2)
{
return p1.second > p2.second;//从大到小
}
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k)
{
unordered_map<int, int> map;
for (auto i: nums)
map[i]++;
//这边也可以是二维数组 也可以
vector<pair<int, int>> temp;
int i = 0;
//这边可以使用迭代器 或者auto的方式
for (auto it = map.begin();it != map.end(); it++)
{
//temp.emplace_back(make_pair(it->first, it->second));
temp.emplace_back(pair(it->first, it->second));
}
sort(temp.begin(), temp.end(), cmp);
vector<int> res(k);
for (int i = 0;i < k;i++)
res[i] = temp[i].first;
return res;
}
};
需要两个辅助栈和两个临时变量,分为4种情况
需要自己演变一下这个过程,我的理解因为res临时变量始终表示需要重复的量,字符串栈中的表示的是对于当前阶段只需要重复一次的,因为都是[之前的。阶段变化一定是从内部考虑的。如果是并列的【】【】那就是前面先考虑。当我们从]取出后并没有入栈,因为还可能重复
对入栈的问题需要考虑3个 1 栈元素是什么 2 什么时候入栈 3 什么时候出栈
对于这类问题 我觉的应该从一个最简单的结构进行分析 例如a3[bbb] 分解成最简单的一个结构
class Solution {
public:
string decodeString(string s) {
//两个栈分别压int res和用pair
stack<pair<int, string>> sta;
int num = 0; string res = "";
//循环检查字符串
for (int i = 0; i < s.size(); i++) {
//遇到数字则存入num
if (s[i] >= '0'&&s[i] <= '9') {
num *= 10;
num += (s[i] - '0');//这里括号是否需要
}
else if (s[i] == '[') {//遇到[压栈数字和字符串,置零置空
sta.push(make_pair(num, res));
num = 0;
res = "";
}
else if (s[i] == ']') {//遇到]出栈数字和字符串,组装
int n = sta.top().first;//n指示的是res的循环次数,不是a的
string a = sta.top().second;
sta.pop();
for (int i = 0; i < n; i++) a = a + res; //循环n次
res = a;
}
else {//遇到字符存入字符
res += s[i];
}
}
return res;
}
};
代码回想录
题解总结:(套路):一般这种数对,还涉及排序的,根据第一个元素正向排序,根据第二个元素反向排序,或者根据第一个元素反向排序,根据第二个元素正向排序,往往能够简化解题过程。
在本题目中,我首先对数对进行排序,按照数对的元素 1 降序排序,按照数对的元素 2 升序排序。原因是,按照元素 1 进行降序排序,对于每个元素,在其之前的元素的个数,就是大于等于他的元素的数量,而按照第二个元素正向排序,我们希望 k 大的尽量在后面,减少插入操作的次数(或者说保证正确性) 比如(5,2)(5,3)很明显顺序相同的身高按照第二个元素升序 降序必然会错误
步骤
注意
问题
注意看代码的区别,list是双向迭代器 只能++ -- 而vector deque是随机访问迭代器 可以 + 3 -4 这种 所以代码有所区别
class Solution {
public:
//如果是类内写cmp 需要加上static
bool static cmp(const vector<int>& a, const vector<int>&b)
{
if(a[0]>b[0]) return true;//true表示ab位置不变
else if(a[0] < b[0]) return false;//false 表示改变
else
{
//或者如下简洁写法 直接把要求写在return后面
return a[1]<b[1];
}
}
vector<vector<int>> reconstructQueue(vector<vector<int>>& people)
{
sort(people.begin(),people.end(),cmp);
vector<vector<int>> result;
for(int i =0;i<people.size();i++)
{
int position = people[i][1];
result.insert(result.begin()+position,people[i]);//参数一 iterator loc
}
return result;
}
};
// 版本二
class Solution {
public:
// 身高从大到小排(身高相同k小的站前面)
static bool cmp(const vector<int> a, const vector<int> b) {
if (a[0] == b[0]) return a[1] < b[1];
return a[0] > b[0];
}
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
sort (people.begin(), people.end(), cmp);
list<vector<int>> que; // list底层是链表实现,插入效率比vector高的多
for (int i = 0; i < people.size(); i++) {
int position = people[i][1]; // 插入到下标为position的位置
std::list<vector<int>>::iterator it = que.begin();
while (position--) { // 寻找在插入位置
it++;
}
que.insert(it, people[i]);
}
return vector<vector<int>>(que.begin(), que.end());
}
};
其实我自己第一个想法就是归为 第一次遍历,把数字交换对应的下标的位置(比如1 放入下标0 i-1的位置)如果 (1)情况1 两个数字一样 那就当前置为0 (2)如果交换的位置是0 那就换过去,把自己置为0 后移(3)如果遇到0就后移(4) 如果两个数字不一样都不是零 这边应该需要一个while不断交换。 第二次遍历就应该找0了
总结 两种方法都是类似的 就是希望在原本数组信息做标记 但是又能保存原来的信息 ,一个是+n 后% 一个是修改正负
看了题解,发现自己的方法还是太笨了,正常的用辅助数组的方法是出现的我们在对应的数组位置标记上。
方法一如下:那如何在原数组进行标记么,我们完全可以把原本的正数变成负数,这样就可以保存原来的信息,比如一开始修改后边的数组元素 把正数变成负数,等便利到的时候我们可以取出取绝对值就好了。
class Solution {
public:
vector<int> findDisappearedNumbers(vector<int>& nums)
{
int n=nums.size();
vector<int> res;
if(n<=0) return res;
for(int i =0;i<n;++i)
{
int index =abs(nums[i])-1;//表示我要修改的坐标
//注意这边的判断!!!!!!!
if(nums[index]>0) nums[index]=-nums[index];
}
for(int i=0;i<n;++i)
{
if(nums[i]>0) res.push_back(i+1);
}
return res;
}
};
class Solution {
public:
vector<int> findDisappearedNumbers(vector<int>& nums) {
vector<int> res;
if(nums.empty()) return nums;
for(int i=0;i<nums.size();i++)
{
int index=(nums[i]-1)%nums.size();
nums[index]+=nums.size();
}
for(int i=0;i<nums.size();i++)
{
if(nums[i]<=nums.size())
res.push_back(i+1);
}
return res;
}
};
。
class Solution {
public:
vector<int> findDisappearedNumbers(vector<int>& nums)
{
vector<int> res;
for(int i=0;i<nums.size();i++)
{
int index = abs(nums[i])-1;//注意点一
if(nums[index]>0)//表示没有标记过 注意点儿
{
nums[index] =-nums[index];
}
}
for(int i =0;i<nums.size();++i)
{
if(nums[i]>0) res.push_back(i+1);//注意点三
}
return res;
}
};
class Solution {
public:
int hammingDistance(int x, int y)
{
int result= x^y;
int count=0;
while(result!=0)
{
result =result & (result-1);
count++;
}
return count;
}
};
我的第一个想法:遇到最长最短 方案数可以想象动态规划
我的第二个想法: 回溯 非加 就是减
看了题解…确实想的差不多
参考链接
在背包专栏总结了
就是要把这个问题转换一下,其实就是比较每一个子树(左子树最大深度和右子树最大深度的和),取其中的最大值
很容易出错的是我们默认以为最大直径就是过跟节点的左子树深度加右子树的深度,然后再主函数调用两次dfs 最后相加那么就错了
/**
* 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:
int max_result=0;
int dfs(TreeNode* root)
{
//递归终止条件
if(root==nullptr) return 0;//表示当前深度为0
int left= dfs(root->left);
int right =dfs(root->right);
max_result =max((left+right),max_result);
return max(left,right)+1;//还是照样保存当前子树的最大深度
}
int diameterOfBinaryTree(TreeNode* root)
{
dfs(root);
return max_result;
}
};
一篇文章解决所有二叉树路径问题(问题分析+分类模板+题目剖析)有空再看
第二遍复习
简单的说前缀和是思想,从3层循环暴力 减少到2层,哈希表是优化,从二层循环减少到一层
简单的说前缀表就是把问题转换成两数之差 会不会等于 k 情况的判断。
我们之前做过两次for循环求两数之和的题 通过hash减少时间复杂度 这题同理
特别的在于 前缀表的定义决定写法,如果下标表示长度 add(n+1) 长度从0开始 那么任意两数相减就可以遍历所有的情况 如果下标表示实际坐标 那么还要还要考虑 a-b的a本身
特别重要 注意下面 1 2代码的区别
当然代码还可以简洁 前缀表的空间还可以省去 代码一的改进,两个并列for循环可以同时进行 修改为第三个代码
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int n=nums.size();
vector<int>add(n,0);
add[0]=nums[0];
for(int i=1;i < n;i++)add[i]=add[i-1]+nums[i];
int ans=0;
unordered_map<int,int> map;
map[0]=1;//这边是判断等于本身的情况
for(int i=0; i<n ;i++)
{
if(map.count(add[i]-k))ans+=map[add[i]-k];
map[add[i]]++;
}
return ans;
}
};
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int n=nums.size();
vector<int>add(n+1,0);
//add[0]=nums[0];
for(int i=1;i <= n;i++)add[i]=add[i-1]+nums[i-1];
int ans=0;
unordered_map<int,int> map;
for(int i=0; i<=n ;i++)
{
if(map.count(add[i]-k))ans+=map[add[i]-k];
map[add[i]]++;
}
return ans;
}
};
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int n=nums.size();
unordered_map<int,int> map;
int pre = 0;
int count =0;
map[0] = 1;
for(int i=0; i<n ;i++)
{
pre =pre+nums[i];
if(map.count(pre-k)==1)
{
count+=map[pre-k];
}
map[pre]++;
}
return count;
}
};
、
想要沿用对称二叉树传入两个跟节点和构建二叉树返回值为节点的想法
,如果一边为空 那就直接返回另外一个树的节点 后面 就不用遍历了。/**
* 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:
TreeNode* dfs(TreeNode* root1, TreeNode* root2)
{
//终止条件 这种写法包含了 两边为空返回为null 和 只有一边为空的情况
if(root1 == nullptr)
{
return root2;
}
if(root2 ==nullptr)
{
return root1;
}
//处理当前层的逻辑
//构建新节点 挂上下一层传递上来的值 把当前层返回给上一层
TreeNode* new_node = new TreeNode(root1->val + root2->val);
new_node->left = mergeTrees(root1->left, root2->left);
new_node->right = mergeTrees(root1->right, root2->right);
return new_node;
}
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2)
{
TreeNode* root = dfs(root1,root2);
return root;
}
};
class Solution {
public:
TreeNode* dfs(TreeNode* root1, TreeNode* root2)
{
//终止条件 这种写法包含了 两边为空返回为null 和 只有一边为空的情况
if(root1 == nullptr)
{
return root2;
}
if(root2 ==nullptr)
{
return root1;
}
//处理当前层的逻辑
//构建新节点 挂上下一层传递上来的值 把当前层返回给上一层
TreeNode* new_node = new TreeNode(root1->val + root2->val);
TreeNode* left = dfs(root1->left, root2->left);
TreeNode* right = dfs(root1->right, root2->right);
//后序处理
new_node->left=left;
new_node->right=right;
return new_node;
}
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2)
{
TreeNode* root = dfs(root1,root2);
return root;
}
};
/**
* 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:
int sum =0;
void dfs(TreeNode* root)
{
if(root==nullptr) return;
dfs(root->right);
sum=sum+root->val;
root->val=sum;
dfs(root->left);
}
TreeNode* convertBST(TreeNode* root)
{
dfs(root);
return root;
}
};
最后一个
比最大值小的。右指针向左移动,不断保存最小值,找到最后一个
比最小值大的数。0(n)的时间复杂度class Solution {
public:
int findUnsortedSubarray(vector<int>& nums)
{
int n =nums.size();
int right_max = INT_MIN;
int left_min =INT_MAX;
int left=0;
int right=0;
for(int i =0 ; i < n ; i++)
{
if(nums[i] >= right_max) right_max=nums[i];//正常情况
else
{
right=i;
}
if(nums[n-i-1] <= left_min) left_min=nums[n-i-1]; //正常情况
else
{
left=n-i-1;
}
}
if (right==0) return 0;
else return right-left+1;
}
};
题目分为4种
第一眼看到这个题目,其实没有想到前缀和+回溯来解决。但是看了题解很容易理解,如果想到用前缀和来解题(单独思考一条路劲),然后再想到如果想到从左子树返回到父子节点再进入右子树需要修改当前前准和 和 map的值就可以想到用回溯
和之前做的一个题很像,我们只要知道有几条路径,不需要知道具体的路劲,所以键是sum 值是数量
老样子要么一开始定义路径为0的数量为1,这样是为了找到本身,之前讲过
回溯什么东西么1 当你从下一层退回到上一层首先前缀和需要修改回去,第二个就是map对于当前层的前缀和减去
认真思考
/**
* 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:
unordered_map<int,int> map;//键表示sum综合 值表示数量
int res=0;//保存结果
//先序处理
void dfs(TreeNode* root,int& sum,int& cur_sum)
{
//递归终止条件
if(root==nullptr) return;
//处理当前层的逻辑
//1更新当前层的前缀和
cur_sum=cur_sum+root->val;
//2判断有没有符合条件的 有就统计结果 没有啥事没做
if(map.count(cur_sum-sum)==1)
res =res+map[cur_sum-sum];
//3 不管有没有符合的 把当前前缀和 放入map
map[cur_sum]++;
//4递归
dfs(root->left,sum,cur_sum);
dfs(root->right,sum,cur_sum);
//5回溯
map[cur_sum]--;
cur_sum=cur_sum-root->val;
return;
}
int pathSum(TreeNode* root, int targetSum)
{
map[0]=1;//表示本身为结果 不是差为结果
int cur_sum=0;
dfs(root,targetSum,cur_sum);
return res;
}
};
概括起来就是创建一个桶,长度为任务最多的那个数量,宽度为冷却时间+1.桶有一个缺口就是最后一个任务后面不用等待了所以需要计算并列最多的任务数。当这个桶装满了(相当于没有空闲的时间)剩下的任务可以扩充桶的宽度然后放入任务(需要多少扩充多少,不存在剩余时间),此时时间就是任务的数量。所以最后返回值就是桶的原始大小和任务数的最大值
class Solution {
public:
static bool cmp(int& p1,int& p2)
{
return p1>p2;
}
int leastInterval(vector<char>& tasks, int n)
{
int len =tasks.size();
int same=1;//表示数量也表示下标
vector<int> vec(26);
for(auto x:tasks)
{
vec[x-'A']++;
}
sort(vec.begin(),vec.end(),cmp);
while(same<vec.size() && vec[same]==vec[0]) same++;//找到相同最大
return max(len,same+(n+1)*(vec[0]-1));
}
};
所以while循环可以改成for循环 right是一直增加的不需要任何条件
class Solution {
public:
int lengthOfLongestSubstring(string s)
{
int n=s.size();
unordered_map<char,int> map;
int sum=0;
int left=0;
int right=0;
int result=0;
//如果右边的数不在map
while(right<=n-1 && left <= right)
{
if(map.count(s[right])==0)//不存在
{
//存入
map[s[right]]=right;
//计算值
sum=right-left+1;
result=max(result,sum);
//边界右移
right++;//下一个待判断的点
}
else//表示存在
{
//修改左边界
int k=map[s[right]]+1;//出现的位置+1
for(int i=left;i<k;i++)
{
map.erase(s[i]);
}
left=k;
map[s[right]]=right;//修改为最后一次存在的值
sum=right-left+1;//这边不加1 aba
result=max(result,sum);
right++;
}
}
return result;
}
};
class Solution {
public:
int lengthOfLongestSubstring(string s)
{
int n=s.size();
unordered_map<char,int> map;
int sum=0;
int left=0;
int right=0;
int result=0;
//如果右边的数不在map
for(right;right<n;right++)
{
if(map.count(s[right])==0)//不存在
{
//存入
map[s[right]]=right;
//计算值
sum=right-left+1;
result=max(result,sum);
//边界右移
}
else//表示存在
{
//修改左边界
int k=map[s[right]]+1;//出现的位置+1
for(int i=left;i<k;i++)
{
map.erase(s[i]);
}
left=k;
map[s[right]]=right;//修改为最后一次存在的值
sum=right-left+1;//这边不加1 aba
result=max(result,sum);
}
}
return result;
}
};
其次想明白为什么不是先序遍历,我一开始想把到当前节点偷和不偷的最大值传入下一层。但是这个存在一个问题,如果左节点和右节点判断的结果不同怎么办:如果左子树认为不偷父子节点到当前最大,右节点认为相反,没办法综合考量,所以用后续遍历,这样就可以综合考虑并且可以把结果汇总。
dp递推方程为当前层逻辑
注意返回值不要写反 不容易找
class Solution {
public:
vector<int> dfs(TreeNode* cur)
{
// 终止条件
if (cur == NULL) return vector<int>{0, 0};
//后续遍历
//递归
vector<int> left =dfs(cur->left);
vector<int> right =dfs(cur->right);
//处理当前层的逻辑
// 偷cur
int val1 = cur->val + left[0] + right[0];//0表示不偷
// 不偷cur
int val2 = max(left[0], left[1]) + max(right[0], right[1]);
//注意这边返回值第一个表示不偷 第二个表示偷
return {val2, val1};
}
int rob(TreeNode* root)
{
vector<int> result = dfs(root);
return max(result[0],result[1]);
}
};
dp[i] dp表示考虑到第i间房子能偷到的最大金额
确定递推公式,两个状态i房子偷或者不偷。当前房子偷那么dp[i]=dp[i-2]+nums[i] i-1的房子我们不考虑了 如果不偷 那么i-1的房子可以考虑,dp[i]=dp[i-1].dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
初始化就简单了
方法二 是我们自己做的一个修改,如果dp【i】表示第i间房子一定偷那么递推公式就是 dp[i] = max(dp[i - 2] , dp[i - 3])+nums[i];
可能存在间隔为1或者为2 但是不可能存在间隔为3. 最后返回值要么是最后一件偷了 要么最后一件没偷 两种可能。
写的时候注意到 修改了递推公式 就要修改初始化 修改了初始化就要注意特判,如果长度为2 那么dp【2】越界了 需要注意
class Solution {
public:
int rob(vector<int>& nums) {
if (nums.size() == 0) return 0;
if (nums.size() == 1) return nums[0];
vector<int> dp(nums.size());
dp[0] = nums[0];
dp[1] = max(nums[0], nums[1]);
for (int i = 2; i < nums.size(); i++) {
dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
}
return dp[nums.size() - 1];
}
};
class Solution {
public:
int rob(vector<int>& nums)
{
//如果dp【i】表示第i间屋子一定偷
if (nums.size() == 0) return 0;
if (nums.size() == 1) return nums[0];
if (nums.size()==2) return max(nums[0],nums[1]);
vector<int> dp(nums.size());
dp[0] = nums[0];
dp[1] = nums [1];
dp[2]= nums[0]+nums[2];
for (int i = 3; i < nums.size(); i++)
{
dp[i] = max(dp[i - 2] , dp[i - 3])+nums[i];
}
return max(dp[nums.size()-1],dp[nums.size()-2]);
}
};
// 注意注释中的情况二情况三,以及把198.打家劫舍的代码抽离出来了
class Solution {
public:
int rob(vector<int>& nums) {
if (nums.size() == 0) return 0;
if (nums.size() == 1) return nums[0];
int result1 = robRange(nums, 0, nums.size() - 2); // 情况二
int result2 = robRange(nums, 1, nums.size() - 1); // 情况三
return max(result1, result2);
}
// 198.打家劫舍的逻辑
int robRange(vector<int>& nums, int start, int end) {
if (end == start) return nums[start];
vector<int> dp(nums.size());
dp[start] = nums[start];
dp[start + 1] = max(nums[start], nums[start + 1]);
for (int i = start + 2; i <= end; i++) {
dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
}
return dp[end];
}
};
如有错误,欢迎指出,整理方便个人复习回顾。