目录
该文章刷题顺序按照代码随想录刷,只记录简单算法思路和源码
数组
704、二分查找
class Solution {
public:
int search(vector<int>& nums, int target) {
//二分查找
int n=nums.size();
int left=0,right=n-1;
while(left<=right){
int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
if (nums[middle] > target) {
right = middle - 1; // target 在左区间,所以[left, middle - 1]
} else if (nums[middle] < target) {
left = middle + 1; // target 在右区间,所以[middle + 1, right]
} else { // nums[middle] == target
return middle; // 数组中找到目标值,直接返回下标
}
}
return -1;
}
};
27、移除元素
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
/*双指针,快慢指针*/
int slow_ptr = 0;
for(int fast_ptr=0;fast_ptr<nums.size();fast_ptr++){
if(nums[fast_ptr]!=val) nums[slow_ptr++] = nums[fast_ptr];
}
return slow_ptr;
}
};
977、有序数组的平方
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
/**/
for(int i=0;i<nums.size();i++){
nums[i] = nums[i]*nums[i];
}
sort(nums.begin(),nums.end());
return nums;
}
};
209、长度最小的子数组
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
/*滑动窗口*/
int left=0,right=left;
int n = nums.size();
int sum_val = 0;
int min_val = INT_MAX;
while(right<n && left<=right){
sum_val+=nums[right];
if(sum_val<target) right++;
else {
min_val = min(min_val,right-left+1);
sum_val-=nums[left];
sum_val-=nums[right];
left++;
}
}
if(min_val==INT_MAX) return 0;
else return min_val;
}
};
59、螺旋矩阵2(经常出现)
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> Matrix(n,vector<int> (n,0));
if(n%2==1) Matrix[n/2][n/2] = n*n;
int left=0, right = n-1;
int top=0, botton = n-1;
int val = 1;
while(left<=right && top <=botton){
for(int j=left;j<right;j++){
Matrix[top][j] = val++;
}
for(int i=top;i<botton;i++){
Matrix[i][right] = val++;
}
for(int j=right;j>left;j--){
Matrix[botton][j] = val++;
}
for(int i=botton;i>top;i--){
Matrix[i][left] = val++;
}
left++;right--;
top++;botton--;
}
return Matrix;
}
};
链表
203、移除链表元素
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
if(head==nullptr) return head;
ListNode* yumNode = new ListNode(0);
yumNode->next = head;
ListNode* ptr_pre = yumNode;
while(head!=nullptr){
if(head->val==val) {
ptr_pre->next = head->next;
head = head->next;
}else{
ptr_pre = head;
head = head->next;
}
}
head = yumNode->next;
delete yumNode;
return head;
}
};
707、设计链表
class Node {
public:
int val=0;
Node* next=nullptr;
Node(){
this->val = 0;
this->next = nullptr;
}
Node(int val){
this->val = val;
this->next = nullptr;
}
};
class MyLinkedList {
private:
Node* head;
int sz; //表示链表的长度
public:
MyLinkedList() {
this->sz = 0;
this->head = nullptr;
}
int get(int index) {
if(index>=sz) return -1;
Node* ptr = this->head;
for(int i=0;i<index;i++){
ptr = ptr->next;
}
return ptr->val;
}
void addAtHead(int val) {
this->sz++;
Node* new_node = new Node(val);
new_node->next = this->head;
this->head = new_node;
return;
}
void addAtTail(int val) {
this->sz++;
Node* new_node = new Node(val);
Node* ptr = this->head;
if(ptr==nullptr) {
this->head = new_node;
return;
}
while(ptr->next!=nullptr){
ptr = ptr->next;
}
ptr->next = new_node;
return;
}
void addAtIndex(int index, int val) {
if(index>sz) return;
if(index==0){ //插入到头部
this->addAtHead(val);
return;
}
if(index==sz){
//插入到末尾
this->addAtTail(val);
return;
}
this->sz++;
Node* new_node = new Node(val);
Node* ptr = this->head->next;
Node* ptr_pre = this->head;
for(int i=1;i<index;i++){
ptr = ptr->next;
ptr_pre = ptr_pre->next;
}
ptr_pre->next = new_node;
new_node->next = ptr;
return;
}
void deleteAtIndex(int index) {
//删除指定元素
if(index>=this->sz) return;
this->sz--;
Node* temp = head;
if(index==0) {
head = head->next;
delete temp;
return;
}
for(int i=1;i<index;i++){
temp = temp->next;
}
Node* use = temp->next;
temp->next = temp->next->next;
delete use;
return;
}
};
206、翻转链表
class Solution {
public:
ListNode* reverseList(ListNode* head) {
/*递归*/
if(head==nullptr) return head;
ListNode* ptr_head = head;
ListNode* ptr_trail = head;
ListNode* ptr = head->next;
if(ptr==nullptr){
return head;
}
while(ptr->next!=nullptr){
ptr = ptr->next;
ptr_trail = ptr_trail->next;
}
head = ptr;
ptr_trail->next = nullptr;
ptr->next = reverseList(ptr_head);
return head;
}
};
24、两两交换链表中的节点
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
/*递归实现*/
if(head==nullptr || head->next==nullptr) return head;
ListNode* ptr_pre = head;
ListNode* ptr = head->next;
ListNode* new_head = ptr->next;
head = ptr;
ptr->next = ptr_pre;
ptr_pre->next = swapPairs(new_head);
return head;
}
};
19、删除链表中倒数第N个结点
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
//双指针解法
if(head==nullptr) return head;
ListNode* ptr_pre = head;
ListNode* ptr = head;
for(int i=0;i<n;i++) ptr = ptr->next;
if(ptr==nullptr) return head->next;
while(ptr->next!=nullptr){
ptr = ptr->next;
ptr_pre = ptr_pre->next;
}
ptr_pre->next = ptr_pre->next->next;
return head;
}
};
142、环形链表2
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
/*快慢指针,经典题目,数学关系要考虑清楚*/
if(head==nullptr) return head;
ListNode* fast=head;
ListNode* slow=head;
do{
fast = fast->next;
if(fast!=nullptr) fast = fast->next;
else return nullptr;
slow = slow->next;
}while(fast!=slow);
slow = head;
while(slow!=fast){
slow = slow->next;
fast = fast->next;
}
return slow;
}
};
哈希表
242、有效的字母异位词
class Solution {
public:
bool isAnagram(string s, string t) {
/*哈希表统计字母出现的次数
哈希表的三种类型:数组、unordered_set、unordered_map*/
int hash_map[26] = {0};
for(char i : s){
hash_map[i-'a']++;
}
for(char i : t){
hash_map[i-'a']--;
}
for(int i=0;i<26;i++){
if(hash_map[i] != 0) return false;
}
return true;
}
};
1002、查找共用字符
class Solution {
public:
vector<string> commonChars(vector<string>& words) {
/*统计每个字符串每个字母出现的频数,纵向取最小值*/
vector<string> res;
int n = words.size();
vector<vector<int>> keep_matrix(n+1,vector<int>(26,0));
for(int j=0;j<n;j++){
for(char i : words[j]){
keep_matrix[j][i-'a']++;
}
}
for(int j=0;j<26;j++){
keep_matrix[n][j] = INT_MAX;
for(int i=0;i<n;i++){
keep_matrix[n][j] = min(keep_matrix[n][j],keep_matrix[i][j]);
}
}
for(int i=0;i<26;i++){
while(keep_matrix[n][i]!=0){
string temp(1,i+'a');
res.push_back(temp);
keep_matrix[n][i]--;
}
}
return res;
}
};
349、两个数组的交集
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
/*用nums1做成哈希表,然后在哈希表中查找nums2的元素*/
unordered_set<int> res; //去重
unordered_set<int> hash_map;
for(int i=0;i<nums1.size();i++){
hash_map.insert(nums1[i]);
}
for(int i=0;i<nums2.size();i++){
if( hash_map.find(nums2[i])!= hash_map.end()) res.insert(nums2[i]);
}
return vector<int> (res.begin(),res.end());
}
};
202、快乐数
看似是数学问题,其实用计算机暴力搜索是可以搜索到的
class Solution {
public:
int getSum(int n){
int sum = 0;
while(n){
sum+=(n%10)*(n%10);
n/=10;
}
return sum;
}
bool isHappy(int n) {
/*一旦出现重复的数,则说明不是快乐数,因为有环*/
unordered_set<int> hash_map;
while(1){
int sum = getSum(n);
if (sum==1){
return true;
}
if(hash_map.find(sum)!=hash_map.end()) return false;
else{
hash_map.insert(sum);
}
n = sum;
}
}
};
1、两数之和
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
//在nums中查找值为target-nums[i]的数
vector<int> res;
unordered_map<int,int> hash_map; //用来存放关键字、值
for(int i=0;i<nums.size();i++){
auto it = hash_map.find(target-nums[i]);
if(it!=hash_map.end()){
res.push_back(i);
res.push_back(it->second);
return res;
}else{
hash_map[nums[i]]=i;
}
}
return res;
}
};
454、四数相加2
class Solution {
public:
int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
unordered_map<int, int> umap; //key:a+b的数值,value:a+b数值出现的次数
// 遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中
for (int a : A) {
for (int b : B) {
umap[a + b]++;
}
}
int count = 0; // 统计a+b+c+d = 0 出现的次数
// 再遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来。
for (int c : C) {
for (int d : D) {
if (umap.find(0 - (c + d)) != umap.end()) {
count += umap[0 - (c + d)];
}
}
}
return count;
}
};
383、赎金信
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
/*即判断ransomNote是否是magazine的子串*/
int hash_map[26]={0};
for(char i : magazine){
hash_map[i-'a']++;
}
for(int i=0;i<ransomNote.size();i++){
if(hash_map[ransomNote[i]-'a']==0) return false;
else {
hash_map[ransomNote[i]-'a']-=1;
}
}
return true;
}
};
15、三数之和(难点在于去重)
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
/*找到三个数,使得三数和为0*/
int n = nums.size();
vector<vector<int>> res;
if(n<3) return res;
sort(nums.begin(),nums.end());
for(int i=0;i<n-2;i++){
int left=i+1;
int right = n-1;
if(nums[i]>0) break;
if(i>0 && nums[i]==nums[i-1]){
continue;
}
int target = 0-nums[i];
while(left<right){
if(nums[left]+nums[right]>target) {
right--;
}else if(nums[left]+nums[right]<target){
left++;
}else{
vector<int> temp = {nums[i],nums[left],nums[right]};
res.push_back(temp);
while(right>left && nums[right] == nums[right-1]) right--;
while(right>left && nums[left] == nums[left+1]) left++;
left++;
right--;
}
}
}
return res;
}
};
18、四数之和
注意去重
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for (int k = 0; k < nums.size(); k++) {
// 剪枝处理
if (nums[k] > target && nums[k] >= 0) {
break; // 这里使用break,统一通过最后的return返回
}
// 对nums[k]去重
if (k > 0 && nums[k] == nums[k - 1]) {
continue;
}
for (int i = k + 1; i < nums.size(); i++) {
// 2级剪枝处理
if (nums[k] + nums[i] > target && nums[k] + nums[i] >= 0) {
break;
}
// 对nums[i]去重
if (i > k + 1 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1;
int right = nums.size() - 1;
while (right > left) {
// nums[k] + nums[i] + nums[left] + nums[right] > target 会溢出
if ((long) nums[k] + nums[i] + nums[left] + nums[right] > target) {
right--;
// nums[k] + nums[i] + nums[left] + nums[right] < target 会溢出
} else if ((long) nums[k] + nums[i] + nums[left] + nums[right] < target) {
left++;
} else {
result.push_back(vector<int>{nums[k], nums[i], nums[left], nums[right]});
// 对nums[left]和nums[right]去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
// 找到答案时,双指针同时收缩
right--;
left++;
}
}
}
}
return result;
}
};
字符串
344、反转字符串
class Solution {
public:
void reverseString(vector<char>& s) {
//交换收尾即可
int n = s.size();
int j=n-1;
for(int i=0;i<n/2;i++){
char temp = s[i];
s[i] = s[j];
s[j] = temp;
j--;
}
return;
}
};
541、反转字符串2
class Solution {
public:
string reverseStr(string s, int k) {
for (int i = 0; i < s.size(); i += (2 * k)) {
// 1. 每隔 2k 个字符的前 k 个字符进行反转
// 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
if (i + k <= s.size()) {
reverse(s.begin() + i, s.begin() + i + k );
} else {
// 3. 剩余字符少于 k 个,则将剩余字符全部反转。
reverse(s.begin() + i, s.end());
}
}
return s;
}
};
151、翻转字符串里的单词
双指针法
栈和队列
二叉树
回溯算法
回溯其实可以说是我们熟悉的DFS,本质上一种暴力枚举算法。回溯算法的唯一优化方法是剪枝。
回溯算法的模板
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
77、组合(中等)
class Solution {
public:
void backTracking(vector<vector<int>>& res,vector<int>& temp,int n,int k,int index){
//回溯
if(temp.size()==k) {
res.push_back(temp);
return;
}
for(int i=index;i<=n-(k-temp.size())+1;i++){ //剪枝优化
temp.push_back(i);
backTracking(res,temp,n,k,i+1); //注意:i+1而不是index+1
temp.pop_back();
}
}
vector<vector<int>> combine(int n, int k) {
//回溯的经典算法
vector<vector<int>> res;
vector<int> temp;
backTracking(res,temp,n,k,1);
return res;
}
};
216、组合总和3
class Solution {
public:
void backTracking(vector<vector<int>>& res,vector<int>& temp,int k,int n,int index){
//回溯
if(temp.size()==k && n==0){
res.push_back(temp);
return;
}
for(int i=index;i<=9;i++){
if(n<0) continue; //剪枝优化
temp.push_back(i);
n=n-i;
backTracking(res,temp,k,n,i+1);
temp.pop_back();
n=n+i;
}
}
vector<vector<int>> combinationSum3(int k, int n) {
/*回溯*/
vector<vector<int>> res;
vector<int> temp;
backTracking(res,temp,k,n,1);
return res;
}
};
17、电话号码的字母组合
class Solution {
public:
void backTracking(vector<string>& res,vector<string>& temp,string& ans,int n,int index){
//回溯
if(index==n) {
res.push_back(ans);
return;
}
for(int i=0;i<temp[index].size();i++){
ans.push_back(temp[index][i]);
backTracking(res,temp,ans,n,index+1);
ans.pop_back();
}
return;
}
vector<string> letterCombinations(string digits) {
/**/
vector<string> temp;
vector<string> res;
int n = digits.size();
if(n==0) return res;
for (char i : digits){
if(i=='2') temp.push_back("abc");
if(i=='3') temp.push_back("def");
if(i=='4') temp.push_back("ghi");
if(i=='5') temp.push_back("jkl");
if(i=='6') temp.push_back("mno");
if(i=='7') temp.push_back("pqrs");
if(i=='8') temp.push_back("tuv");
if(i=='9') temp.push_back("wxyz");
}
string ans;
backTracking(res,temp,ans,n,0);
return res;
}
};
39组合总和
class Solution {
public:
vector<vector<int>> res;
vector<int> temp;
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
/*回溯实现*/
backTracking(candidates,target,0);
return res;
}
void backTracking(vector<int>& candidates, int target, int index) {
/*回溯实现*/
if( target ==0 ){
res.push_back(temp);
return ;
}
if(target < 0) {
return ;
}
for(int i=index;i<candidates.size();i++){
if(candidates[i]>target) continue; //剪枝
temp.push_back(candidates[i]);
backTracking(candidates,target-candidates[i],i); //微调,这里是i而不是i+1,因为可以重复使用,保证本层使用了i之后下一层也可以使用i
temp.pop_back();
}
return ;
}
};
40、组合总和2(该题很好地讲解了去重的原理,多看)
class Solution {
public:
void backTracking(vector<vector<int>>& res,vector<int>& candidates,vector<int>& path,int target,int index,vector<bool>& used){
//回溯
if(candidates.size()==0 || target==0){
res.push_back(path);
return;
}
for(int i=index;i<candidates.size();i++){
if(i>0 && candidates[i]==candidates[i-1] && used[i-1]==false) continue; //细节,used[i-1]==false,如果为true,则为树枝上的去重
used[i] = true;
path.push_back(candidates[i]);
backTracking(res,candidates,path,target-candidates[i],i+1,used);
used[i] = false;
path.pop_back();
}
return;
}
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
/*典型回溯算法*/
vector<vector<int>> res;
vector<int> path;
vector<bool> used(candidates.size(),false);
sort(candidates.begin(),candidates.end());
backTracking(res,candidates,path,target,0,used);
return res;
}
};
131、分割回文串(多看,对于理解index和i的关系有好处)
class Solution {
public:
bool isOrNo(string s){
//该函数用于判断该子串是否是回文串
int n=s.size();
for(int i=0;i<n/2;i++){
if(s[i]!=s[n-1-i]) return false;
}
return true;
}
void backTracking(vector<vector<string>>& res,string& s,vector<string>& path,int index){
/*回溯*/
if( index ==s.size()){
res.push_back(path);
return;
}
for(int i=index;i<s.size();i++){
string s1;
for(int j=index;j<=i;j++){ //想清楚为什么是从index到i
s1.push_back(s[j]);
}
if(isOrNo(s1)){
path.push_back(s1);
backTracking(res,s,path,i+1);
path.pop_back();
}else{
continue;
}
}
return;
}
vector<vector<string>> partition(string s) {
/*回溯算法,分割*/
vector<vector<string>> res;
vector<string> path;
backTracking(res,s,path,0);
return res;
}
};
93、复原IP地址
class Solution {
public:
int cnt=0;
bool isIP(const string path){
//该函数用于判断字符是否为0-255之间
int n=path.size();
if(n>3) return false;
if(path[0]=='0' && n!=1) return false;
if(n==3){
int val = 0;
for(int i=0;i<3;i++){
val=val*10+path[i]-'0';
}
if(val>255) return false;
else return true;
}
return true;
}
void backTracking(vector<string>& res,const string s,string& path,int index){
//回溯
if(cnt==4 && index==s.size()){
res.push_back(path);
return;
}
for(int i=index;i<s.size();i++){
string temp;
if(cnt<=2){
for(int j=index;j<=i;j++){
temp.push_back(s[j]);
}
}else{
for(int j=index;j<s.size();j++){
temp.push_back(s[j]);
}
}
if(isIP(temp)){
int sz=path.size();
if(cnt==0) path+=temp;
else{
path=path+'.'+temp;
}
cnt++;
backTracking(res,s,path,i+1);
path.resize(sz);
cnt--;
}else{
break;
}
}
return;
}
vector<string> restoreIpAddresses(string s) {
/*分割字符串问题
*/
vector<string> res;
string path;
backTracking(res,s,path,0);
return res;
}
};
78、子集
class Solution {
public:
void backTracking(vector<vector<int>>& res,vector<int>& path,const vector<int> nums,int index){
//回溯
res.push_back(path);
if(index==nums.size()){
return;
}
for(int i=index;i<nums.size();i++){
path.push_back(nums[i]);
backTracking(res,path,nums,i+1);
path.pop_back();
}
return;
}
vector<vector<int>> subsets(vector<int>& nums) {
sort(nums.begin(),nums.end());
vector<vector<int>> res;
vector<int> path;
backTracking(res,path,nums,0);
return res;
}
};
贪心算法
贪心的本质是选择每一阶段的局部最优,从而达到全局最优。
贪心算法一般分为如下四步:
将问题分解为若干个子问题
找出适合的贪心策略
求解每一个子问题的最优解
将局部最优解堆叠成全局最优解
455、分发饼干
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
/*将胃口值g进行排序,优先满足小胃口的*/
sort(g.begin(),g.end());
sort(s.begin(),s.end());
int n=g.size(),m=s.size();
int cnt =0;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(g[i]<=s[j]){
cnt++;
s[j]=0;
break;
}
}
}
return cnt;
}
};
376、摆动序列
动态规划
动规五部曲:
确定dp数组(dp table)以及下标的含义
确定递推公式
dp数组如何初始化
确定遍历顺序
举例推导dp数组
509、斐波那契数
class Solution {
public:
int fib(int n) {
/*动态规划问题*/
if(n==0 ) return 0;
if(n==2|| n==1) return 1;
vector<int> DP;
DP.push_back(0);
DP.push_back(1);
int i=2;
for(i;i<=n;i++){
DP.push_back(DP[i-2]+DP[i-1]);
}
return DP[n];
}
};
70、爬楼梯
class Solution {
public:
int climbStairs(int n) {
/*动态规划
递推公式f(n)=f(n-1)+f(n-2)
DP数组表示爬i阶有DP[i]种方法
初始化DP(0)=1,DP(1)=1,DP(2)=2
从前向后遍历*/
vector<int> DP(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];
}
};
可以优化DP数组,只存储两个值即可
class Solution {
public:
int climbStairs(int n) {
/*动态规划
递推公式f(n)=f(n-1)+f(n-2)
DP数组表示爬i阶有DP[i]种方法
初始化DP(0)=1,DP(1)=1,DP(2)=2
从前向后遍历*/
vector<int> DP(2);
DP[0]=1;DP[1]=1;
for(int i=2;i<=n;i++){
int temp = DP[0]+DP[1];
DP[0] = DP[1];
DP[1] = temp;
}
return DP[1];
}
};
746、使用最小花费爬楼梯
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
/*动态规划
DP[i]表示当前在第i个台阶,下一步累积的最低花费
初始化:DP[0]=0,DP[1]=0
递推公式:f(n)=min(f(n-1)+cost(n-1),f(n-2)+cost(n-2))
*/
int n=cost.size();
if(n==0) return 0;
vector<int> DP(n+1);
DP[0]=0;DP[1]=0;
for(int i=2;i<=n;i++){
DP[i] = min(DP[i-1]+cost[i-1],DP[i-2]+cost[i-2]);
}
return DP[n];
}
};
62、不同路径
class Solution {
public:
int uniquePaths(int m, int n) {
/*动态规划
DP[i][j]表示走到第i,j位置总共有的路径条数
初始化DP[0][j]=1,DP[i][0]=1;
递推公式:DP[i][j]=DP[i-1][j]+DP[i][j-1],注意边界条件
遍历顺序:*/
vector<vector<int>> A;
for(int i=0;i<m;i++){
vector<int> temp(n,1);
A.push_back(temp);
}
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
A[i][j]=A[i-1][j]+A[i][j-1];
}
}
return A[m-1][n-1];
}
};
63、不同路径2
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
/*动态规划:
DP[i][j]代表走到第i,j位置需要的路径数,如果有障碍则置为0
DP[i][0]=1,DP[0][j]=1,如果边界上有障碍,则后续为0
递推关系:分四种情况,没障碍,障碍在上面,障碍在左边,两边都有障碍
遍历顺序:*/
vector<vector<int>> DP;
int m = obstacleGrid.size();
int n = obstacleGrid[0].size();
//DP初始化
for(int i=0;i<m;i++){
vector<int> temp(n,0);
DP.push_back(temp);
}
for(int i=0;i<n;i++){
if(obstacleGrid[0][i]==1) break;
DP[0][i]=1;
}
for(int i=0;i<m;i++){
if(obstacleGrid[i][0]==1) break;
DP[i][0]=1;
}
//遍历DP数组
for(int j=1;j<n;j++){
for(int i=1;i<m;i++){
if(obstacleGrid[i-1][j]!=1 && obstacleGrid[i][j-1]!=1) {
DP[i][j]=DP[i-1][j]+DP[i][j-1];
}
else if(obstacleGrid[i-1][j]==1 && obstacleGrid[i][j-1]!=1){
DP[i][j]=DP[i][j-1];
}
else if(obstacleGrid[i][j-1]==1 && obstacleGrid[i-1][j]!=1){
DP[i][j]=DP[i-1][j];
}
else{
DP[i][j]=0;
}
}
}
if(obstacleGrid[m-1][n-1]==1) return 0;
else return DP[m-1][n-1];
}
};
343、整数拆分
该题难在递推关系,怎么推导。
class Solution {
public:
int integerBreak(int n) {
/*DP[i]表示拆分i,返回的乘积最大值
初始值:DP[1]=1,DP[2]=1,DP[3] =2
递推关系:DP[i] = max(j*DP[i-j],j*(i-j)),j从1到i
遍历顺序:依次*/
vector<int> DP(n+1,1);
DP[1]=1; DP[2]=1;
for(int i=3;i<n+1;i++){
for(int j=1;j<i;j++){
int temp = DP[i];
DP[i] = max(j*(i-j),j*DP[i-j]);
DP[i] = max(DP[i],temp);
}
}
return DP[n];
}
};
96、不同的二叉搜索树
关键在于递推公式为:
D
P
[
i
]
=
∑
j
=
0
i
−
1
D
P
[
j
]
D
P
[
i
−
j
−
1
]
DP[i]=\sum_{j=0}^{i-1} DP[j]DP[i-j-1]
DP[i]=j=0∑i−1DP[j]DP[i−j−1]
class Solution {
public:
int numTrees(int n) {
/*动态规划,难点在于递推公式*/
vector<int> DP(n+1,0);
DP[0]=1;DP[1]=1;
for(int i=2;i<=n;i++){
for(int j=0;j<i;j++){
DP[i] += DP[j]*DP[i-j-1];
}
}
return DP[n];
}
};
动规:背包问题
有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
01背包问题
每个物品只有一个,求背包能容纳的最大价值。(看代码随想录讲解,很详细)
两个关注点:1、递推公式的推导,2、两个for循环遍历的顺序(如果求组合数,外层for循环遍历物品,内层for遍历背包,如果求排列数,外层for遍历背包,内层for遍历物品,01背包内层for循环逆序遍历,完全背包内层for循环顺序遍历)。
注意:怎么理解遍历背包时,逆序是01背包,顺序是完全背包。因为,DP数组本身每一项DP[i]都是依赖于DP[0~i-1]前i-1项推导出来的,如果顺序则会出现套娃的情况,实现的效果为每个物品任取背包容量大小次。而逆序,先更新第i+1,后更新第i项,更新结果取决于初始值,则每个物品只取了一次。
416、分割等和子集
class Solution {
public:
bool canPartition(vector<int>& nums) {
/*每个数取一次,看成动态规划里的01背包问题,本题物品价值等于物品大小
DP[j]表示,背包大小为j的背包,所能装载的最大价值
初始化:DP[0]=0,DP[j]=0
递推公式:DP[j]=max{DP[j],DP[j-nums[i]]+nums[i]}
遍历顺序:先物品i,再背包大小j(逆序)
打印DP数组*/
int sum=0;
int n=nums.size();
for(int i=0;i<n;i++){
sum+=nums[i];
}
int target = sum/2;
if(sum%2==1) return false;
vector<int> DP(target+1,0);
for(int i=0;i<n;i++){
for(int j=target;j>=nums[i];j--){
DP[j] = max(DP[j],DP[j-nums[i]]+nums[i]);
}
}
if(DP[target]==target) return true;
return false;
}
};
1049、最后一块石头的重量2
class Solution {
public:
int lastStoneWeightII(vector<int>& stones) {
/*分解为,将石头分为大小和几乎相等的两堆,
相撞后差的最小值,如果两堆大小相等则返回0*/
int n=stones.size();
int sum=0;
for(int i=0;i<n;i++){
sum+=stones[i];
}
int target = sum/2;
vector<int> DP(target+1,0);
for(int i=0;i<n;i++){
for(int j=target;j>=stones[i];j--){
DP[j] = max(DP[j],DP[j-stones[i]]+stones[i]);
}
}
return abs(sum-2*DP[target]);
}
};
494、目标和
DP[j]表示装满大小为j的背包有DP[j]中方法。
递推公式:
D
P
[
j
]
=
∑
i
=
0
n
−
1
D
P
[
j
−
n
u
m
s
[
i
]
]
DP[j]=\sum_{i=0}^{n-1} DP[j-nums[i]]
DP[j]=i=0∑n−1DP[j−nums[i]]
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
/*动规五部曲*/
int sum=0;
for(int i:nums) sum+=i;
if(abs(target)>sum) return 0;
if((target+sum)%2==1) return 0;
int left = (target+sum)/2;
vector<int> DP(left+1,0);
DP[0] =1;
for(int i=0;i<nums.size();i++){
for(int j=left;j>=nums[i];j--){
DP[j]+=DP[j-nums[i]];
}
}
return DP[left];
}
};
474、一和零
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
vector<vector<int>> dp(m + 1, vector<int> (n + 1, 0)); // 默认初始化0
for (string str : strs) { // 遍历物品
int oneNum = 0, zeroNum = 0;
for (char c : str) {
if (c == '0') zeroNum++;
else oneNum++;
}
for (int i = m; i >= zeroNum; i--) { // 遍历背包容量且从后向前遍历!
for (int j = n; j >= oneNum; j--) {
dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
}
}
}
return dp[m][n];
}
};
完全背包问题
每个物品可以使用无数次
518、零钱兑换2
递推公式:
D
P
[
j
]
=
∑
i
=
0
n
−
1
D
P
[
j
−
c
o
i
n
[
i
]
]
DP[j]=\sum_{i=0}^{n-1} DP[j-coin[i]]
DP[j]=i=0∑n−1DP[j−coin[i]]
class Solution {
public:
int change(int amount, vector<int>& coins) {
/*动规五部曲
dp[j] 就是所有的dp[j - coins[i]](考虑coins[i]的情况)相加。*/
int n=coins.size();
vector<int> DP(amount+1,0);
DP[0]=1;
for(int i=0;i<n;i++){
for(int j=coins[i];j<=amount;j++){
DP[j]+=DP[j-coins[i]];
}
}
return DP[amount];
}
};
377、组合总和4
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
/**/
int n=nums.size();
vector<int> DP(target+1,0);
DP[0]=1;
for (int i = 0; i <= target; i++) { // 遍历背包
for (int j = 0; j < n; j++) { // 遍历物品
if (i - nums[j] >= 0 && DP[i] < INT_MAX - DP[i - nums[j]]) {
DP[i] += DP[i - nums[j]];
}
}
}
return DP[target];
}
};
322、零钱兑换
递推公式:
D
P
[
j
]
=
m
i
n
i
=
0
n
−
1
(
D
P
[
j
]
,
D
P
[
j
−
c
o
i
n
s
[
i
]
]
+
1
)
DP[j]=min_{i=0}^{n-1}(DP[j],DP[j-coins[i]]+1)
DP[j]=mini=0n−1(DP[j],DP[j−coins[i]]+1)
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
/*动规五部曲*/
int n=coins.size();
vector<int> DP(amount+1,INT_MAX-1);
DP[0]=0;
for(int i=0;i<n;i++){
for(int j=coins[i];j<=amount;j++){
DP[j]=min(DP[j],DP[j-coins[i]]+1);
}
}
if(DP[amount]==INT_MAX-1) return -1;
else return DP[amount];
}
};
279、完全平方数
递推公式:
D
P
[
j
]
=
m
i
n
i
=
0
n
u
m
s
.
s
i
z
e
(
)
−
1
(
D
P
[
j
]
,
D
P
[
j
−
n
u
m
s
[
i
]
]
+
1
)
DP[j]=min_{i=0}^{nums.size()-1}(DP[j],DP[j-nums[i]]+1)
DP[j]=mini=0nums.size()−1(DP[j],DP[j−nums[i]]+1)
遍历顺序:完全背包问题,且求组合,先物品,后背包,背包顺序遍历。
class Solution {
public:
int numSquares(int n) {
/*递归五部曲*/
vector<int> nums;
for(int i=1;i*i<=n;i++){
nums.push_back(i*i);
}
vector<int> DP(n+1,INT_MAX);
DP[0]=0;DP[1]=1;
for(int i=0;i<nums.size();i++){
for(int j=nums[i];j<=n;j++){
DP[j]=min(DP[j],DP[j-nums[i]]+1);
}
}
return DP[n];
}
};
139、单词拆分
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> wordSet(wordDict.begin(),wordDict.end());
int n=s.size();
vector<bool> DP(n+1,false);
DP[0] = true;
for(int j=1;j<=n;j++){ //遍历背包
for(int i=0;i<j;i++){ //遍历物品
string temp = s.substr(i,j-i);
if(wordSet.find(temp)!=wordSet.end() && DP[i])
DP[j] = true;
}
}
return DP[n];
}
};
动规:打家劫舍系列
198、打家劫舍
DP[j] 表示j个房间能偷到的最高金额
递推公式:
D
P
[
j
]
=
m
a
x
(
D
P
[
j
−
1
]
,
D
P
[
j
−
2
]
+
n
u
m
s
[
j
−
1
]
)
DP[j] = max(DP[j-1],DP[j-2]+nums[j-1])
DP[j]=max(DP[j−1],DP[j−2]+nums[j−1])
初始化:DP[0]=0,DP[1]=nums[0]
遍历顺序:顺序
打印DP数组
class Solution {
public:
int rob(vector<int>& nums) {
/*动规五部曲*/
int n=nums.size();
if(n==1) return nums[0];
vector<int> DP(n+1,0);
DP[1]=nums[0];
for(int i=2;i<=n;i++){
DP[i]=max(DP[i-1],DP[i-2]+nums[i-1]);
}
return DP[n];
}
};
213、打家劫舍2
内容同1,将1进行拆分成两个数组。
class Solution {
public:
int rob_old(vector<int>& nums) {
/*动规五部曲*/
int n=nums.size();
if(n==1) return nums[0];
vector<int> DP(n+1,0);
DP[1]=nums[0];
for(int i=2;i<=n;i++){
DP[i]=max(DP[i-1],DP[i-2]+nums[i-1]);
}
return DP[n];
}
int rob(vector<int>& nums) {
/*将打家劫舍1分装成函数,将该问题拆分为两个子数组,分别传入函数即可*/
int n = nums.size();
if(n==1) return nums[0];
vector<int> nums_pre,nums_sub;
for(int i=0;i<n;i++){
if(i==0){
nums_pre.push_back(nums[i]);
}else if(i==n-1){
nums_sub.push_back(nums[i]);
}else{
nums_sub.push_back(nums[i]);
nums_pre.push_back(nums[i]);
}
}
return max(rob_old(nums_pre),rob_old(nums_sub));
}
};
337、打家劫舍3(没怎么看懂)
动态规划,使用01DP数组
class Solution {
public:
int rob(TreeNode* root) {
vector<int> result = robTree(root);
return max(result[0], result[1]);
}
// 长度为2的数组,0:不偷,1:偷
vector<int> robTree(TreeNode* cur) {
if (cur == nullptr) return vector<int>{0, 0};
vector<int> left = robTree(cur->left);
vector<int> right = robTree(cur->right);
// 偷cur,那么就不能偷左右节点。
int val1 = cur->val + left[0] + right[0];
// 不偷cur,那么可以偷也可以不偷左右节点,则取较大的情况
int val2 = max(left[0], left[1]) + max(right[0], right[1]);
return {val2, val1};
}
};
动规:股票系列
121、买卖股票的最佳时机
DP[i]表示第i天卖出股票所能获得的最大利润为DP[i]
递推公式:
D
P
[
i
]
=
p
r
i
c
e
s
[
i
]
−
m
i
n
.
p
r
e
DP[i]=prices[i]-min.pre
DP[i]=prices[i]−min.pre其中min.pre表示第i天及之前所出现的最低价格
class Solution {
public:
int maxProfit(vector<int>& prices) {
/*动态规划*/
int min_pre=prices[0];
int n = prices.size();
vector<int> DP(n+1,0);
int max_val=0;
for(int i=1;i<n;i++){
min_pre = min(min_pre,prices[i]);
DP[i] = prices[i]-min_pre;
max_val = max(DP[i],max_val);
}
return max_val;
}
};
122、买卖股票的最佳时机2
没使用动态规划,直接做差求和。代码如下
class Solution {
public:
int maxProfit(vector<int>& prices) {
/*遍历一遍prices如果前项小于后项,则做差,否则为0*/
int n=prices.size();
int sum=0;
for(int i=1;i<n;i++){
int k = prices[i]-prices[i-1];
if(k>0) sum+=k;
}
return sum;
}
};
动规:子序列系列
300、最长递归子序列
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
/*难点在于DP数组的定义,以及递推公式*/
int n=nums.size();
vector<int> DP(n,1);
int max_val=1;
for(int i=1;i<n;i++){
for(int j=0;j<i;j++){
if(nums[i]>nums[j]) DP[i] = max(DP[j]+1,DP[i]);
}
max_val = max(max_val,DP[i]);
}
return max_val;
}
};
674、最长连续递增序列
class Solution {
public:
int findLengthOfLCIS(vector<int>& nums) {
int n=nums.size();
vector<int> DP(n,1);
int max_val = 1;
for(int i=1;i<n;i++){
if(nums[i]>nums[i-1]) DP[i]=DP[i-1]+1;
max_val=max(max_val,DP[i]);
}
return max_val;
}
};
718、最长重复(连续)子数组
class Solution {
public:
int findLength(vector<int>& nums1, vector<int>& nums2) {
/*本题难点在于DP数组的定义
DP[i][j]定义为以i-1为结尾的nums1,和以j-1为结尾的nums2,最长重复子数组的长度
递推公式:DP[i][j]=DP[i-1][j-1]+1,if(nums1[i]==nums2[j])*/
int m=nums1.size();
int n=nums2.size();
vector<vector<int>> DP(m+1,vector<int> (n+1,0));
int max_val=0;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(nums1[i-1]==nums2[j-1]) DP[i][j]=DP[i-1][j-1]+1;
max_val=max(max_val,DP[i][j]);
}
}
return max_val;
}
};
1143、最长公共子序列(不连续)
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
/*子序列,不连续*/
int m=text1.size();
int n=text2.size();
vector<vector<int>> DP(m+1,vector<int> (n+1,0));
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(text1[i-1]==text2[j-1]) DP[i][j] = DP[i-1][j-1]+1;
else DP[i][j] = max(DP[i][j-1],DP[i-1][j]);
}
}
return DP[m][n];
}
};
1035、不相交的线
class Solution {
public:
int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {
/*动态规划
DP[i][j]表示以i-1为结尾的nums1和以j-1为结尾的nums2的最大连线数
DP[i][j]=DP[i-1][j-1] if(nums1[i-1]==nums2[j-1])
=max(DP[i-1][j],DP[i][j-1]) else
初始化:全部为0
遍历顺序
打印DP数组*/
int n=nums1.size();
int m=nums2.size();
vector<vector<int>> DP(n+1,vector<int> (m+1,0));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(nums1[i-1]==nums2[j-1]) DP[i][j]=DP[i-1][j-1]+1;
else DP[i][j] = max(DP[i-1][j],DP[i][j-1]);
}
}
return DP[n][m];
}
};
53、最大子数组和
如果使用双指针,会超时。建议动态规划
class Solution {
public:
int maxSubArray(vector<int>& nums) {
/*双指针*/
int n=nums.size();
if(n==0) return 0;
int sum=0;
int max_val=nums[0];
for(int left=0;left<n;left++){
for(int right=left;right<n;right++){
int temp_val=0;
for(int temp=left;temp<=right;temp++){
temp_val+=nums[temp];
}
if(temp_val<=0) {
max_val=max(max_val,nums[right]);
left=right+1;
continue;
}
max_val=max(max_val,temp_val);
}
}
return max_val;
}
};
动规:
DP[i]表示以i为结尾的数组nums中最大的子数组和为DP[i]
递推公式
D
P
[
i
]
=
m
a
x
(
D
P
[
i
−
1
]
+
n
u
m
s
[
i
]
,
n
u
m
s
[
i
]
)
DP[i]=max(DP[i-1]+nums[i],nums[i])
DP[i]=max(DP[i−1]+nums[i],nums[i])
class Solution {
public:
int maxSubArray(vector<int>& nums) {
/*难点在于递推公式即DP数组的定义*/
int n=nums.size();
if(n==0) return 0;
vector<int> DP(n,0);
DP[0] = nums[0];
int max_val=DP[0];
for(int i=1;i<n;i++){
DP[i] = max(DP[i-1]+nums[i],nums[i]);
max_val = max(DP[i],max_val);
}
return max_val;
}
};
392、判断子序列
class Solution {
public:
bool isSubsequence(string s, string t) {
//双指针
int s_size = s.size();
int t_size = t.size();
int i=0;
for(int j=0;j<t_size;j++){
if(t[j]==s[i]) i++;
}
if(i==s_size) return true;
else return false;
}
};
115、不同的子序列
uint64_t,注意写法
class Solution {
public:
int numDistinct(string s, string t) {
/*难点在于DP数组的定义,以及递推公式,初始化*/
int n=s.size();
int m=t.size();
vector<vector<uint64_t>> DP(n+1,vector<uint64_t> (m+1,0));
for(int i=0;i<=n;i++) DP[i][0] = 1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(s[i-1]==t[j-1]) DP[i][j] = DP[i-1][j-1]+DP[i-1][j];
else DP[i][j] = DP[i-1][j];
}
}
return DP[n][m];
}
};
583、两个字符串的删除操作
记录一下,在没看任何提示的情况下,独立做出来了
DP[i][j]表示以i-1为结尾的word1和以j-1为结尾的word2相同所需的最小删除步数。
递推公式:
D
P
[
i
]
[
j
]
=
{
D
P
[
i
−
1
]
[
j
−
1
]
,
i
f
(
w
o
r
d
[
i
−
1
]
=
=
w
o
r
d
2
[
j
−
1
]
)
m
i
n
(
D
P
[
i
−
1
]
[
j
]
+
1
,
D
P
[
i
]
[
j
−
1
]
+
1
,
D
P
[
i
−
1
]
D
P
[
j
−
1
]
+
2
)
,
e
l
s
e
}
DP[i][j]= \left\{_{DP[i-1][j-1],if(word[i-1]==word2[j-1])}^{min(DP[i-1][j]+1,DP[i][j-1]+1,DP[i-1]DP[j-1]+2),else}\right\}
DP[i][j]={DP[i−1][j−1],if(word[i−1]==word2[j−1])min(DP[i−1][j]+1,DP[i][j−1]+1,DP[i−1]DP[j−1]+2),else}
递推公式可以简化一点点。
初始化:本题有讲究,需要初始化为0-i或者0-j
遍历顺序:顺序
打印DP数组
class Solution {
public:
int minDistance(string word1, string word2) {
/*动态规划的思想*/
int n = word1.size();
int m= word2.size();
vector<vector<int>> DP(n+1,vector<int> (m+1,0));
for(int j=1;j<=m;j++){
DP[0][j] = j;
}
for(int i=1;i<=n;i++){
DP[i][0] = i;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(word1[i-1]==word2[j-1]) DP[i][j] = DP[i-1][j-1];
else {
DP[i][j] = min(DP[i-1][j],DP[i][j-1])+1;
}
}
}
return DP[n][m];
}
};
72、编辑距离
此题也独立AC了。
class Solution {
public:
int minDistance(string word1, string word2) {
/*递推公式花了很长时间才推导出来,也是在没看提示的前提下完成了推导*/
int m=word1.size();
int n=word2.size();
vector<vector<int>> DP(m+1,vector<int>(n+1,0));
for(int j=1;j<=n;j++) DP[0][j] = j;
for(int i=1;i<=m;i++) DP[i][0] = i;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(word1[i-1]==word2[j-1]) DP[i][j] = DP[i-1][j-1];
else {
int temp_val = min(DP[i][j-1],DP[i-1][j])+1;
DP[i][j] = min(temp_val,DP[i-1][j-1]+1);
}
}
}
return DP[m][n];
}
};
647、回文子串
暴力搜索的思路如下:不推荐,该方式写成双指针性能最优。
class Solution {
public:
bool isOrNot(const string s,int left,int right){
//该函数输入字符串s,和子串的起始位置和终止位置,输出该子串是否为回文子串
while(left<=right){
if(s[left]!=s[right]) return false;
left++;
right--;
}
return true;
}
int countSubstrings(string s) {
/*暴力遍历*/
int n=s.size();
int cnt = 0;
for(int left=0;left<n;left++){
for(int right=left;right<n;right++){
if(isOrNot(s,left,right)) cnt++;
}
}
return cnt;
}
};
动态规划的思想:
class Solution {
public:
int countSubstrings(string s) {
/*动态规划的思路*/
int n = s.size();
vector<vector<bool>> DP(n,vector<bool> (n,false));
int cnt = 0;
for(int i=n-1;i>=0;i--){
for(int j=i;j<n;j++){
if(s[i]==s[j]) {
if(j-i<=1) {
DP[i][j] = true;
cnt++;
}
else if(DP[i+1][j-1]){
DP[i][j] = DP[i+1][j-1];
cnt++;
}
}
}
}
return cnt;
}
};
516、最长回文子序列
暴力搜索,回溯算法版本:
class Solution {
public:
bool isOrNot(string s){
//该函数用于判断s是否是回文串
int n = s.size();
for(int i=0;i<n/2;i++){
if(s[i]!=s[n-1-i]) return false;
}
return true;
}
int max_val = 0;
void backTracking(string s,int index){
int n = s.size();
if(n==0) return ;
if(isOrNot(s)){
int temp = s.size();
max_val = std::max(max_val,temp);
return;
}
for(int i=index;i<n;i++){
char keep = s[i];
s.erase(i,1);
backTracking(s,index++);
if(i<=s.size()) s.insert(s.begin()+i,keep);
}
return;
}
int longestPalindromeSubseq(string s) {
/*回溯算法*/
backTracking(s,0);
return max_val;
}
};
动态规划:
DP数组 表示s中[i,j]范围内最长回文子序列的长度
递推公式:
D
P
[
i
]
[
j
]
=
{
m
a
x
(
D
P
[
i
]
[
j
−
1
]
,
D
P
[
i
+
1
]
[
j
]
)
,
e
l
s
e
D
P
[
i
+
1
]
[
j
−
1
]
+
2
,
i
f
(
s
[
i
]
=
=
s
[
j
]
)
}
DP[i][j]=\left\{_{max(DP[i][j-1],DP[i+1][j]),else}^{DP[i+1][j-1]+2,if (s[i]==s[j])} \right\}
DP[i][j]={max(DP[i][j−1],DP[i+1][j]),elseDP[i+1][j−1]+2,if(s[i]==s[j])}
初始化:按照定义当i>j时无意义,初始化为0,对角元素初始化为1
遍历顺序:根据递推公式,从左下向右上遍历。
打印DP数组。
class Solution {
public:
int longestPalindromeSubseq(string s) {
/*难点在于DP数组定义及递推公式*/
int n = s.size();
//初始化
vector<vector<int>> DP(n,vector<int> (n,0));
for(int i=0;i<n;i++){
DP[i][i] = 1;
}
//递推公式
for(int i=n-2;i>=0;i--){
for(int j=i+1;j<n;j++){
if(s[i]==s[j]) DP[i][j] = DP[i+1][j-1] + 2;
else DP[i][j] = max(DP[i][j-1],DP[i+1][j]);
}
}
return DP[0][n-1];
}
};