1、贪心算法
贪心算法的指导思想是:每一步下的最优,也就是局部最优,一定情况下逼近全局最优。
对应刷题:买卖股票的最佳时机
(1)给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
#贪心算法
max1=0
k=len(prices)
for i in range(1,k):
max1=max1+max((prices[i]-prices[i-1]),0)
return max1
(2)给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
if len(prices)<=1:
return 0
else:
result=0
min1=prices[0]
for i in range(1,len(prices)):
if prices[i]<min1:
min1=prices[i]
result=max(result,prices[i]-min1)
return result
(3)中等 跳跃游戏
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。
class Solution:
def canJump(self, nums: List[int]) -> bool:
mostright=0
for i in range(len(nums)):
if i<=mostright:
mostright=max(mostright,i+nums[i])
if mostright>=len(nums)-1:
return True
return False
2、动态规划
用一句话解释动态规划就是 “记住你之前做过的事”,如果更准确些,其实是 “记住你之前得到的答案”。
动态规划解题四个步骤:
问题拆解,找到问题之间的具体联系
状态定义
递推方程推导
实现(初始化)
典型例题1:爬楼梯LeetCode 第 70 号问题:爬楼梯。
假设你正在爬楼梯。需要 n阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
时间优,空间不优。
class Solution:
def climbStairs(self, n: int) -> int:
reslut=[]
reslut.append(1)
reslut.append(2)
for i in range(2,n):
reslut.append(reslut[i-1]+reslut[i-2])
return reslut[n-1]
典型例题2:LeetCode 第 53 号问题:最大子序和。
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
smax=nums[0]
tmp=nums[0]
for i in range(1,len(nums)):
tmp=max(tmp,0)
smax=max(nums[i]+tmp,smax)
tmp=tmp+nums[i]
return smax
典型例题3:
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
class Solution:
def rob(self, nums: List[int]) -> int:
if len(nums)==0:
return 0
else:
if len(nums)<=2:
return max(nums)
else:
first=nums[0]
second=max(nums[0],nums[1])
for i in range(2,len(nums)):
nums[i]=max(first+nums[i],second)
first=second
second=max(first,nums[i])
return nums[len(nums)-1]
通过这几个简单的例子,相信你不难发现,解动态规划题目其实就是拆解问题,定义状态的过程,严格说来,动态规划并不是一个具体的算法,而是凌驾于算法之上的一种 思想 。
这种思想强调的是从局部最优解通过一定的策略推得全局最优解,从子问题的答案一步步推出整个问题的答案,并且利用空间换取时间。从很多算法之中你都可以看到动态规划的影子,所以,还是那句话 技术都是相通的,找到背后的本质思想是关键。
参考网址:https://www.cxyxiaowu.com/6781.html
3、分治思想
分治策略:对于一个规模为 n 的问题,将其分解为 k 个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。
关键点:递归地解子问题,合并得到原问题的解,归并排序的典型思路。
典型例题1:二分查找。
x 的平方根:实现 int sqrt(int x) 函数。计算并返回 x 的平方根,其中 x 是非负整数。由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
class Solution:
def mySqrt(self, x: int) -> int:
l=0
r=x
ans=-1
while l<=r:
m=int((l+r)/2)
if m*m<=x:
ans=m
l=m+1
else:
r=m-1
return ans
典型例题2:归并排序。
归并排序,两路已经有序,合并成一路,关键点:如果两个数组是有序的,则可以便捷地计算两个数组的交集。
首先对两个数组进行排序,然后使用两个指针遍历两个数组。
初始时,两个指针分别指向两个数组的头部。每次比较两个指针指向的两个数组中的数字,如果两个数字不相等,则将指向较小数字的指针右移一位,如果两个数字相等,将该数字添加到答案,并将两个指针都右移一位。当至少有一个指针超出数组范围时,遍历结束。
给定两个数组,编写一个函数来计算它们的交集。
classSolution: defintersect(self, nums1: List[int], nums2: List[int])-> List[int]: nums1.sort()
nums2.sort()
result=[]
length1=len(nums1)
length2=len(nums2)
index1=0 index2=0 while index1<length1 and index2<length2:
if nums1[index1]<nums2[index2]:
index1=index1+1
elif nums1[index1]>nums2[index2]:
index2=index2+1
else:
result.append(nums1[index1])
index1=index1+1
index2=index2+1
return result
归并排序:归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。即先划分为两个部分,最后进行合并。
典型例题3:977. 有序数组的平方
给定一个按非递减顺序排序的整数数组 A,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。
low一点的版本:
class Solution:
def sortedSquares(self, A: List[int]) -> List[int]:
length=len(A)
if length==0:
return A
else:
result=[]
l=0
r=length-1
while l<=r:
tmp1=A[l]*A[l]
tmp2=A[r]*A[r]
if tmp1<tmp2:
result.insert(0,tmp2)
r=r-1
elif tmp1>tmp2:
result.insert(0,tmp1)
l=l+1
else:
result.insert(0,tmp1)
if l!=r:
result.insert(0,tmp2)
l=l+1
r=r-1
return result
优化后的版本:
class Solution:
def sortedSquares(self, A: List[int]) -> List[int]:
length=len(A)
if length==0:
return A
else:
result=[]
l=0
r=length-1
while l<=r:
tmp1=A[l]*A[l]
tmp2=A[r]*A[r]
if tmp1<tmp2:
result.insert(0,tmp2)
r=r-1
else:
result.insert(0,tmp1)
l=l+1
return result
典型例题3:快速排序。
快速排序的基本思想:当前待排序的无序区为 A[low..high] ,利用分治法可将快速排序的基本思想描述为:
(1)分解:
在A[low..high]中任选一个记录作为基准(pivot),以此基准将当前无序区划分为左、右两个较小的子区间R[low..pivotpos-1) 和 R[pivotpos+1..high] ,并使左边子区间中所有记录的关键字均小于等于基准记录(不妨记为pivot)的关键字 pivot.key,右边的子区间中所有记录的关键字均大于等于pivot.key,而基准记录pivot则位于正确的位置( pivotpos )上,它无须参加后续的排序。
(2)求解:
通过递归调用快速排序对左、右子区间R[low..pivotpos-1]和R[pivotpos+1..high]快速排序。
(3)合并:
因为当"求解"步骤中的两个递归调用结束时,其左、右两个子区间已有序。对快速排序而言,"组合"步骤无须做什么,可看作是空操作。
总结分析:
分治法将规模为 n 的问题分成 k 个规模为 n/m 的子问题去解。设分解阀值 n0 = 1 ,且 adhoc 解规模为 1 的问题耗费 1 个单位时间。再设将原问题分解为 k 个子问题以及用 merge 将 k 个子问题的解合并为原问题的解需用 f(n) 个单位时间。用T(n)表示该分治法解规模为 |P| = n 的问题所需的计算时间,则有:
T(n)= k T(n/m) + f(n)
4、递归算法
先下定义:递归算法是一种直接或者间接调用自身函数或者方法的算法。
通俗来说,递归算法的实质是把问题分解成规模缩小的同类问题的子问题,然后递归调用方法来表示问题的解。它有如下特点:
1. 一个问题的解可以分解为几个子问题的解
2. 这个问题与分解之后的子问题,除了数据规模不同,求解思路完全一样
3. 存在递归终止条件,即必须有一个明确的递归结束条件,称之为递归出口。
典型例题1:汉诺塔问题
5、回溯算法
待补充:
查找算法
排序算法
搜索算法
字符串匹配算法
接下来是各种数据结构:
线性表、散列表、树、图
参考资料:
内容来自:https://zhuanlan.zhihu.com/p/137041568