leetcode动态规划(python与c++)

1 . 斐波那契数  

class Solution:
    def fib(self, n: int) -> int:
        # if n==0:
        #     return 0
        # elif n==1:
        #     return 1
        # else:
        #     return self.fib(n-1)+self.fib(n-2)
        a =0
        b =1
        for i in range(n):
            a,b = b,a+b
        return a
class Solution {
public:
    int fib(int n) {
        int a = 0, b = 1;
        for(int i = 0; i < n; i++){
            int temp = a;
            a = a + b;
            b = temp;
        }
        return a;
    }
};

2. 第 N 个泰波那契数

class Solution:
    def tribonacci(self, n: int) -> int:
        if n == 0:
            return 0
        if n <= 2:
            return 1
        dp = [0]*(n+1)
        dp[1] = 1
        dp[2] = 1
        for i in range(3, n+1):
            dp[i] = dp[i-3] + dp[i-2] + dp[i-1]
        return dp[-1]
class Solution {
public:
    int tribonacci(int n) {
        if(n == 0){
            return 0;
        }
        if(n <= 2){
            return 1;
        }
        vector<int> dp(n + 1, 0);
        dp[1] = 1;
        dp[2] = 1;
        for(int i = 3; i < n+1; i++){
            dp[i] = dp[i-3] + dp[i-2] + dp[i-1];
        }
        return dp[n];
    }
};

3. 爬楼梯

class Solution:
    def climbStairs(self, n: int) -> int:
        if n < 3:
            return n
        a = 1
        b = 2
        for i in range(3, n + 1):
            a, b = b, a + b
        return b
class Solution {
public:
    int climbStairs(int n) {
        if(n < 3){
            return n;
        }
        int a = 1, b = 2;
        for(int i = 3; i < n + 1; i++){
            int temp = b;
            b = a + b;
            a = temp;
        }
        return b;
    }
};

 4. 使用最小花费爬楼梯

class Solution:
    def minCostClimbingStairs(self, cost: List[int]) -> int:
        dp = [0]*len(cost)
        dp[0],dp[1] =cost[0],cost[1]
        for i in range(2,len(cost)):
            dp[i] = min(dp[i-1],dp[i-2])+cost[i]
        return min(dp[-1],dp[-2])
class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int n = cost.size();
        vector<int> dp(n, 0);
        dp[0] = cost[0];
        dp[1] = cost[1];
        for(int i = 2; i < n; i++){
            dp[i] = min(dp[i-1], dp[i-2]) + cost[i];
        }
        return min(dp[n-2], dp[n-1]);
    }
};

 5. 打家劫舍

class Solution:
    def rob(self, nums: List[int]) -> int:
        n = len(nums)
        if n < 2:
            return max(nums)
        dp = [0] * n
        dp[0] = nums[0]
        dp[1] = max(nums[1],nums[0])
        for i in range(2, n):
            dp[i] = max(dp[i-2]+nums[i], dp[i-1])
        return dp[-1]
class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        if(n == 0){
            return 0;
        }
        if(n == 1){
            return nums[0];
        }
        if(n == 2){
            return max(nums[0],nums[1]);
        }
        vector<int> dp(n, 0);
        dp[0] = nums[0];
        dp[1] = max(nums[0], nums[1]);
        for(int i = 2; i < n; i++){
            dp[i] = max(dp[i-2]+nums[i], dp[i-1]);
        }
        return dp[n-1];
    }
};

6. 打家劫舍 II

class Solution:
    def rob(self, nums: List[int]) -> int:
        n = len(nums)
        if n < 2:
            return max(nums)
        dp1 = [0]*n
        dp2 = [0]*n
        dp1[0] = 0
        dp1[1] = nums[1]

        dp2[0] = nums[0]
        dp2[1] = max(nums[0], nums[1])
        for i in range(2, n):
            dp1[i] = max(dp1[i-2]+nums[i], dp1[i-1])
        for i in range(2, n-1):
            dp2[i] = max(dp2[i-2]+nums[i], dp2[i-1])
        return max(dp1[-1], dp2[-2])

7. 删除并获得点数

class Solution:
    def deleteAndEarn(self, nums: List[int]) -> int:
        maxVal = max(nums)
        total = [0] * (maxVal + 1)
        for val in nums:
            total[val] += val
        # print(total)
        n = len(total)
        dp = [0]*n
        if n <=2:
            return max(total)
        dp[0] = total[0]
        dp[1] = max(total[0], total[1])
        for i in range(2, n):
            dp[i] = max(dp[i-1], dp[i-2] + total[i])
        return dp[-1]

 8. 跳跃游戏

class Solution:
    def canJump(self, nums: List[int]) -> bool:
        #贪心算法
        most_dis = 0
        for i in range(len(nums)):
            if i <= most_dis:
                most_dis = max(most_dis, nums[i] + i)
                if most_dis >= len(nums) - 1:
                    return True
        return False
class Solution {
public:
    bool canJump(vector<int>& nums) {
        int most_length = 0;
        for(int i = 0; i < nums.size(); i++){
            if(i <= most_length){
                most_length = max(nums[i] + i, most_length);
            }
            if(most_length >= nums.size() - 1){
                return true;
            }
        }
        return false;

        int n = nums.size();
        vector<bool> opt(n, false);
        opt[0] = true;
        for(int i = 1; i < n; i++){
            opt[i] = opt[i - 1] && nums[i-1] >= 1;
            nums[i] = max(nums[i - 1] - 1, nums[i]);
        }
        return opt[n-1];
    }
};

9. 跳跃游戏 II

class Solution {
public:
    int jump(vector<int>& nums) {
        int res = 0;
        int start = 0;
        int end = 1;
        int maxPos = 0;
        while(end < nums.size()){
            for(int i = start; i < end; i++){
                //能跳到的最远距离
                maxPos = max(maxPos, nums[i] + i);
            }
            start = end;//下一次起跳点范围开始的格子
            end = maxPos + 1;//下一次起跳点范围结束的格子
            res++;
        }
        return res;
    }
};

10. 最大子序和

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        for i in range(1,len(nums)):
            nums[i]+=max(nums[i-1],0)
        return max(nums)
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        int max_num = nums[0];
        for(int i = 1; i < n; i++){
            nums[i] += max(nums[i-1], 0);
            max_num = max(nums[i], max_num);
        }
        return max_num;
    }
};

11. 环形子数组的最大和

题解:

class Solution:
    def maxSubarraySumCircular(self, nums: List[int]) -> int:
        dpmax = nums.copy()
        dpmin = nums.copy()
        sum_ = sum(nums)
        for i in range(1, len(nums)):
            dpmax[i] = max(dpmax[i-1]+nums[i], dpmax[i])
        for i in range(1, len(nums)):
            dpmin[i] = min(dpmin[i-1]+nums[i], dpmin[i])
        # print(dpmax)
        # print(dpmin)
        max_value = max(dpmax)
        min_value = min(dpmin)
        if (sum_ - min_value) == 0:
            return max_value
        else:
            return max(max_value, sum_ - min_value)
class Solution {
public:
    int maxSubarraySumCircular(vector<int>& nums) {
        int sum_ = nums[0];
        vector<int>dpmax(nums);
        vector<int>dpmin(nums);
        for(int i=1;i<nums.size();i++){
            dpmax[i]=max(dpmax[i-1]+nums[i],nums[i]);
            dpmin[i]=min(dpmin[i-1]+nums[i],nums[i]);
            sum_ += nums[i];
        }
        int maxv=*max_element(dpmax.begin(),dpmax.end());
        int minv=*min_element(dpmin.begin(),dpmin.end());
        if(sum_ - minv == 0 ){
            return maxv;
        }
        else{
            return max(maxv, sum_ - minv);
        }
    }
};

12. 最佳观光组合

class Solution:
    def maxScoreSightseeingPair(self, values: List[int]) -> int:
        # n = len(values)
        # max_score = float('-inf')
        # for i in range(n):
        #     for j in range(i+1, n):
        #         max_score = max(max_score, values[i]+values[j]+i-j)
        # return max_score
        n = len(values)
        mx = values[0]
        max_score = float('-inf')
        for i in range(1, n):
            max_score = max(max_score, mx + values[i] - i)
            mx = max(mx, values[i] + i)
        return max_score
class Solution {
public:
    int maxScoreSightseeingPair(vector<int>& values) {
        int n = values.size();
        int mx = values[0];
        int max_score = INT_MIN;
        for(int i = 1; i < n; i++){
            max_score = max(max_score, mx + values[i] - i);
            mx = max(mx, values[i] + i);
        }
        return max_score;
    }   
};

13. 买卖股票的最佳时机

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if len(prices) <= 1:
            return 0
        min_price = prices[0]
        max_profit = 0
        for i in range(1, len(prices)):
            min_price = min(prices[i], min_price)
            max_profit = max(max_profit, prices[i] - min_price)
        return max_profit
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size() <= 1){
            return 0;
        }
        int max_profit = 0, min_price = prices[0];        
        for(int i = 1; i < prices.size(); i++){
            min_price = min(min_price, prices[i]);
            max_profit = max(max_profit, prices[i] - min_price);
        }
        return max_profit;   
    }
};

 14. 买卖股票的最佳时机 II

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if len(prices)<=1:
            return 0
        res = 0
        for i in range(1, len(prices)):
            res += max(0, prices[i] - prices[i-1])
        return res
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        if(n <= 1){
            return 0;
        }
        int res = 0;
        for(int i = 1; i < n; i++){
            res += max(0, prices[i] - prices[i-1]);
        }
        return res;
    }
};

15.买卖股票的最佳时机含手续费

class Solution:
    def maxProfit(self, prices: List[int], fee: int) -> int:
        dp = [[0 for i in range(2)] for i in range(len(prices))]
        dp[0][1] = -prices[0]

        for i in range(1, len(prices)):
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i] - fee)
            dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i])
        # print('==dp:', dp)
        return dp[-1][0]

16.最佳买卖股票时机含冷冻期

 

class Solution:
    def maxProfit(self, prices: List[int]) -> int: 
        if len(prices)<=1:
            return 0       
        dp = [[0 for i in range(2)] for i in range(len(prices))]
        # print('==dp:', dp)
        dp[0][1] = -prices[0]

        dp[1][0] = max(dp[1 - 1][0], dp[1 - 1][1] + prices[1])
        dp[1][1] = max(dp[1-1][1], -prices[1])
        for i in range(2, len(prices)):
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i])
            dp[i][1] = max(dp[i - 1][1], dp[i - 2][0] - prices[i])
        # print('==dp:', dp)
        return dp[-1][0]#返回没有的最大利润

16-1. 单词拆分

思路1:动态规划

#动态规划 dp[i]表示 s 的前 i 位是否可以用 wordDict 中的单词表示,
#
class Solution:
    def wordBreak(self, s, wordDict):
        n = len(s)
        dp = [False] * (n + 1)
        dp[0] = True
        for i in range(n):
            for j in range(i+1, n+1):
                if dp[i] and (s[i:j] in wordDict):
                    dp[j] = True
        print('==dp:', dp)
        return dp[-1]
s = "leetcode"
wordDict = ["leet", "code"]
sol = Solution()
res=  sol.wordBreak(s, wordDict)
print('==res:', res)

c++实现:

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        int n = s.size();
        unordered_set<string> wordDictSet;
        for (auto word: wordDict) {
            wordDictSet.insert(word);
        }        
        vector<bool> dp(n+1, false);
        dp[0] = true;
        for(int i = 0; i < n; i++){
            for(int j = i+1; j < n+1; j++){                
                if(dp[i] && wordDictSet.find(s.substr(i, j - i)) != wordDictSet.end())                 {
                    // cout<<"s.substr(i, j - i):"<<s.substr(i, j - i)<<endl;
                    dp[j] = true;
                }
            }
        }
        return dp[n];
    }
};

思路2:回溯加缓存


#递归 lru_cache用于缓存 将数据缓存下来 加快后续的数据获取 相同参数调用时直接返回上一次的结果
import functools
class Solution:
    @functools.lru_cache()
    def helper(self, s):
        if len(s) == 0:
            return True
        res = False
        for i in range(1, len(s)+1):
            if s[:i] in self.wordDict:
                res = self.helper(s[i:]) or res
        return res
    def wordBreak(self, s, wordDict):
        self.wordDict  = wordDict
        return self.helper(s)
s = "leetcode"
wordDict = ["leet", "code"]
# s = "aaaaaaa"
# wordDict = ["aaaa", "aaa"]
# s= "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab"
# wordDict = ["a","aa","aaa","aaaa","aaaaa","aaaaaa","aaaaaaa","aaaaaaaa","aaaaaaaaa","aaaaaaaaaa"]
sol = Solution()
res=  sol.wordBreak(s, wordDict)
print('==res:', res)

16-2.单词拆分 II

思路:递归 


class Solution:
    def helper(self, s, wordDict, memo):
        if s in memo:#递归终止条件
            return memo[s]
        if s=='':#递归终止条件
            return []
        res = []
        for word in wordDict:
            if not s.startswith(word):
                continue
            if len(word)==len(s):#匹配上刚好相等
                res.append(word)
            else:#匹配上 但是字符还没到最后
                rest = self.helper(s[len(word):], wordDict, memo)
                for tmp in rest:
                    tmp = word+ " "+ tmp
                    res.append(tmp)
        print('==res:', res)
        print('==memo:', memo)
        memo[s] = res
        return res

    def wordBreak(self, s, wordDict):
        if s=='':
            return []
        return self.helper(s, wordDict, memo={})
s = "catsanddog"
wordDict = ["and", "cat", "cats", "sand", "dog"]
# s = "cat"
# wordDict = ["cat"]
sol = Solution()
res = sol.wordBreak(s, wordDict)
print(res)

 17-1,盛最多水的容器

求Max{(j-i) * Min( h(i), h(j) )}, 

height=[2,8,1,5,9,3,4]

暴力法: 超出时间限制

#解法1
class Solution:
    def maxarea(self,height):
        max_area=0
        for i in range(len(height)-1):
            for j in range(i+1,len(height)):
                if (j-i)*min(height[i],height[j])>max_area:
                    max_area=(j-i)*min(height[i],height[j])
                    index_i=i
                    index_j=j
        return index_i,index_j,max_area

s=Solution()
height=[2,8,1,5,9,3,4]
i,j,max_area=s.maxarea(height)
print(i,j,max_area)

分析:暴力法时间复杂度为O(n2),想想看,

  1. 如果 h(7) >= h(1),我们还有必要再遍历h(6),h(5),...,h(2)吗,其实不用,这便是暴力算法的冗余之处,多做了很多次无用的遍历,i = 1这趟遍历中,最大面积一定为 (7-1) * h(1) ;

  2. 如果 h(7) < h(1),我们再尝试h(6),如果h(6)>=h(1),那么在i = 1这趟遍历中的面积最大为(6-1) * h(1),没必要再试h(5)了,依次这样下去。

动态规划:

  1. 面积最大值初始值设定 maxarea;

  2. i, j 分别指向索引两头,动态交替地调整 i, j ,进而尝试取得较大的相对高度,这个调整的策略是关键,同时,更新目标函数即面积的最大值,如果大于maxarea,则更新;

  3. 直到 i > j 为止;

  4. 返回最大值法

时间复杂度为 O(n),空间复杂度为 O(1) 。 

#解法2
class Solution:
    def maxarea(self,height):
        left=0
        right=len(height)-1
        max_area=0

        while left<right:
            max_area = max(max_area,(right - left) * min(height[left], height[right]))
            if height[left]<height[right]:
                left+=1
            else:
                right-=1
            # index_i = left
            # index_j=right

        return max_area

s=Solution()
height=[2,8,1,5,9,3,4]
max_area=s.maxarea(height)
print(max_area)

17-2:接雨水

思路1.暴力法  对于i处能存的水,向右向左分别找到最大的值,在取这两值中的最小值减去此刻的值就是能存的水,超时O(n^2)

class Solution:
    def trap(self, height):
        res = 0
        n = len(height)
        for i in range(1, n):
            print('==i:', i)
            left_max, right_max = 0, 0
            for j in range(i, -1, -1):#往左搜索
                left_max = max(left_max, height[j])

            for j in range(i, n):#往右搜索
                right_max = max(right_max, height[j])
            print('==left_max:', left_max)
            print('==right_max:', right_max)
            res +=min(right_max, left_max) - height[i]
        print('res:', res)
        return res

height = [0,1,0,2,1,0,1,3,2,1,2,1]
sol = Solution()
sol.trap(height)

思路2.优化,双指针

#某个位置i处,它能存的水,取决于它左右两边(left_max,right_max)的最大值中较小的一个。
#对于位置left而言,它左边最大值一定是left_max,右边最大值“大于等于”right_max,
# 这时候,如果left_max<right_max成立,那么它就知道自己能存多少水了。
# 无论右边将来会不会出现更大的right_max,都不影响这个结果。
# 所以当left_max<right_max时,我们就希望去处理left下标,反之,我们希望去处理right下标。O(n)
class Solution:
    def trap(self, height):
        left,right =0,len(height)-1
        left_max,right_max =0,0
        res = 0
        while left<=right:
            if left_max <right_max:
                res+=max(0, left_max - height[left])
                left_max = max(left_max, height[left])
                left+=1
            else:
                res += max(0, right_max - height[right])
                right_max = max(right_max, height[right])
                right -= 1
        print('==res:', res)
        return res

height = [0,1,0,2,1,0,1,3,2,1,2,1]
sol = Solution()
sol.trap(height)

c++实现:

class Solution {
public:
    int trap(vector<int>& height) {
        int left = 0, right = height.size() - 1;
        int left_max = 0, right_max = 0;
        int res = 0;
        while(left <= right){
            if(left_max < right_max){
                res += max(0, left_max - height[left]);
                left_max = max(height[left], left_max);
                left++;
            }
            else{
                res += max(0, right_max - height[right]);
                right_max = max(height[right], right_max);
                right--;
            }
        }
        return res;
    }
};

思路3:动态规划

开出两个数组,一个用于统计坐边最大值,一个用于统计右边最大值,这样最终该点的雨水就是当前点的短板减去当前值。

class Solution:
    def trap(self, height: List[int]) -> int:
        length = len(height)
        if length == 0:
            return 0
        left_max = [0 for i in range(length)]
        left_max[0] = height[0]

        right_max = [0 for i in range(length)]
        right_max[-1] = height[-1]

        for i in range(1, length):
            left_max[i] = max(left_max[i-1], height[i])
        
        for i in range(length-2, -1, -1):
            right_max[i] = max(right_max[i + 1], height[i])

        res = 0
        for i in range(length):
            res += min(left_max[i], right_max[i]) - height[i]
        return res

c++实现 :

class Solution {
public:
    int trap(vector<int>& height) {
        int res = 0;
        int length = height.size();
        if (length == 0){
            return res;
        }
        vector<int> left_max(length, 0);
        vector<int> right_max(length, 0);
        left_max[0] = height[0];
        right_max[length-1] = height[length-1];
        for (int i=1; i<length; i++){
            left_max[i] = max(left_max[i-1], height[i]);
        }
        for (int i=length-2; i>=0; i--){
            right_max[i] = max(right_max[i+1], height[i]);
        }
        for (int i=0; i<length; i++){
            res += min(right_max[i], left_max[i]) - height[i];
        }
        return res;
    }
};

18. 等差数列划分

class Solution:
    def numberOfArithmeticSlices(self, nums: List[int]) -> int:
        n = len(nums)
        if n <= 1:
            return 0
        dis = nums[1] - nums[0]
        res = 0
        temp = 0
        for i in range(2, n):
            if nums[i] - nums[i-1] == dis:
                temp += 1
            else:
                dis = nums[i] - nums[i-1]
                temp = 0
            res += temp
        return res

19. 解码方法

class Solution:
    def numDecodings(self, s: str) -> int:
        n = len(s)
        f = [1] + [0] * n
        for i in range(1, n + 1):
            if s[i-1] != '0':
                f[i] += f[i-1]
            if i > 1 and s[i-2] != '0' and int(s[i-2:i])<=26:
                f[i] += f[i-2]
        return f[n]

20-1:丑数

思路:判断是否能整除2,3,5依次整除下去,将不能整除和1进行判断就知道是否是丑数了

class Solution:
    def isUgly(self, n: int) -> bool:
        if n <= 0: return False
        while((n % 2) == 0):
            n /= 2
        while((n % 3) == 0):
            n /= 3
        while((n % 5) == 0):
            n /= 5
        return n == 1

c++循环实现:

class Solution {
public:
    bool isUgly(int n) {
        if(n <= 0){return false;}
        while((n % 2) == 0){
            n /= 2;
        }
        while((n % 3) == 0){
            n /= 3;
        }
        while((n % 5) == 0){
            n /= 5;
        }
        return n == 1;
    }
};

c++递归实现:


//递归写法
class Solution {
public:
    bool isUgly(int n) {
        if(n <= 0){return false;}
        while((n % 2) == 0){
            return isUgly(n / 2);
        }
        while((n % 3) == 0){
            return isUgly(n / 3);
        }
        while((n % 5) == 0){
            return isUgly(n / 5);
        }
        return n == 1;
    }
};

20-2.丑数

思路:题目要求的这个数字一定是由单个或者多个2,3,5的乘积,如果从小到大去枚举在判断是否由2,3,5乘积组成,工作量会很大,所以考虑用2,3,5从下往上递推,需要开辟空间为n的数组,采用动态规划,2,3,5分别有三个索引,如果满足要求的数字等于2,3,5的倍数乘积,那么就直接将索引加1.

python代码:

class Solution:
    def nthUglyNumber(self, n):
        dp, index_two, index_three, index_five = [1] * n, 0, 0, 0
        for i in range(1, n):
            two = dp[index_two] * 2
            three = dp[index_three] * 3
            five = dp[index_five] * 5
            dp[i] = min(two, three, five)
            if two==dp[i]:
                index_two+=1
            if three==dp[i]:
                index_three+=1
            if five==dp[i]:
                index_five+=1
            print('==dp:', dp)
        return dp[-1]

n = 11
sol = Solution()
sol.nthUglyNumber(n)

c++代码:

class Solution {
public:
    int nthUglyNumber(int n) {
        vector<int> dp(n,1);
        int index_two=0;
        int index_three=0;
        int index_five=0;
        for (int i=1;i<n;i++){
            int two = dp[index_two]*2;
            int three = dp[index_three]*3;
            int five = dp[index_five]*5;
            dp[i] = min(min(two, three), five);
            if (dp[i]==two){
                index_two++;
            }
            if (dp[i]==three){
                index_three++;
            }
            if (dp[i]==five){
                index_five++;
            }
        }
        return dp[n-1];

    }
};

21.不同的二叉搜索树

思路:卡塔兰数

将 1⋯(i−1) 序列作为左子树,将 (i+1)⋯n 序列作为右子树。接着我们可以按照同样的方式递归构建左子树和右子树。

在上述构建的过程中,由于根的值不同,因此我们能保证每棵二叉搜索树是唯一的.也就得到卡塔兰数

class Solution(object):
    def numTrees(self, n):
        """
        :type n: int
        :rtype: int
        """
        #状态方程 和G(j-1) * G(n-j)
        dp = [0]*(n+1)
        #0 1树都为1
        dp[0], dp[1] = 1, 1
        for i in range(2,n+1):
            for j in range(1,i+1):
                dp[i] += dp[j-1]*dp[i-j]
        # print('==dp:', dp)
        return dp[-1]

c++实现:

class Solution {
public:
    
    int numTrees(int n) {
        vector<int> res(n+1,0);    
        res[0] = 1;
        res[1] = 1;
        for (int i=2;i<n+1;i++){
            for (int j=1;j<i+1;j++){
                res[i] += res[j-1] * res[i-j];
            }
        }
        return res[n];
    }   
};

22. 杨辉三角

python:

class Solution:
    def generate(self, numRows: int) -> List[List[int]]:
        res = []
        for i in range(numRows):
            temp = [0]*(i+1)
            for j in range(i + 1):
                if j == 0 or i == j:
                    temp[j] = 1
                else:
                    temp[j] = res[i-1][j] + res[i-1][j-1]
            res.append(temp)
        return res

c++:

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> res;
        for(int i = 0; i < numRows; i++){
            vector<int> temp(i+1, 0);
            for(int j = 0; j < i+1; j++){
                if(j == 0 || i == j){
                    temp[j] = 1;
                }
                else{
                    temp[j] = res[i-1][j-1] + res[i-1][j];
                }
            }
            res.push_back(temp);
        }
        return res;
    }
};

 23. 杨辉三角 II

python: 

class Solution:
    def getRow(self, rowIndex: int) -> List[int]:
        res = [0]*(rowIndex+1)
        res[0] = 1
        for i in range(rowIndex + 1):
            for j in range(i, 0, -1):
                res[j] += res[j-1]
        return res

 c++:

class Solution {
public:
    vector<int> getRow(int rowIndex) {
        vector<int> res(rowIndex+1, 0);
        res[0] = 1;
        for(int i = 1; i < rowIndex + 1; i++){
            for(int j = i; j > 0; j--){
                res[j] += res[j-1];
            }
        }
        return res;
    }
};

24.最小路径和

思路:动态规划 dp[i][j] = min(dp[i-1][j],dp[i][j-1])+v[i][j]


import numpy as np
#dp[i][j] = min(dp[i-1][j],dp[i][j-1])+v[i][j]
class Solution:
    def minPathSum(self, grid):
        h = len(grid)
        w = len(grid[0])

        dp = [[0 for i in range(w)] for j in range(h)]
        dp[0][0] = grid[0][0]
        for i in range(1, h):
            dp[i][0] = dp[i-1][0]+grid[i][0]

        for i in range(1, w):
            dp[0][i] = dp[0][i-1]+grid[0][i]
        print('==np.array(dp):\n', np.array(dp))

        for i in range(1, h):
            for j in range(1, w):
                dp[i][j] = min(dp[i-1][j],dp[i][j-1])+grid[i][j]
        print('==np.array(dp):\n', np.array(dp))
        return dp[-1][-1]
grid = [[1,3,1],[1,5,1],[4,2,1]]
sol = Solution()
sol.minPathSum(grid)

c++:

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        //dp[i][j] = min(dp[i-1][j], dp[i][j-1])+matrix[i][j]
        int m = grid.size();
        int n = grid[0].size();
        vector<vector<int>> dp(m, vector<int>(n, 0));
        dp[0][0] = grid[0][0];
        for(int i = 1; i < m; i++){
            dp[i][0] = dp[i-1][0] + grid[i][0];
        }
        for(int j = 1; j < n; j++){
            dp[0][j] = dp[0][j-1] + grid[0][j];
        }
        for(int i = 1; i < m; i++){
            for(int j = 1; j < n; j++){
                dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j];
            }
        }
        return dp[m-1][n-1];
    }
};

25. 下降路径最小和

状态转移方程:(注意边界) 

dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i-1][j+1])+matrix[i][j]

python:

class Solution:
    def minFallingPathSum(self, matrix: List[List[int]]) -> int:
        #dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i-1][j+1])+matrix[i][j]
        n = len(matrix)
        # dp = [[0 for i in range(n)] for i in range(n)]
        # for i in range(n):
        #     dp[0][i] = matrix[0][i]
        for i in range(1, n):
            for j in range(n):
                if j == 0:
                    matrix[i][j] = min(matrix[i-1][j], matrix[i-1][j+1])+matrix[i][j]             
                elif j == n-1:
                    matrix[i][j] = min(matrix[i-1][j-1], matrix[i-1][j])+matrix[i][j]   
                else:
                    matrix[i][j] = min(matrix[i-1][j-1], matrix[i-1][j], matrix[i-1][j+1])+matrix[i][j]
        # print(dp)
        return min(matrix[-1])

c++:

class Solution {
public:
    int minFallingPathSum(vector<vector<int>>& matrix) {
    //dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i-1][j+1])+matrix[i][j]
        int n = matrix.size();
        vector<vector<int>> dp(n, vector<int>(n, 0));
        for(int i = 0; i < n; i++){
            dp[0][i] = matrix[0][i];
        }
        for(int i = 1; i < n; i++){
            for(int j = 0; j < n; j++){
                if(j == 0){
                    dp[i][j] = min(dp[i-1][j], dp[i-1][j+1])+matrix[i][j];
                }
                else if(j == n-1){
                    dp[i][j] = min(dp[i-1][j-1], dp[i-1][j])+matrix[i][j];
                }
                else{
                    dp[i][j] = min(dp[i-1][j-1], min(dp[i-1][j], dp[i-1][j+1]))+matrix[i][j];
                }
            }
        }
        int min_value = *min_element(dp[n-1].begin(), dp[n-1].end());
        return min_value;
    }
};

26.三角形最小路径和

状态转移方程,注意边界 

dp[i][j] = min(dp[i-1][j-1],dp[i-1][j])+ matrix[i][j]

python:

class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        #dp[i][j] = min(dp[i-1][j-1],dp[i-1][j])+ matrix[i][j]
        for i in range(1, len(triangle)):
            for j in range(len(triangle[i])):
                if j == 0:
                    triangle[i][j] = triangle[i-1][j] + triangle[i][j]
                elif j == i:
                    triangle[i][j] = triangle[i-1][j-1] + triangle[i][j]
                else:
                    triangle[i][j] = min(triangle[i-1][j-1], triangle[i-1][j]) + triangle[i][j]
        return min(triangle[-1])

c++: 

class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        int h = triangle.size();
        for(int i = 1; i < triangle.size(); i++){
            for(int j = 0; j < triangle[i].size(); j++){
                if(j == 0){
                    triangle[i][j] = triangle[i-1][j] + triangle[i][j];
                }
                else if(j == i){
                    triangle[i][j] = triangle[i-1][j-1] + triangle[i][j];
                }
                else{
                    triangle[i][j] = min(triangle[i-1][j-1], triangle[i-1][j]) + triangle[i][j];
                }
            }
        }
        int min_value = *min_element(triangle[h - 1].begin(), triangle[h - 1].end());
        return min_value;
    }
};

27. 不同路径

 python:

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        dp = [[0 for i in range(n)] for j in range(m)]
        for i in range(m):
            dp[i][0]  = 1
        for i in range(n):
            dp[0][i]  = 1
        # print('==np.array(dp):', np.array(dp))
        for i in range(1,m):
            for j in range(1,n):
                dp[i][j] = dp[i-1][j]+dp[i][j-1]
        # print(np.array(dp))
        return dp[-1][-1]

c++:

class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<vector<int>> dp(m, vector<int>(n, 0));
        for(int i = 0; i < m; i++){
            dp[i][0] = 1;
        }
        for(int j = 1; j < n; j++){
            dp[0][j] = 1;
        }
        for(int i = 1; i < m; i++){
            for(int j = 1; j < n; j++){
                dp[i][j] = dp[i-1][j] + dp[i][j-1];
            }
        }
        return dp[m-1][n-1];
    }
};

28.不同路径 II

 

 python:

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
        if obstacleGrid[0][0] == 1:
            return 0
        m, n = len(obstacleGrid), len(obstacleGrid[0])
        dp = [[0 for _ in range(n)] for _ in range(m)]
        dp[0][0] = 1
        for i in range(1, m):
            if dp[i-1][0] == 1 and obstacleGrid[i][0] != 1:
                dp[i][0] = 1
        for j in range(1, n):
            if dp[0][j-1] ==1 and obstacleGrid[0][j] != 1:
                dp[0][j] = 1
        for i in range(1, m):
            for j in range(1, n):
                if obstacleGrid[i][j] != 1:
                    dp[i][j] = dp[i-1][j] + dp[i][j-1]
        return dp[m-1][n-1]

c++:

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();
        vector<vector<int>> dp(m, vector<int>(n, 0));
        if(obstacleGrid[0][0] == 1){
            return 0;
        }
        dp[0][0] = 1;
        for(int i = 1; i < m; i++){
            if(dp[i-1][0]==1 && obstacleGrid[i][0] != 1){
                dp[i][0] = 1;
            }                
        }
        for(int j = 1; j < n; j++){
            if(dp[0][j-1] == 1 && obstacleGrid[0][j] != 1){
                dp[0][j] = 1;
            }                
        }
        for(int i = 1; i < m; i++){
            for(int j = 1; j < n; j++){
                if(obstacleGrid[i][j] != 1){
                    dp[i][j] = dp[i-1][j] + dp[i][j-1];
                }
            }
        }
        return dp[m-1][n-1];
    }
};

29.最大正方形

python:

状态转移方程

matrix[i][j] == '1'

dp[i][j] = min(dp[i-1][j], dp[i-1][j-1], dp[i][j-1]) + 1

class Solution:
    def maximalSquare(self, matrix: List[List[str]]) -> int:
        h = len(matrix)
        w = len(matrix[0])
        dp = [[0 for j in range(w)] for i in range(h)]
        dp[0][0] = int(matrix[0][0])
        maxside = int(matrix[0][0])
        for i in range(1, h):
            dp[i][0] = int(matrix[i][0])
            maxside = max(maxside, dp[i][0]) 
        for j in range(1, w):
            dp[0][j] = int(matrix[0][j])
            maxside = max(maxside, dp[0][j])
        
        for i in range(1, h):
            for j in range(1, w):
                if matrix[i][j] == '1':
                    dp[i][j] = min(dp[i-1][j], dp[i-1][j-1], dp[i][j-1]) + 1                  
                    maxside = max(maxside, dp[i][j])
        return maxside**2

c++:

class Solution {
public:
    int maximalSquare(vector<vector<char>>& matrix) {
        int m = matrix.size();
        int n = matrix[0].size();
        vector<vector<int>> dp(m, vector<int>(n, 0));
        int maxside = matrix[0][0] - '0';
        dp[0][0] = matrix[0][0] -  '0';
        for(int i = 1; i < m; i++){
            dp[i][0] = matrix[i][0] - '0';
            maxside = max(maxside, dp[i][0]);
        }
        for(int j = 1; j < n; j++){
            dp[0][j] = matrix[0][j] - '0';
            maxside = max(maxside, dp[0][j]);
        }
        // cout<<maxside<<endl;
        for(int i = 1; i < m; i++){
            for(int j = 1; j < n; j++){
                if(matrix[i][j] == '1'){
                    dp[i][j] = min(dp[i][j-1], min(dp[i-1][j-1], dp[i-1][j])) + 1;
                    maxside = max(maxside, dp[i][j]);
                }
            }
        }
        return maxside*maxside;

    }

};

30.最长回文子串

思路:中心扩散或者动态规划

class Solution:
    def helper(self,left,right,s):
        while left>=0 and right<len(s) and s[left]==s[right]:
            left-=1
            right+=1
        if len(s[left+1:right])>len(self.res):
            self.res = s[left+1:right]
    def longestPalindrome(self, s: str) -> str:
        self.res = ''
        for i in range(len(s)):
            self.helper(i,i,s)
            self.helper(i,i+1,s)
        return self.res

 c++:

class Solution {
public:
    string res;
    void help(string s, int left, int right){
        while(left>=0 && right<s.size() && s[left]==s[right]){
            left--;
            right++;
        }
        left++;
        right--;
        if((right - left + 1) > res.size()){
            res = s.substr(left, right - left + 1);            
            }
        }
    string longestPalindrome(string s) {
        if(s.size()<=1){
            return s;
        }
        for(int i=0; i<s.size(); i++){
            help(s, i, i);
            help(s, i, i+1);
            }
        return res;
        }
};

动态规划:

class Solution:
    def longestPalindrome(self, s: str) -> str:
        
        size = len(s)
        # 特殊处理
        if size == 1:
            return s
        # 创建动态规划dynamic programing表
        dp = [[False for _ in range(size)] for _ in range(size)]
        # 初始长度为1,这样万一不存在回文,就返回第一个值(初始条件设置的时候一定要考虑输出)
        max_len = 1
        start = 0
        for j in range(1,size):
            for i in range(j):
                # 边界条件:
                # 只要头尾相等(s[i]==s[j])就能返回True
                if j-i<=2:
                    if s[i]==s[j]:
                        dp[i][j] = True
                        cur_len = j-i+1
                # 状态转移方程 
                # 当前dp[i][j]状态:头尾相等(s[i]==s[j])
                # 过去dp[i][j]状态:去掉头尾之后还是一个回文(dp[i+1][j-1] is True)
                else:
                    if s[i]==s[j] and dp[i+1][j-1]:
                        dp[i][j] = True
                        cur_len = j-i+1
                # 出现回文更新输出
                if dp[i][j]:
                    if cur_len > max_len:
                        max_len = cur_len
                        start = i

        return s[start:start+max_len]

31.最长回文子序列

思路:动态规划

class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
        n = len(s)
        dp = [[0] * n for _ in range(n)]
        for i in range(n - 1, -1, -1):
            dp[i][i] = 1
            for j in range(i + 1, n):
                if s[i] == s[j]:
                    dp[i][j] = dp[i + 1][j - 1] + 2
                else:
                    dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
        return dp[0][n - 1]

32.最长递增子序列

思路:动态规划

python:

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        if len(nums)==0:
            return 0
        opt = [1]*len(nums)

        for i in range(1, len(nums)):
            for j in range(i):
                if nums[i]>nums[j]:
                    opt[i] = max(opt[i], opt[j]+1)
            # print('==value:', value)
            # opt[i] = value+1
            # print('==dp:', opt)
        return max(opt)

c++:

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n = nums.size();
        vector<int> dp(n, 1);
        int max_value = 1;
        for(int i = 1; i < n; i++){
            for(int j = 0; j < i; j++){
                if(nums[j] < nums[i]){
                    dp[i] = max(dp[i], dp[j] + 1);
                    max_value = max(dp[i], max_value);
                }
            }
        }
        return max_value;

    }
};

33.判断子序列

思路:双指针

class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
        #双指针
        i, j = 0, 0
        while i < len(s) and j < len(t):
            if s[i] == t[j]:
                i += 1
                j += 1
            else:
                j += 1
        if i == len(s):
            return True
        else:
            return False

class Solution {
public:
    bool isSubsequence(string s, string t) {
            int i=0,j=0;
            while(i < s.size() && j < t.size()){
                if(s[i] == t[j]){
                    i++;
                    j++;
                }
                else{
                    j++;
                }
            }
            if(i == s.size()){
                return true;
            }
            else{
                return false;
            }
    }
};

34-1.最长公共子序列

python: 

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        m = len(word1)
        n = len(word2)
        dp = [[0 for i in range(n+1)] for i in range(m+1)]
        for i in range(1, m+1):
            dp[i][0] = i
        for j in range(1, n+1):
            dp[0][j] = j
        
        for i in range(m):
            for j in range(n):
                if word1[i] == word2[j]:
                    dp[i+1][j+1] = dp[i][j]
                else:
                    dp[i+1][j+1] = min(dp[i][j], dp[i+1][j], dp[i][j+1]) + 1
        return dp[m][n]


        

c++: 

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 = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(text1[i] == text2[j]){
                    dp[i+1][j+1] = dp[i][j] + 1;
                }
                else{
                    dp[i+1][j+1] = max(dp[i+1][j], dp[i][j+1]);
                }
            }
        }
        return dp[m][n];
    }
};

34-2.最长公共字串

# # 动态规划解决最大公共子串问题
def find_lcsubstr(s1, s2):
    m = [[0 for i in range(len(s2) + 1)] for j in range(len(s1) + 1)]  # 生成0矩阵,为方便后续计算,比字符串长度多了一列
    print(m)
    mmax = 0  # 最长匹配的长度
    p = 0  # 最长匹配对应在si中的最后一位
    for i in range(len(s1)):
        for j in range(len(s2)):
            if s1[i] == s2[j]:
                m[i + 1][j + 1] = m[i][j] + 1
                if m[i + 1][j + 1] > mmax:
                    mmax = m[i + 1][j + 1]
                    p = i + 1
    print(p)
    return s1[(p - mmax):p], mmax  # 返回最长子串及其长度

 35.编辑距离

 编辑距离,又称Levenshtein距离(莱文斯坦距离也叫做Edit Distance),是指两个字串之间,由一个转成另一个所需的最少编辑操作次数,如果它们的距离越大,说明它们越是不同。许可的编辑操作包括将一个字符替换成另一个字符插入一个字符删除一个字符

mat[i+1,j]+1表示增加操作
d[i,j+1]+1 表示删除操作
d[i,j]+temp表示替换操作,其中temp取0或1


import numpy as np


# 相等的情况dp[i][j] = min(dp[i-1][j-1], dp[i-1][j]+1, dp[i][j-1]+1)
# 不相等的情况dp[i][j] = min(dp[i-1][j-1]+1, dp[i-1][j]+1, dp[i][j-1]+1)
class Solution:
    def minDistance(self, word1, word2):
        dp = [[0 for i in range(len(word1) + 1)] for i in range(len(word2) + 1)]

        for i in range(len(word1) + 1):
            dp[0][i] = i
        print('==np.array(dp):', np.array(dp))
        for i in range(len(word2) + 1):
            dp[i][0] = i
        print('==np.array(dp):', np.array(dp))
        for i in range(len(word2)):
            for j in range(len(word1)):
                if word2[i] == word1[j]:
                    dp[i+1][j+1] = dp[i][j]
                else:
                    dp[i+1][j+1] = min(dp[i][j]+1, dp[i][j+1]+1, dp[i+1][j]+1)
        print('==np.array(dp):', np.array(dp))
        return dp[-1][-1]

word1 = "horse"
word2 = "ros"
sol = Solution()
sol.minDistance(word1, word2)

c++实现:

class Solution {
public:
    int minDistance(string word1, string word2) {
        int h  = word1.size();
        int w  = word2.size();
        vector<vector<int>> opt(h + 1, vector<int>(w + 1, 0));
        for(int i = 0; i < h; i++){
            opt[i + 1][0] = i + 1;
        }
        for(int j = 0; j < w; j++){
            opt[0][j + 1] = j + 1;
        }
        for(int i = 0; i < h; i++){
            for (int j = 0; j < w; j++){
                if(word1[i] == word2[j]){
                    opt[i + 1][j + 1] = opt[i][j];
                }
                else{
                    opt[i + 1][j + 1] = min(opt[i][j] + 1, min(opt[i + 1][j] + 1, opt[i][j + 1] + 1));
                }
            }
        }
        return opt[h][w];
    }
};

36.零钱兑换

 思路:找准状态状转移方程,f代表选择银币的函数,则f(11)=f(11-1)+1或f(11)=f(11-2)+1或f(11)=f(11-5)+1,则一般方程为:

f(money) = min(f(money), f(money-coin)+1)

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        #状态转移方程f(money) = min(f(money),f(money-coin)+1)
        f = [float('inf')] * (amount + 1)
        f[0] = 0
        # print('==f:', f)
        for i in range(1, amount + 1):
            for coin in coins:
                if i - coin >= 0:
                    f[i] = min(f[i], f[i - coin] + 1)
            # print('==f:', f)
        return f[-1] if f[-1]!=float('inf') else -1

            

c++实现:

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount+1, INT_MAX-1);
        dp[0] = 0;
        for(int i = 1; i < amount + 1; i++){
            for(int j = 0; j < coins.size(); j++){
                if(i - coins[j] >= 0){
                    dp[i] = min(dp[i - coins[j]] + 1, dp[i]);
                }
            }
        }
        if(dp[amount] == INT_MAX-1){
            return -1;
        }
        else{
            return dp[amount];
        }
    }
};

3​​​​​​​​​​​​​7:零钱兑换 II

思路1:回溯 会超时


# 组合问题 回溯 超时
class Solution:
    def backtrace(self, amount, start, coins, track):
        if amount == 0:  # 终止条件
            # self.res.append(track)
            self.res+=1
            return
        for i in range(start, len(coins)):  # 选择条件
            if coins[i] > amount:
                continue
            # store = track.copy()
            # track.append(coins[i])
            self.backtrace(amount - coins[i], i, coins, track)
            # track = store

    def change(self, amount, coins):
        self.res = 0#[]
        coins = sorted(coins)
        self.backtrace(amount, 0, coins, [])
        return self.res

# amount = 5
# coins = [2]
amount = 5
coins = [1, 2, 5]
# amount = 500
# coins = [3,5,7,8,9,10,11]
sol = Solution()
res = sol.change(amount, coins)
print('==res:', res)

思路2:当成完全背包问题,用dp

#dp[i][j] 硬币为i 金额为j的组合数
import numpy as np
class Solution:
    def change(self, amount, coins):
        if len(coins) == 0:
            if amount == 0:
                return 1
            else:
                return 0
        dp = [[0 for i in range(amount+1)] for j in range(len(coins))]
        print('==np.array(dp):', np.array(dp))
        dp[0][0] = 1
        for j in range(coins[0], amount+1, coins[0]):
            dp[0][j] = 1
        print('==np.array(dp):', np.array(dp))
        for i in range(1, len(coins)):
            print('==coins[i]:', coins[i])
            for j in range(amount+1):
                dp[i][j] = dp[i - 1][j]#不选
                if j >= coins[i]:#选 注意与0 1背包有一点不同
                    dp[i][j] += dp[i][j - coins[i]]
            print('==np.array(dp):', np.array(dp))
        return dp[-1][-1]

amount = 5
coins = [1, 2, 5]
sol = Solution()
sol.change(amount, coins)

c++实现:

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        int m = coins.size();
        vector<vector<int>> dp(m, vector<int>(amount + 1, 0));
        dp[0][0] = 1;
        for(int j = coins[0]; j < amount + 1; j += coins[0]){
            dp[0][j] = 1;
        }
        for(int i = 1; i < m; i++){
            for(int j = 0; j < amount + 1; j++){
                dp[i][j] = dp[i-1][j];
                if(j >= coins[i]){
                    dp[i][j] += dp[i][j - coins[i]];
                }
            }
        }
        return dp[m-1][amount];
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值