力扣刷题记录

目录

该文章刷题顺序按照代码随想录刷,只记录简单算法思路和源码

数组

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=0i1DP[j]DP[ij1]

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=0n1DP[jnums[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=0n1DP[jcoin[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=0n1(DP[j],DP[jcoins[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[jnums[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[j1],DP[j2]+nums[j1])
初始化: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[i1]+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[i1][j1],if(word[i1]==word2[j1])min(DP[i1][j]+1,DP[i][j1]+1,DP[i1]DP[j1]+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][j1],DP[i+1][j]),elseDP[i+1][j1]+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];
    }
};

单调栈

图论

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于力扣刷题C++常用操作,我可以给你一些常见的操作和技巧: 1. 使用 STL 容器和算法库:STL(Standard Template Library)是 C++ 标准库中的一个重要组成部分,包含了许多常用的容器和算法。在力扣刷题中,使用 STL 可以大大提高代码的效率和可读性。例如,vector 可以用来存储动态数组,sort 可以用来排序等等。 2. 使用 auto 关键字:auto 关键字可以自动推导变量类型,可以减少代码量和提高可读性。例如,auto x = 1; 可以自动推导出 x 的类型为 int。 3. 使用 lambda 表达式:lambda 表达式是 C++11 中引入的一种匿名函数,可以方便地定义一些简单的函数对象。在力扣刷题中,使用 lambda 表达式可以简化代码,例如在 sort 函数中自定义比较函数。 4. 使用位运算:位运算是一种高效的运算方式,在力扣刷题中经常会用到。例如,左移运算符 << 可以用来计算 2 的幂次方,右移运算符 >> 可以用来除以 2 等等。 5. 使用递归:递归是一种常见的算法思想,在力扣刷题中也经常会用到。例如,二叉树的遍历、链表的反转等等。 6. 使用 STL 中的 priority_queue:priority_queue 是 STL 中的一个容器,可以用来实现堆。在力扣刷题中,使用 priority_queue 可以方便地实现一些需要维护最大值或最小值的算法。 7. 使用 STL 中的 unordered_map:unordered_map 是 STL 中的一个容器,可以用来实现哈希表。在力扣刷题中,使用 unordered_map 可以方便地实现一些需要快速查找和插入的算法。 8. 使用 STL 中的 string:string 是 STL 中的一个容器,可以用来存储字符串。在力扣刷题中,使用 string 可以方便地处理字符串相关的问题。 9. 注意边界条件:在力扣刷题中,边界条件往往是解决问题的关键。需要仔细分析题目,考虑各种边界情况,避免出现错误。 10. 注意时间复杂度:在力扣刷题中,时间复杂度往往是评判代码优劣的重要指标。需要仔细分析算法的时间复杂度,并尽可能优化代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值