算法专辑

动态规划

最大连续子数组

任何一道动态规划  最为关键的是如何找到状态转移方程 (也就是最终问题的解 如何通过一步步其它的解得出来)

这题得出来的状态转移方程就是  f(n) = nums[i] + f(n-1)
其中n是假设该最大子数组的最右下标为 n
nums= [-2,1,-3,4,-1,2,1,-5,4]        # 最大子数组 动态规划解法
n = len(nums)
# dp = [-99999 for i in range(n)]
dp = []
for i in range(n):
    if i == 0:				# 从左往右遍历 因此第一个数一定要写进 dp数组中
        dp.append(nums[i])
    else:
        if (dp[i-1]<=0):	# 后续 dp的值取决于前面那个值的大小  如果小于等于0  那就没有加上他们的必要了
            dp.append(nums[i])
        else:
            dp.append(nums[i]+dp[i-1])	# 反之需要加上   这个正数代表着 不管前面是怎样的排列 最终能给我带来正数的加
dp = sorted(dp)
print(dp[n-1])

在这里插入图片描述

第N个泰波那契数

在这里插入图片描述
题解

class Solution:
    def tribonacci(self, n: int) -> int:
        if n == 0:
            return 0
        elif n == 1:
            return 1
        elif n == 2:
            return 1
        dp = [0,1,1]
        for i in range(3,n+1):
            dp.append(dp[i-1]+dp[i-2]+dp[i-3])
        return dp[n]

爬楼梯

在这里插入图片描述
这题的关键点在于 需要想明白 第n阶楼梯的总方法 是 等于 n-1加上 n-2方法的总和
这样就能写出 状态转移方程(最重要的部分) f(n) = f(n-1) + f(n-2)

class Solution:
    def climbStairs(self, n: int) -> int:
        if n == 1:
            return 1
        elif n == 2:
            return 2
        dp = [1,2]
        for i in range(2,n):
            dp.append(dp[i-1]+dp[i-2])
        return dp[n-1]

使用最小花费爬楼梯

在这里插入图片描述
像这种爬楼梯问题 题目限定一次只能爬一阶 或者两阶的问题

设上到第n阶梯最小花费为 F(n) ,则F(n) = min(F(n-1),F(n-2)) 因为上到这一阶肯定是前面
的最小值来的   所以就可以写出  转移方程

class Solution:
    def minCostClimbingStairs(self, cost: List[int]) -> int:
        dp = [cost[0], cost[1]]	   # 初始dp
        n = len(cost)
        for i in range(2,n):
            dp.append(min(dp[i-1],dp[i-2])+cost[i])  # 代价是前面的最小加上这一阶的
        return min(dp[n-1],dp[n-2])
 

打家劫舍

在这里插入图片描述

1. 偷窃第 k 间房屋,那么就不能偷窃第 k−1 间房屋,偷窃总金额为前 k−2 间房屋的最高总金额与第 k 间房屋的金额之和。

2. 不偷窃第 k 间房屋,偷窃总金额为前 k−1 间房屋的最高总金额。
3. 设dp[i]为前i间房屋能偷到的最大金额 那么就有一下状态转移方程:
				dp[i] = max(dp[i-2]+nums[i], dp[i-1])
边界条件为:
{ 
	dp[0]=nums[0]						只有一间房屋,则偷窃该房屋
	dp[1]=max(nums[0],nums[1])			只有两间房屋,选择其中金额较高的房屋进行偷窃
}
class Solution:
    def rob(self, nums: List[int]) -> int:
        
        n = len(nums)
        if n == 1:
            return nums[0]

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

双指针

两数之和 ||

在这里插入图片描述
题目分析:

因为给定的数组是有序(正序)数组  且要找的两个数一定是一大一小
假设要找的数是   nums[i]  nums[j]     i<j 
这样就可以用双指针  左指针起始在0  右指针起始在 len(nums)-1   通过不断对比 target的值  将 指针缩小范围直至确认 i j
class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        low, high = 0, len(numbers) - 1 # 确定两个指针的位置
        while low < high: # 指针移动条件
            total = numbers[low] + numbers[high]
            if total == target:
                return [low + 1, high + 1] # // 返回下标从1开始
            elif total < target:
                low += 1 # Python中没有++ --的用法
            else:
                high -= 1
        return [-1, -1]

反转字符串

在这里插入图片描述
这里是通过元组不断 交换 头尾指针的数值

class Solution:
    def reverseString(self, s: List[str]) -> None:
        """
        Do not return anything, modify s in-place instead.
        """
        r = len(s)-1
        l = 0
        while (l<r):
            s[l],s[r] = s[r], s[l]
            l += 1
            r -= 1
        print(s)
        # s = list(s[::-1])
        # print(s[::-1])

移动零

在这里插入图片描述
题目分析

这里也是采用两个指针  左右指针 两个指针的起始位置都是 0  
往右遍历   不同的是:
左指针左边的数都是处理好的数  右指针负责主要遍历
class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        n = len(nums)
        left = right = 0 # 初始化左指针和右指针均指向列表的开头
        while right < n: 
            if nums[right] != 0: # 左指针应当指向为0的元素,如果右指针不为0就将左右指针指向的元素互换
                nums[left],nums[right] = nums[right],nums[left]
                left += 1
            right += 1

两个数组的交集 ||

在这里插入图片描述
这题我用到的思路也是双指针 题目中的序列是未排序的
那就先将其排序完后 在分别遍历两个数组 一致就放入新的数组中 因为是有序数组了 因此只需要一直向右遍历就可

class Solution:
    def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
        nums1 = sorted(nums1)
        nums2 = sorted(nums2)
        n = len(nums1)
        m = len(nums2)
        i = j = 0
        dp = []
        while i<n and j<m:
            if nums1[i]<nums2[j]:
                i += 1
            elif nums1[i]>nums2[j]:
                j += 1
            else:
                dp.append(nums1[i])
                i +=1
                j +=1
        return dp

寻找两个正序数组的中位数

在这里插入图片描述
题目分析

这题因为给的 两个数组是有序的  因此我们可以采用左右数组排序法(类似于归并排序的合并数组)
将两个有序数组合并成一个数组 因为是求中位数
因此我们不需要合并完 只需要合并到刚好能求出中位数即可
class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        n = len(nums1)
        m = len(nums2)
        l = r = i = 0	# l用来当作nums1的下标  r当作是nums2的下标来遍历  i是计数
        end = int((n+m)/2)
        fisrt = second = 0	# 因为中位数 最多也只需要两个数来求和 因此这里使用滚动数组
        while (i<=end):		# 只需遍历到end即可刚好求出中位数
            if l>=n:		# 当一边数组已经遍历完了 那就直接取另一边的就行
                fisrt , second = second , nums2[r]	# 滚动数组
                i += 1
                r += 1
                continue
            if r>=m:
                fisrt , second = second , nums1[l]
                i += 1
                l += 1
                continue
            if (nums1[l]<nums2[r]):	# 判断  小的放入滚动数组中 实现排序滚动数组
                fisrt , second = second , nums1[l]
                i += 1
                l += 1
            else:
                fisrt , second = second , nums2[r]
                i += 1
                r += 1
        if (n+m)%2 == 0:		# 判断  如果数组个数为偶数 那么就需要两个数来求出中位数  反之一个就行
            return (fisrt+second)/2
        else:
            return second

滑动窗口

无重复字符的最长子串

在这里插入图片描述
题目分析:

滑动窗口模板 :  需要两个指针 左右指针  l r  
l指针负责控制这个“窗口”在序列中的起始位置  (窗口可以是一个集合  或者字符串 或者数组 具体看题目)
r指针负责遍历 题目给出的序列
所以代码开头就会有一个遍历  用r指针遍历完对象序列  你可以while r<len(指定序列) 
每次遍历  需要看看新增的元素是否满足该“窗口” 满足就放进去  不满足就将窗口最左边的移除掉
且l 右移一位
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        l = r = 0	# 左右指针
        n = len(s)
        max_ = ans = 0	# max_来统计窗口最大长度  ans是负责统计窗口的实时长度
        ss = ''   # 窗口
        while (r<n):	# 用r指针 从左到右遍历目标序列 n为序列长度
            while s[r] in ss:   # 该情况就是遍历到的元素不满足窗口  需要将窗口左侧的元素剔除  因为窗口的长度变换 需要实时更新ans的大小 且将l指针右移
                ss = ss[1:]		# 剔除窗口最左边元素
                l += 1			# 右移左指针
                ans -= 1		# 将长度-1
            ss += s[r]		# 满足题目的要求 因此放入窗口中
            r += 1			# 更新右指针	
            ans += 1		# 更新目标序列长度
            max_ = max(max_,ans)	# max_永远记录最大的那个长度
        print(max_)
        return max_
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值