1、数组
1、快排
def quick_sort(alist, start, end):
"""快速排序"""
if start >= end: # 递归的退出条件
return
mid = alist[start] # 设定起始的基准元素
low = start # low为序列左边在开始位置的由左向右移动的游标
high = end # high为序列右边末尾位置的由右向左移动的游标
while low < high:
# 如果low与high未重合,high(右边)指向的元素大于等于基准元素,则high向左移动
while low < high and alist[high] >= mid:
high -= 1
alist[low] = alist[high] # 走到此位置时high指向一个比基准元素小的元素,将high指向的元素放到low的位置上,此时high指向的位置空着,接下来移动low找到符合条件的元素放在此处
# 如果low与high未重合,low指向的元素比基准元素小,则low向右移动
while low < high and alist[low] < mid:
low += 1
alist[high] = alist[low] # 此时low指向一个比基准元素大的元素,将low指向的元素放到high空着的位置上,此时low指向的位置空着,之后进行下一次循环,将high找到符合条件的元素填到此处
# 退出循环后,low与high重合,此时所指位置为基准元素的正确位置,左边的元素都比基准元素小,右边的元素都比基准元素大
alist[low] = mid # 将基准元素放到该位置,
# 对基准元素左边的子序列进行快速排序
quick_sort(alist, start, low - 1) # start :0 low -1 原基准元素靠左边一位
# 对基准元素右边的子序列进行快速排序
quick_sort(alist, low + 1, end) # low+1 : 原基准元素靠右一位 end: 最后
if __name__ == '__main__':
alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
quick_sort(alist, 0, len(alist) - 1)
print(alist)
2、归并排序
def merge(a, b):
c = []
h = j = 0
while j < len(a) and h < len(b):
if a[j] < b[h]:
c.append(a[j])
j += 1
else:
c.append(b[h])
h += 1
if j == len(a):
for i in b[h:]:
c.append(i)
else:
for i in a[j:]:
c.append(i)
return c
def merge_sort(lists):
if len(lists) <= 1:
return lists
middle = len(lists)//2
left = merge_sort(lists[:middle])
right = merge_sort(lists[middle:])
return merge(left, right)
if __name__ == '__main__':
a = [14, 2, 34, 43, 21, 19]
print (merge_sort(a))
1、二分查找
1、例子
class Solution(object):
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
left = 0
right = len(nums)-1
while left<=right:
mid = (left+right)/2
if nums[mid]==target:
return mid
elif nums[mid]<target:
left = mid+1
else:
right = mid-1
return -1
2、x 的平方根
在[0,x]之间使用二分查找寻找算数平方根,看算术平方根平方后是不是这个数,如果正好是就返回,如果不是就返回left-1
class Solution(object):
def mySqrt(self, x):
"""
:type x: int
:rtype: int
"""
left = 0
right = x
while left<=right:
mid = int((left+right)/2)
if mid*mid>x:
right = mid-1
elif mid*mid==x:
return mid
else:
left = mid+1
return left-1
3、搜索选择排序数列
**二分法:将数组从中间分开分成两部分,一定有一部分是有序的。
- 如果左边是有序的
-
- target大小在左边数组大小之内,我们在左边搜
-
- 否则到右边搜
- 如果右边是有序的
-
- target大小在右边数组大小之内,我们在右边搜
-
- 否则到左边搜**
class Solution(object):
def helper(self,left,right,nums):
if left>right:
return -1
mid = (right-left)/2+left
if nums[left] == self.target:
return left
if nums[right] == self.target:
return right
if nums[mid] == self.target:
return mid
if nums[left]<nums[mid]:
if self.target>nums[left] and self.target<nums[mid]:
return self.helper(left+1,mid-1,nums)
else:
return self.helper(mid+1,right-1,nums)
else:
if self.target>nums[mid] and self.target<nums[right]:
return self.helper(mid+1,right-1,nums)
else:
return self.helper(left+1,mid-1,nums)
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
self.target = target
return self.helper(0,len(nums)-1,nums)
2、双指针
1、移除元素
使用快慢指针,初始都指在0,快指针依次加过去,但不能找过这个数组的大小
(1)如果快指针所指元素不等于要找的数,慢指针的数就等于快指针现在指的数,然后慢指针向前移动
(2)如果是要找的数,慢指针不懂,快指针往前走。
class Solution(object):
def removeElement(self, nums, val):
"""
:type nums: List[int]
:type val: int
:rtype: int
"""
slow = 0
fast = 0
size = len(nums)
while fast<size:
if nums[fast]!=val:
nums[slow] = nums[fast]
slow += 1
fast += 1
return slow
2、移动零
快慢指针,快指针依次过去,指到非0值,慢指针等于这个非0值并且加1.
然后记得补充完整
class Solution(object):
def moveZeroes(self, nums):
"""
:type nums: List[int]
:rtype: None Do not return anything, modify nums in-place instead.
"""
slow = 0
fast = 0
size = len(nums)
while fast<size:
if nums[fast]!=0:
nums[slow] = nums[fast]
slow += 1
fast += 1
for i in range(slow,size):
nums[i] = 0
return nums
3、滑动窗口
1、长度最小的子数组
右指针不停往前走,当然要小于这个数组,sum也依次加下去,加到大于等于target的时候,左指针开始动,左指针一边往前走一边sum要减去不在这个滑动窗口中的值,并且计算这个滑动窗口的size,取所有可能的size的最小值,直到sum比target小。
class Solution(object):
def minSubArrayLen(self, target, nums):
"""
:type target: int
:type nums: List[int]
:rtype: int
"""
left = 0
right = 0
Sum = 0
minSize = 10e9
while right<len(nums) and left<=right:
Sum += nums[right]
right += 1
while Sum>=target:
minSize = min(minSize,right-left)
Sum -= nums[left]
left += 1
if minSize==10e9:
return 0
return minSize
2、水果成篮
随着右指针往前走,字典中的水果开始增加(包括水果种类,和水果个数),当水果种类超过2个的时候,左指针开始走,这个时候字典里的水果开始减少,如果字典里的水果减少到0的时候去掉这个种类,直到水果种类不超过2个,然后计算右指针和左指针的距离,得出一个最大距离。
class Solution(object):
def totalFruit(self, fruits):
"""
:type fruits: List[int]
:rtype: int
"""
left = 0
right = 0
maxNum = 0
Dict = defaultdict(int)
count = 0
while right<len(fruits):
Dict[fruits[right]]+=1
right += 1
while len(Dict)>2:
Dict[fruits[left]] -= 1
if Dict[fruits[left]]==0:
Dict.pop(fruits[left])
left += 1
maxNum = max(maxNum,right-left)
return maxNum
3、最长连续序列
首先找到我想要找的连续序列的最小值x,也就是说x-1不能出现在数组中。找到x后,这时的长度是1,依次增加x的值看是否在这个数组中,在的话长度加1,不在就拉到了,最终得到最长的长度。
class Solution:
def longestConsecutive(self, nums):
nums_set = set(nums)
maxLen = 0
for num in nums_set:
if num-1 not in nums_set:
curnum = num
length = 1
while curnum+1 in nums_set:
curnum += 1
length += 1
maxLen = max(maxLen,length)
return maxLen
4、四数之和
首先对数组排序,num1和num2一前一后初始化,num3和num4在num2的右边数组。一个是该数组的第一个数,一个是该数组的最后一个数,如果相加大了,右边那个往左移,如果相加小了,左边那个往右移。
class Solution(object):
def fourSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[List[int]]
"""
res = []
nums = sorted(nums)
print(nums)
for i in range(len(nums)-2):
for j in range(i+1,len(nums)-1):
num1 = nums[i]
num2 = nums[j]
left = j+1
right = len(nums)-1
while left<right:
if num1+num2+nums[left]+nums[right]>target:
right -= 1
elif num1+num2+nums[left]+nums[right]<target:
left += 1
else:
num = [num1,num2,nums[left],nums[right]]
if num not in res:
res.append(num)
right -= 1
left += 1
return res
5、四数之和II
对于nums1和nums2暴力组合得到DictAB,对于nums3和nums4暴力组合得到DictCD,然后组合DictAB和DictCD。
class Solution(object):
def fourSumCount(self, nums1, nums2, nums3, nums4):
"""
:type nums1: List[int]
:type nums2: List[int]
:type nums3: List[int]
:type nums4: List[int]
:rtype: int
"""
DictAB = defaultdict(int)
DictCD = defaultdict(int)
res = 0
for i in range(len(nums1)):
for j in range(len(nums2)):
num = nums1[i]+nums2[j]
DictAB[num] += 1
for i in range(len(nums3)):
for j in range(len(nums4)):
num = nums3[i]+nums4[j]
DictCD[num] += 1
for key_i in DictAB:
for key_j in DictCD:
if key_i+key_j==0:
res += DictCD[key_j]*DictAB[key_i]
return res
6、盛最多水的容器
i,j是底,高是min(height[i],height[j]),如果height[i]比height[j]小的话,i++,大的话j–。
class Solution(object):
def maxArea(self, height):
"""
:type height: List[int]
:rtype: int
"""
i = 0
j = len(height)-1
maxArea = 0
while i<j:
area = (j-i)*min(height[i],height[j])
maxArea = max(maxArea,area)
if height[i]<height[j]:
i += 1
else:
j -= 1
return maxArea
7、三数之和
固定一个值用前后指针往中间找去找另外两个值
class Solution(object):
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
nums = sorted(nums)
res = []
for i in range(len(nums)-1):
left = i+1
right = len(nums)-1
while left<right:
if nums[i]+nums[left]+nums[right]>0:
right -= 1
elif nums[i]+nums[left]+nums[right]<0:
left += 1
else:
RList = [nums[i],nums[left],nums[right]]
if RList not in res:
res.append(RList)
right -= 1
left += 1
return res
8、下一个排列(要背的)
题解:
class Solution(object):
def nextPermutation(self, nums):
"""
:type nums: List[int]
:rtype: None Do not return anything, modify nums in-place instead.
"""
flag = 0
for j in range(len(nums)-1,0,-1):
i = j-1
if nums[i]<nums[j]:
for k in range(len(nums)-1,j-1,-1):
if nums[i]<nums[k]:
tmp = nums[k]
nums[k] = nums[i]
nums[i] = tmp
break
flag =1
break
print(nums)
if not flag:
j = 0
nums_j = nums[j:len(nums)]
nums_j = list(reversed(nums_j))
print(flag)
print(nums_j)
p = 0
for n in range(j,len(nums)):
nums[n] = nums_j[p]
p += 1
return nums
10、在排序数组中查找元素的第一个和最后一个位置
二分法。
class Solution(object):
def searchRange(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
if len(nums)==0:
return [-1,-1]
left = 0
right = len(nums)-1
while nums[left]<target or nums[right]>target:
if nums[left]<target:
left += 1
if nums[right]>target:
right -= 1
if left>right:
return [-1,-1]
return [left,right]
11、旋转图像(要背的)
找规律
class Solution(object):
def rotate(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: None Do not return anything, modify matrix in-place instead.
"""
n = len(matrix)
for i in range(n/2):
for j in range((n+1)/2):
tmp = matrix[i][j]
matrix[i][j] = matrix[n-j-1][i]
matrix[n-j-1][i] = matrix[n-i-1][n-j-1]
matrix[n-i-1][n-j-1] = matrix[j][n-i-1]
matrix[j][n-i-1] = tmp
return matrix
12、字母异位词分组
使用dict处理
class Solution(object):
def groupAnagrams(self, strs):
"""
:type strs: List[str]
:rtype: List[List[str]]
"""
dict_str = {}
for i in range(len(strs)):
str_i = ''.join(sorted(strs[i]))
if str_i not in dict_str:
dict_str[str_i] = [strs[i]]
else:
dict_str[str_i] += [strs[i]]
res = []
for key in dict_str:
res.append(dict_str[key])
return res
13、合并区间
首先对区间排序,一个区间一个区间找下去,如果说还是第一个找或者上一个区间和这个区间不重合就要了这个区间,否则,看上一个区间和这一个区间的右边哪边大取大的那边得到新的区间,然后把上面那个区间换成新的区间。
class Solution:
def merge(self, intervals) :
res = []
intervals = sorted(intervals)
for interval in intervals:
if not res or res[-1][1]<interval[0]:
res.append(interval)
else:
res[-1][1] = max(res[-1][1],interval[1])
return res
14、颜色分类(不会!!!)
首先设置一个转换函数。想要找到3个值,分别是zero,i和two
想要
[0,zero]=0
[zero,i]=1
[two,len-1]=2
初始的时候,zero=-1,two在最右边,i在最左边。
当i小于等于two的时候。如果i所指的值=0,zero就往右移动一位,转换i和zero的值,并且
class Solution:
def sortColors(self, nums):
"""
Do not return anything, modify nums in-place instead.
"""
# all in [0, zero] = 0
# all in (zero, i) = 1
# all in (two, len - 1] = 2
def swap(nums, index1, index2):
nums[index1], nums[index2] = nums[index2], nums[index1]
size = len(nums)
if size < 2:
return
zero = -1
two = size - 1
i = 0
while i <= two:
if nums[i] == 0:
zero += 1
swap(nums, i, zero)
i += 1
elif nums[i] == 1:
i += 1
else:
swap(nums, i, two)
two -= 1
16、买卖股票的最佳时机
设定最小价格和最大利润,一个一个价格看过去,如果这个价格减去最小利润得到的利润比最大利润大,就把这个利润设成最大利润,如果这个价格比最小价格小,就把这个价格设置成最小价格。
class Solution:
def maxProfit(self, prices):
maxProfit = 0
minPrice = 10e5
for price in prices:
maxProfit = max(maxProfit,price-minPrice)
minPrice = min(minPrice,price)
return maxProfit
17、买卖任意多次股票,最大利润
一个一个往后找,
(1)如果值大于等于最小价格就卖掉,卖掉以后的最小价格就是这个价格啦,
(2)如果小于最小价格,最小价格就是这个价格啦
(3)如果只有一个值,或者没有值就直接返回0
def maxProfit(prices):
maxProfit = 0
if len(princes)<2:
return maxProfit
minPrice = prices[0]
for i in range(1,len(prices)):
price = prices[i]
if price>=minPrice:
maxProfit += price-minPrice
minPrice = price
return maxProfit
17、只出现一次的数字(要背的)
最大的难度是给字典排序,写法
num_dict = sorted(num_dict.items(),key = lambda x:x[1])
class Solution(object):
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
num_dict = {}
for i in range(len(nums)):
if nums[i] in num_dict:
num_dict[nums[i]] += 1
else:
num_dict[nums[i]] = 1
num_dict = sorted(num_dict.items(),key = lambda x:x[1])
return num_dict[0][0]
18、LRU 缓存
设定时间!!!!
class LRUCache(object):
def __init__(self, capacity):
"""
:type capacity: int
"""
self.capacity = capacity
self.time = 0
self.dict = {}
def get(self, key):
"""
:type key: int
:rtype: int
"""
if key not in self.dict:
return -1
self.time += 1
t,num = self.dict[key]
self.dict[key] = [self.time,num]
return num
def put(self, key, value):
"""
:type key: int
:type value: int
:rtype: None
"""
if len(self.dict)>=self.capacity and key not in self.dict:
t_min = self.time
for i in self.dict:
t,_ = self.dict[i]
if t<=t_min:
t_min = t
key_i = i
self.dict.pop(key_i)
self.time += 1
self.dict[key] = [self.time,value]
# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)
20、课程表
对于
[
a
i
,
b
i
]
[a_i,b_i]
[ai,bi]表示
b
i
−
>
a
i
b_i->a_i
bi−>ai,设定一个是入度list(大小等于课程数)和关系dict,对于这个例子,list[a_i]+=1,dict[b_i] = [a_i]。
找到那些入度为0的课程做成队列。
依次出队列,除了队列以后总课程数-1,得到这个课程的关系list,这个list里的所有课程入度-1,如果这些课程的入度-1后入度为0就加入到队列中。这么做直到队列为空位置。如果这时总课程数==0就返回True,否则就是False
from collections import defaultdict
class Solution(object):
def canFinish(self, numCourses, prerequisites):
"""
:type numCourses: int
:type prerequisites: List[List[int]]
:rtype: bool
"""
inDegree = [0]*numCourses
relationDict = defaultdict(list)
for i in prerequisites:
inDegree[i[0]]+=1
relationDict[i[1]].append(i[0])
queue = []
for i in range(len(inDegree)):
if inDegree[i]==0:
queue.append(i)
while queue:
numi = queue.pop(0)
numCourses -= 1
relationList = relationDict[numi]
for numj in relationList:
inDegree[numj] -= 1
if inDegree[numj]==0:
queue.append(numj)
return numCourses==0
21、除自身以外数组的乘积
做一个矩阵,这个矩阵每个位置对应的就是该数组前面所有值的成绩。
然后再从后面搜起,那个矩阵的值乘以nums后面的值。
class Solution(object):
def productExceptSelf(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
result = [0]*len(nums)
result[0] = 1
for i in range(1,len(nums)):
result[i] = nums[i-1]*result[i-1]
R = 1
for j in range(len(nums)-1,-1,-1):
result[j] *= R
R *= nums[j]
return result
22、搜索二维矩阵 II
i控制m,j控制n,不同的是i是从上往下,j是从右往左。
如果matrix[i][j]>target的时候j往左,
如果matrix[i][j]>target的时候i往下。
class Solution:
def searchMatrix(self, matrix, target):
m, n = len(matrix), len(matrix[0])
i, j = 0, n-1
while 0<=i<m and 0<=j<n and matrix[i][j] != target:
if matrix[i][j] > target:
j -= 1
else:
i += 1
return 0<=i<m and 0<=j<n and matrix[i][j] == target
23、完全平方数【背包问题,未】
完全平方数就是物品(可以无限件使用),凑个正整数n就是背包,问凑满这个背包最少有多少物品?就是动态规划问题。
(1)dp[j]:和为j的完全平方数的最少数量为dp[j]
(2)dp[j] 可以由dp[j - i * i]推出, dp[j - i * i] + 1 便可以凑成dp[j]。此时我们要选择最小的dp[j],所以递推公式:dp[j] = min(dp[j - i * i] + 1, dp[j]);
(3)dp[0]一定是0,为了推公式用的
(4)由于每次都要选最小的,所以非零下标的dp[j]一定要初始化为最大值才不会被覆盖
class Solution(object):
def numSquares(self, n):
"""
:type n: int
:rtype: int
"""
f = [0]*(n+1)
for i in range(1,n+1):
minn = 10e4
j = 1
while j*j<=i:
minn = min(minn,f[i-j*j])
j += 1
f[i] = minn+1
return f[n]
25、寻找重复数
class Solution(object):
def findDuplicate(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
Dict = {}
for num in nums:
if num not in Dict:
Dict[num] = 1
else:
return num
26、比特位计数
对于偶数,1的个数和它的一半一样;对于奇数,1的个数和它前面一个偶数的1的个数+1一样
class Solution(object):
def countBits(self, n):
"""
:type n: int
:rtype: List[int]
"""
res = [0]*(n+1)
for i in range(1,n+1):
if i%2==0:
res[i] = res[i/2]
else:
res[i] = res[i-1]+1
return res
27、前 K 个高频元素
主要就是字典排序的写法:
Dict_a = sorted(Dict.items(),key = lambda x:x[1],reverse = True)
class Solution(object):
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
Dict = {}
for num in nums:
if num not in Dict:
Dict[num] = 1
else:
Dict[num] += 1
res = []
Dict_a = sorted(Dict.items(),key = lambda x:x[1],reverse = True)[:k]
print(list(Dict_a))
for i in range(len(Dict_a)):
list_a = list(Dict_a[i])
res.append(list_a[0])
print(res)
return res
28、除法求值[wei ]
class Solution:
def calcEquation(self, equations, values, queries):
equationSize = len(equations)
unionfind = UnionFind(2 * equationSize)
# 第一步 预处理 将变量的值与id进行映射 方便编码
hash_dict = dict()
id = 0
for i in range(equationSize):
var1 = equations[i][0]
var2 = equations[i][1]
if var1 not in hash_dict and len(hash_dict) <= 2 * equationSize:
hash_dict[var1] = len(hash_dict)
if var2 not in hash_dict and len(hash_dict) <= 2 * equationSize:
hash_dict[var2] = len(hash_dict)
# 合并
# print(hash_dict[var1], hash_dict[var2], values[i])
unionfind.union(hash_dict[var1], hash_dict[var2], values[i])
# 做查询
queriesSize = len(queries) #
res = [0.0] * queriesSize # 结果
for i in range(queriesSize):
var1 = queries[i][0]
var2 = queries[i][1]
id1 = hash_dict.get(var1, -1)
id2 = hash_dict.get(var2, -1)
# print(var1, var2, id1, id2)
if id1 == -1 or id2 == -1:
res[i] = -1.0
else:
res[i] = unionfind.isConnected(id1, id2)
return res
class UnionFind:
def __init__(self, n):
# eg a / b = 2的表示方法
# a --> 0
# b --> 1
# self.parent[0] = 1
# self.weight[0] = 2
self.parent = [i for i in range(n)]
# 这题额外加入weight 数组
self.weight = [1.0 for i in range(n)] # i / i = 1.0
# 有没有老哥解释一下 为什么隔代路径压缩不行? 有几个示例没法通过 --> 懂了 因为调用isconnected的时候有些是直接输出结果 隔代压缩没法得到最后的除法的值 如果多次调用就可以 算是吸取一个教训。
# def find(self, x):
# while x != self.parent[x]:
# origin = self.parent[x]
# self.parent[x] = self.parent[self.parent[x]]
# self.weight[x] = self.weight[x] * self.weight[origin]
# x = self.parent[x]
# return x
def find(self, x):
if x != self.parent[x]:
origin = self.parent[x]
self.parent[x] = self.find(self.parent[x])
self.weight[x] = self.weight[x] * self.weight[origin]
return self.parent[x]
def union(self, x, y, value):
root_x = self.find(x)
root_y = self.find(y)
if root_x != root_y:
self.parent[root_x] = root_y
self.weight[root_x] = value * self.weight[y] / self.weight[x]
def isConnected(self, x, y):
root_x = self.find(x)
root_y = self.find(y)
if root_x == root_y:
return self.weight[x] / self.weight[y]
else:
return -1.0
29、根据身高重建队列
首先按照身高从大到小排,相同身高k小的站在前面。按照身高排序之后,优先按身高高的people的k来插入,后序插入节点也不会影响前面已经插入的节点,最终按照k的规则完成了队列。
class Solution:
def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]:
# 先按照h维度的身高顺序从高到低排序。确定第一个维度
# lambda返回的是一个元组:当-x[0](维度h)相同时,再根据x[1](维度k)从小到大排序
people.sort(key=lambda x: (-x[0], x[1]))
que = []
# 根据每个元素的第二个维度k,贪心算法,进行插入
# people已经排序过了:同一高度时k值小的排前面。
for p in people:
que.insert(p[1], p)#p[1]:对象obj需要插入的索引位置。
return que
30、找到所有数组中消失的数字
使用Dict
class Solution(object):
def findDisappearedNumbers(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
res = []
Dict = [0]*(len(nums)+1)
numsSet = list(set(nums))
for i in range(len(numsSet)):
Dict[numsSet[i]] = 1
print(Dict)
for i in range(1,len(nums)+1):
if Dict[i] == 0:
res.append(i)
return res
31、汉明距离(背出)
class Solution(object):
def hammingDistance(self, x, y):
"""
:type x: int
:type y: int
:rtype: int
"""
return bin(x^y).count("1")
32、目标和【】背包问题,未
动态规划01背包
假设加法的总和为x,那么减法对应的总和就是sum - x。
所以我们要求的是 x - (sum - x) = target
x = (target + sum) / 2
此时问题就转化为,装满容量为x的背包,有几种方法。
这里的x,就是bagSize,也就是我们后面要求的背包容量。
(1)如果target的绝对值大于sum或者(sum+target)是奇数都为0
(2)dp[j] 表示:填满j(包括j)这么大容积的包,有dp[j]种方法
(3)只要搞到nums[i],凑成dp[j]就有dp[j - nums[i]] 种方法。
(4)从递推公式可以看出,在初始化的时候dp[0] 一定要初始化为1,因为dp[0]是在公式中一切递推结果的起源,如果dp[0]是0的话,递推结果将都是0。
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
sumValue = sum(nums)
if abs(target) > sumValue or (sumValue + target) % 2 == 1:
return 0
bagSize = (sumValue + target) // 2
dp = [0] * (bagSize + 1)
dp[0] = 1
for i in range(len(nums)):
for j in range(bagSize, nums[i] - 1, -1):
dp[j] += dp[j - nums[i]]
return dp[bagSize]
33、 和为 K 的子数组
前缀和。假设nums的前缀和数组是sums,问题转换为:
求使得sums[j]-sums[i]=k的下标对(i,j)个数。
利用字典存储前缀和以及对应个数。枚举前缀和数组,当枚举到sums[j]时,Dict[sums[j]-k]=v,可知存在v个
a
i
,
j
a_{i,j}
ai,j,把v加入到最终答案即可,并将sums[j]更新。
class Solution:
def subarraySum(self, nums, k):
dic={0:1}
sums,res=0,0
for num in nums:
sums+=num
res+=dic.get(sums-k,0)#如果指定键的值不存在,返回该默认值0.
dic[sums]=dic.get(sums,0)+1
return res
34、最短无序连续子数组
**分两头开始遍历:
- 从左到右维护一个最大值max,在进入右段之前,那么遍历到的nums[i]都是小于max的,我们要求的end就是遍历中最后一个小于max元素的位置;
- 同理,从右到左维护一个最小值min,在进入左段之前,那么遍历到的nums[i]也都是大于min的,要求的begin也就是最后一个大于min元素的位置。**
class Solution(object):
def findUnsortedSubarray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
min_i = nums[len(nums)-1]
max_i = nums[0]
begin = 0
end = -1
for i in range(len(nums)):
if nums[i]<max_i:
end = i
else:
max_i = nums[i]
if nums[len(nums)-i-1]>min_i:
begin = len(nums)-i-1
else:
min_i = nums[len(nums)-i-1]
return end-begin+1
35、任务调度器(不会!!!)
class Solution:
def leastInterval(self, tasks, n):
freq = collections.Counter(tasks)
# 任务总数
m = len(freq)
nextValid = [1] * m
rest = list(freq.values())
time = 0
for i in range(len(tasks)):
time += 1
minNextValid = min(nextValid[j] for j in range(m) if rest[j] > 0)
time = max(time, minNextValid)
best = -1
for j in range(m):
if rest[j] and nextValid[j] <= time:
if best == -1 or rest[j] > rest[best]:
best = j
nextValid[best] = time + n + 1
rest[best] -= 1
return time
36、删除有序数组中的重复项
快慢指针~~~~
class Solution(object):
def removeDuplicates(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
slow = 1
fast = 1
pre = nums[0]
while fast<len(nums):
if nums[fast]!=pre:
nums[slow]=nums[fast]
slow += 1
pre = nums[fast]
fast += 1
return slow
36、合并两个排序数组
def merge(list1, list2):
res = []
i, j = 0,len(list2)-1
while i < len(list1) and j >= 0:
if list1[i] <= list2[j]:
res.append(list1[i])
i += 1
else:
res.append(list2[j])
j -= 1
# 两个数组长度不一致的时候
res += list1[i:]
res += list2[j:]
return res
class Solution(object):
def merge(self, nums1, m, nums2, n):
"""
:type nums1: List[int]
:type m: int
:type nums2: List[int]
:type n: int
:rtype: None Do not return anything, modify nums1 in-place instead.
"""
p = m+n-1
m,n = m-1,n-1
while n>=0:
while m>=0 and nums1[m]>nums2[n]:
nums1[p] = nums1[m]
p,m = p-1,m-1
nums1[p] = nums2[n]
p,n = p-1,n-1
2、链表
1、两两交换链表中的节点
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def swapPairs(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
res = ListNode(next=head)
pre = res
while pre.next and pre.next.next:
cur = pre.next
post = pre.next.next
cur.next = post.next
post.next = cur
pre.next = post
pre = pre.next.next
return res.next
2. 两个链表相加
题解:
(1)考虑哪个链表比较长
(2)对于短的链表没有节点的时候链表值为0
(3)在长的链表中要多一个p1,在p的前面,为了如果节点没有了,但是还要进位,指向后面的值。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def addTwoNumbers(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
p = l1
q = l2
while p and q:
p = p.next
q = q.next
#要求p长,q短
if q:
p = l2
q = l1
else:
p = l1
q = l2
head = p
tens = 0
while p:
if q:
q_val = q.val
else:
q_val = 0
num = p.val+q_val+tens
p.val = num%10
tens = num/10
p1 = p
p = p.next
if q:
q = q.next
if tens:
p1.next = ListNode(val = tens)
return head
3、删除链表的倒数第n个节点
题解:快慢指针,一个指针先走n步,然后两个指针一起走
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def removeNthFromEnd(self, head, n):
"""
:type head: ListNode
:type n: int
:rtype: ListNode
"""
p = head
q = head
while n:
p = p.next
n -= 1
if not p:
return head.next
while p and p.next:
p = p.next
q = q.next
if q.next:
q.next = q.next.next
else:
q.next = None
return head
4、合并两个有序链表
题解:找个节点,然后两个链表一起遍历过去拿小的值
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def mergeTwoLists(self, list1, list2):
"""
:type list1: Optional[ListNode]
:type list2: Optional[ListNode]
:rtype: Optional[ListNode]
"""
pre = ListNode()
p = pre
while list1 and list2:
if list1.val<=list2.val:
p.next = list1
list1 = list1.next
else:
p.next = list2
list2 = list2.next
p = p.next
if list1:
p.next = list1
if list2:
p.next = list2
return pre.next
5、环形链表
快慢指针,慢指针追上快指针说明有环,否则无环。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
quick = head
slow = head
while quick and quick.next:
quick = quick.next.next
slow = slow.next
if quick==slow:
return True
return False
6、环形链表II
(1)使用快慢指针判断是否有环,如果有环,慢指针需要走k步才能和快指针相遇
(2)让q、p同时指向head,先让q走k步,然后q、p一起往后走直到相遇,相遇节点就是需要查找的节点
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def detectCycle(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
quick = head
slow = head
k = 0
flag = 0
while quick and quick.next:
quick = quick.next.next
slow = slow.next
k += 1
if quick==slow:
flag = 1
break
if not flag:
return None
q = head
p = head
while k:
q = q.next
k -= 1
while q!=p:
q = q.next
p = p.next
return q
7、排序链表
先遍历一边把值存起来,然后对这些值进行排序,然后再重新构造链表。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def sortList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
p = head
List = []
while p:
List.append(p.val)
p = p.next
List = sorted(List)
p = head
for i in range(len(List)):
p.val = List[i]
p = p.next
return head
8、相交链表
(1)遍历得知两个链表的长度
(2)设置p为长链表,q为短链表
(3)p先走两个链表的差值长
(4)p和q一起慢慢往后走,直到相遇
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def getIntersectionNode(self, headA, headB):
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
p = headA
q = headB
LenA = 0
LenB = 0
while p or q:
if p:
p = p.next
LenA += 1
if q:
q = q.next
LenB +=1
if LenA<LenB:
p = headA
q = headB
else:
p = headB
q = headA
num = abs(LenA-LenB)
while num:
q = q.next
num -= 1
while p and q:
if p==q:
return p
p = p.next
q = q.next
return None
9、反转链表
(1)设置q=None,p指向head,p1指向head.next
(2)当p1不为空的时候,p.next=q,然后q、p、p1慢慢往后移动
(3)p.next = q
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def reverseList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
if not head or not head.next:
return head
q = None
p = head
pn = head.next
while pn:
p.next = q
q = p
p = pn
pn = pn.next
p.next = q
return p
10、回文链表
(1)遍历得到所有值
(2)查看list是否是回文list
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def isPalindrome(self, head):
"""
:type head: ListNode
:rtype: bool
"""
res = []
while head:
res.append(head.val)
head = head.next
res1 = res
res2 = res[::-1]
if res1 == res2:
return True
return False
3、字符串
1、找到字符串中所有字母异位词
做一个大小为p的滑动窗口,在s中滑动,每个滑动窗口都是Dict,里面有26个字母,慢慢滑动,与p做成的Dict值一样的时候,加入idex。
class Solution:
def findAnagrams(self, s, p):
s_len = len(s)
p_len = len(p)
if s_len<p_len:
return []
ans = []
s_count = [0]*26
p_count = [0]*26
for i in range(p_len):
s_count[ord(s[i])-97] += 1
p_count[ord(p[i])-97] += 1
if s_count==p_count:
ans.append(0)
for i in range(s_len-p_len):
s_count[ord(s[i])-97] -= 1
s_count[ord(s[i+p_len])-97] += 1
if s_count==p_count:
ans.append(i+1)
return ans
2、反转字符串中的单词
class Solution(object):
def reverseWords(self, s):
"""
:type s: str
:rtype: str
"""
return " ".join(reversed(s.split()))
3. 无重复字符的最长字串
题解:滑动窗口,设定一个长度为0的窗口从最左边开始,设定一个空的dict。从最左边开始遍历过去,
(1)如果这个值不在字典里,字典里的放入这个词,并且向右移动一位;
(2)如果这个字在字典里,记录的值也+1,字典里的value+1,也右移一位。
如果说记录的值不为0的话,左边的框框向右边移动
取一个最大的滑动窗口。
from collections import defaultdict
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
Dict = defaultdict(int)
left = 0
right = 0
count = 0
maxLen = 0
while right<len(s):
if Dict[s[right]]>0:
count += 1
Dict[s[right]] += 1
right += 1
while count:
if Dict[s[left]]>1:
count -= 1
Dict[s[left]] -= 1
left += 1
maxLen = max(maxLen,right-left)
return maxLen
4、最长回文子串
题解:中心扩展法:中心位置可能是一个字符,也可能是两个相邻的字符。
class Solution(object):
def helper(self,s,i,j):
while i >= 0 and j < len(s) and s[i] == s[j]:
i -= 1
j += 1
return j-i-1
def longestPalindrome(self, s):
"""
:type s: str
:rtype: str
"""
if len(s)<2:
return s
maxLen = 0
max_i = 0
for i in range(0,len(s)-1):
oddLen = self.helper(s,i,i)
evenLen = self.helper(s,i,i+1)
Len = max(maxLen,oddLen,evenLen)
if maxLen<Len:
maxLen = Len
max_i = i
left = max_i-(maxLen-1)/2
right = max_i + maxLen/2+1
return s[left:right]
5、电话号码的字母组合
一个一个找过去,如果没有了就把str并到res里,如果有的话,一个一个可能性并过去。,回溯~!!
class Solution:
def letterCombinations(self, digits):
if not digits: return []
phone = {'2':['a','b','c'],
'3':['d','e','f'],
'4':['g','h','i'],
'5':['j','k','l'],
'6':['m','n','o'],
'7':['p','q','r','s'],
'8':['t','u','v'],
'9':['w','x','y','z']}
def backtrack(conbination,nextdigit):
if len(nextdigit) == 0:
res.append(conbination)
else:
for letter in phone[nextdigit[0]]:
backtrack(conbination + letter,nextdigit[1:])
res = []
backtrack('',digits)
return res
6、有效的括号
题解:如果遇到左括号就入栈,遇到右括号栈pop进行比较,如果最后栈里还有元素就False,如果栈里没有了就True
class Solution(object):
def isValid(self, s):
"""
:type s: str
:rtype: bool
"""
stack = []
if len(s)<2:
return False
for i in range(len(s)):
if s[i] in ['(','{','[']:
stack.append(s[i])
else:
if stack:
Str = stack.pop()
if (s[i] == ')' and Str in ['{','[']) or (s[i] == '}' and Str in ['(','[']) or (s[i] == ']' and Str in ['{','(']):
return False
else:
return False
if stack:
return False
else:
return True
7、括号生成
题解:回溯法。
class Solution(object):
def helper(self,left,right,tmp):
if left<0 or right<0 or left>right:
return
if left==0 and right==0:
self.ans.append(tmp)
return
tmp += "("
self.helper(left-1,right,tmp)
tmp = tmp[:len(tmp)-1]
tmp += ")"
self.helper(left,right-1,tmp)
def generateParenthesis(self, n):
"""
:type n: int
:rtype: List[str]
"""
self.ans = []
tmp = ''
self.helper(n,n,tmp)
return self.ans
8、字符串解码
一个一个找过去,
(1)遇到数字就该数字+前一个数字10,
(2)如果遇到字母就合并到之前的字母上
(3)如果遇到"[“,就把之前的数字和字母存入stack,并设置数字为0,字母为空
(4)这个时候再遇到字母,字母就是res
(4)当遇到”]",就把之前存入的数字和字母拿出来,分别叫cur_multi和last_res,得到res=last_res+cur_multres
class Solution(object):
def decodeString(self, s):
"""
:type s: str
:rtype: str
"""
stack = []
res = ""
multi = 0
for c in s:
if c=="[":
stack.append([multi,res])
res,multi = "",0
elif c == ']':
cur_multi,last_res = stack.pop()
res = last_res+cur_multi*res
elif '0'<=c<='9':
multi = multi*10+int(c)
else:
res += c
return res
10、回文子串
(1)dp数组定义:dp[i][j]表示s[i:j+1]是不是回文串?
(2)判断s[i]==s[j]?
-
- 情况1:i==j:a是回文子串
-
- 情况2:j-i==1:aa是回文子串
-
- 情况3:看dp[i+1][j-1]是不是回文子串
(3)初始化:dp[i][j]=False
(4)遍历:
从左下角推到右上角。所以应该从下往上、从左往右去遍历。
- 情况3:看dp[i+1][j-1]是不是回文子串
class Solution(object):
def countSubstrings(self, s):
"""
:type s: str
:rtype: int
"""
length = len(s)
dp = [[0]*length for _ in range(length)]
result = 0
for i in range(length-1,-1,-1):
for j in range(i,length):
if s[i] == s[j]:
if (j-i) <=1:
result +=1
dp[i][j] = 1
elif (dp[i+1][j-1]):
result += 1
dp[i][j] = 1
return result
4、栈
1、用栈实现队列
class MyQueue:
def __init__(self):
"""
in主要负责push,out主要负责pop
"""
self.stack_in = []
self.stack_out = []
def push(self, x):
"""
有新元素进来,就往in里面push
"""
self.stack_in.append(x)
def pop(self):
"""
Removes the element from in front of queue and returns that element.
"""
if self.empty():
return None
if self.stack_out:
return self.stack_out.pop()
else:
for i in range(len(self.stack_in)):
self.stack_out.append(self.stack_in.pop())
return self.stack_out.pop()
def peek(self):
"""
Get the front element.
"""
ans = self.pop()
self.stack_out.append(ans)
return ans
def empty(self):
"""
只要in或者out有元素,说明队列不为空
"""
return not (self.stack_in or self.stack_out)
2、逆波兰表达式求值(必须用python3)
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
numStack = []
sym = ['+','-','*','/']
for i in range(len(tokens)):
if tokens[i] not in sym:
numStack.append(int(tokens[i]))
print(numStack)
else:
num1 = numStack.pop()
num2 = numStack.pop()
if tokens[i]=="+":
num3 = num1+num2
elif tokens[i]=='-':
num3 = num2-num1
elif tokens[i]=='*':
num3 = num2*num1
elif tokens[i]=='/':
num3 = int(num2/num1)
numStack.append(num3)
return numStack.pop()
3、最小栈
class MinStack(object):
def __init__(self):
self.stack = []
def push(self, val):
"""
:type val: int
:rtype: None
"""
self.stack.append(val)
def pop(self):
"""
:rtype: None
"""
self.stack.pop()
def top(self):
"""
:rtype: int
"""
return self.stack[len(self.stack)-1]
def getMin(self):
"""
:rtype: int
"""
inf = int(2e31)
min_i = inf
for i in range(len(self.stack)):
if self.stack[i]<=min_i:
min_i = self.stack[i]
return min_i
# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(val)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()
4、每日温度
单调栈递增,得到第一个比当前元素大的元素
单调栈递减,得到第一个比当前元素小的元素
class Solution(object):
def dailyTemperatures(self, temperatures):
"""
:type temperatures: List[int]
:rtype: List[int]
"""
stack = [0]
result = [0]*len(temperatures)
for i in range(1,len(temperatures)):
if temperatures[i]<=temperatures[stack[-1]]:
stack.append(i)
else:
while len(stack)!=0 and temperatures[i]>temperatures[stack[-1]]:
result[stack[-1]] = i-stack[-1]
stack.pop()
stack.append(i)
return result
5、队列
1、用队列实现栈
class MyStack:
def __init__(self):
self.que = deque()
def push(self, x):
self.que.append(x)
def pop(self):
if self.empty():
return None
for i in range(len(self.que)-1):
self.que.append(self.que.popleft())
return self.que.popleft()
def top(self):
if self.empty():
return None
return self.que[-1]
def empty(self):
return not self.que
6、二叉树
1、二叉树的递归遍历
1、前序
当节点为空的时候返回,当节点不为空时,先得到节点的值,再去遍历它的左子树和它的右子树。
# 前序遍历-递归-LC144_二叉树的前序遍历
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
# 保存结果
result = []
def traversal(root: TreeNode):
if root == None:
return
result.append(root.val) # 前序
traversal(root.left) # 左
traversal(root.right) # 右
traversal(root)
return result
2、中序
当节点为空的时候返回,当节点不为空时,先去遍历它的左子树,再得到节点的值,再去它的右子树。
# 中序遍历-递归-LC94_二叉树的中序遍历
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
result = []
def traversal(root: TreeNode):
if root == None:
return
traversal(root.left) # 左
result.append(root.val) # 中序
traversal(root.right) # 右
traversal(root)
return result
3、后序
当节点为空的时候返回,当节点不为空时,先去遍历它的左子树,再去遍历它的右子树得到节点的值,再去它的右子树。
# 后序遍历-递归-LC145_二叉树的后序遍历
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
result = []
def traversal(root: TreeNode):
if root == None:
return
traversal(root.left) # 左
traversal(root.right) # 右
result.append(root.val) # 后序
traversal(root)
return result
2、迭代遍历
1、迭代法前序遍历:
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
result = []
st= []
if root:
st.append(root)
while st:
node = st.pop()
if node != None:
if node.right: #右
st.append(node.right)
if node.left: #左
st.append(node.left)
st.append(node) #中
st.append(None)
else:
node = st.pop()
result.append(node.val)
return result
2、迭代法中序遍历:
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
result = []
st = []
if root:
st.append(root)
while st:
node = st.pop()
if node != None:
if node.right: #添加右节点(空节点不入栈)
st.append(node.right)
st.append(node) #添加中节点
st.append(None) #中节点访问过,但是还没有处理,加入空节点做为标记。
if node.left: #添加左节点(空节点不入栈)
st.append(node.left)
else: #只有遇到空节点的时候,才将下一个节点放进结果集
node = st.pop() #重新取出栈中元素
result.append(node.val) #加入到结果集
return result
3、迭代法后序遍历:
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
result = []
st = []
if root:
st.append(root)
while st:
node = st.pop()
if node != None:
st.append(node) #中
st.append(None)
if node.right: #右
st.append(node.right)
if node.left: #左
st.append(node.left)
else:
node = st.pop()
result.append(node.val)
return result
3、二叉树层序遍历
1、层序遍历
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def levelOrder(self, root):
"""
:type root: TreeNode
:rtype: List[List[int]]
"""
results = []
if not root:
return results
from collections import deque
que = deque([root])
while que:
size = len(que)
result = []
for _ in range(size):
cur = que.popleft()
result.append(cur.val)
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
results.append(result)
return results
2、二叉树的右视图
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def rightSideView(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
results = []
if not root:
return results
que = [root]
while que:
results.append(que[-1].val)
for _ in range(len(que)):
node = que.pop(0)
if node.left:
que.append(node.left)
if node.right:
que.append(node.right)
return results
3、填充每个节点的下一个右侧节点指针 II
"""
# Definition for a Node.
class Node(object):
def __init__(self, val=0, left=None, right=None, next=None):
self.val = val
self.left = left
self.right = right
self.next = next
"""
class Solution(object):
def connect(self, root):
if not root:
return root
que = [root]
while que:
result = []
pre = None
for i in range(len(que)):
cur = que.pop(0)
if result:
pre = result.pop()
pre.next = cur
result.append(cur)
if cur.left:
que.append(cur.left)
if cur.right:
que.append(cur.right)
return root
4、二叉树的最大深度
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def maxDepth(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if not root:
return 0
return max(self.maxDepth(root.left),self.maxDepth(root.right))+1
5、二叉树的最小深度
如果没有左子树或者没有右子树,就取左右子树的最大值+1;
否则,就取左右子树的最小值+1
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def minDepth(self, root):
if root == None:
return 0
left = self.minDepth(root.left)
right = self.minDepth(root.right)
if left == 0 or right == 0:
return max(left, right) + 1
else:
return min(left, right) + 1
6、翻转二叉树
class Solution:
def invertTree(self,root):
if not root:
return
root.left,root.right = root.right,root.left
self.invertTree(root.left)
self.invertTree(root.right)
return root
7、完全二叉树的节点个数
num=左子树num+右子树num+1
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def countNodes(self, root):
"""
:type root: TreeNode
:rtype: int
"""
num = 0
if not root:
return num
num = 1+self.countNodes(root.left)+self.countNodes(root.right)
return num
8、平衡二叉树
后序求高度(3节点的高度是2),前序求深度(3节点的深度是1)
当我们发现有节点不是平衡二叉树时就返回-1,表示老早就不是平衡二叉树了。
空节点height=0
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def get_height(self,root):
if not root:
return 0
left_height = self.get_height(root.left)
right_height = self.get_height(root.right)
if left_height==-1:
return -1
if right_height==-1:
return -1
if abs(left_height-right_height)>1:
return -1
else:
return 1+max(left_height,right_height)
def isBalanced(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
if self.get_height(root)!=-1:
return True
else:
return False
9、二叉树的所有路径
没有到叶子节点就找下去,到了就不找了
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def traversal(self,cur,path,result):
path += str(cur.val)
if not cur.left and not cur.right:
result.append(path)
if cur.left:
self.traversal(cur.left,path+'->',result)
if cur.right:
self.traversal(cur.right,path+'->',result)
def binaryTreePaths(self, root):
"""
:type root: TreeNode
:rtype: List[str]
"""
path = ''
result = []
if not root:
return result
self.traversal(root,path,result)
return result
10、左叶子之和
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def sumOfLeftLeaves(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if not root:
return 0
left_left_leaves_sum = self.sumOfLeftLeaves(root.left)
right_left_leaves_sum = self.sumOfLeftLeaves(root.right)
cur_left_leaf_val = 0
if root.left and not root.left.left and not root.left.right:
cur_left_leaf_val = root.left.val
return cur_left_leaf_val+left_left_leaves_sum+right_left_leaves_sum
11、路径总和
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def traversal(self,cur,count):
if not cur.left and not cur.right and count==0:
return True
if not cur.left and not cur.right:
return False
if cur.left:
count -= cur.left.val
if self.traversal(cur.left,count):
return True
count += cur.left.val
if cur.right:
count -= cur.right.val
if self.traversal(cur.right,count):
return True
count += cur.right.val
return False
def hasPathSum(self, root, targetSum):
"""
:type root: TreeNode
:type targetSum: int
:rtype: bool
"""
if not root:
return False
return self.traversal(root,targetSum-root.val)
12、从中序与后序遍历序列构造二叉树
class Solution:
def buildTree(self, inorder, postorder):
# 第一步: 特殊情况讨论: 树为空. 或者说是递归终止条件
if not postorder:
return
# 第二步: 后序遍历的最后一个就是当前的中间节点
root_val = postorder[-1]
root = TreeNode(root_val)
# 第三步: 找切割点.
root_index = inorder.index(root_val)
# 第四步: 切割inorder数组. 得到inorder数组的左,右半边.
left_inorder = inorder[:root_index]
right_inorder = inorder[root_index + 1:]
# 第五步: 切割postorder数组. 得到postorder数组的左,右半边.
# ⭐️ 重点1: 中序数组大小一定跟后序数组大小是相同的.
left_postorder = postorder[:len(left_inorder)]
right_postorder = postorder[len(left_inorder): len(postorder) - 1]
# 第六步: 递归
root.left = self.buildTree(left_inorder, left_postorder)
root.right = self.buildTree(right_inorder, right_postorder)
# 第七步: 返回答案
return root
13、最大二叉树
class Solution:
"""递归法 更快"""
def constructMaximumBinaryTree(self, nums):
if not nums:
return None
maxvalue = max(nums)
index = nums.index(maxvalue)
root = TreeNode(maxvalue)
left = nums[:index]
right = nums[index + 1:]
root.left = self.constructMaximumBinaryTree(left)
root.right = self.constructMaximumBinaryTree(right)
return root
14、验证二叉搜索树
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def isValidBST(self, root, left=-2e31, right=2e31-1):
if root is None:
return True
x = root.val
return left < x < right and \
self.isValidBST(root.left, left, x) and \
self.isValidBST(root.right, x, right)
15、二叉搜索树中的插入操作
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def insertIntoBST(self, root, val):
"""
:type root: TreeNode
:type val: int
:rtype: TreeNode
"""
if not root:
return TreeNode(val)
if val<root.val:
root.left = self.insertIntoBST(root.left,val)
if root.val<val:
root.right = self.insertIntoBST(root.right,val)
return root
16、删除二叉搜索树中的节点
- 当前节点的左子树为空,返回当前的右子树
- 当前节点的右子树为空,返回当前的左子树
- 左右子树都不为空,找到右孩子的最左节点 记为p
- 将当前节点的左子树挂在p的左孩子上
- 当前节点的右子树替换掉当前节点,完成当前节点的删除
class Solution:
def deleteNode(self, root, key):
if not root :
return None # 节点为空,返回
if root.val < key :
root.right = self.deleteNode(root.right, key)
elif root.val > key :
root.left = self.deleteNode(root.left, key)
else:
# 当前节点的左子树为空,返回当前的右子树
if not root.left :
return root.right
# 当前节点的右子树为空,返回当前的左子树
if not root.right:
return root.left
# 左右子树都不为空,找到右孩子的最左节点 记为p
node = root.right
while node.left :
node = node.left
# 将当前节点的左子树挂在p的左孩子上
node.left = root.left
# 当前节点的右子树替换掉当前节点,完成当前节点的删除
root = root.right
return root
17、修剪二叉搜索树
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def trimBST(self, root, low, high):
'''
确认递归函数参数以及返回值:返回更新后剪枝后的当前root节点
'''
# Base Case
if not root: return None
# 单层递归逻辑
if root.val < low:
# 若当前root节点小于左界:只考虑其右子树,用于替代更新后的其本身,抛弃其左子树整体
return self.trimBST(root.right, low, high)
if high < root.val:
# 若当前root节点大于右界:只考虑其左子树,用于替代更新后的其本身,抛弃其右子树整体
return self.trimBST(root.left, low, high)
if low <= root.val <= high:
root.left = self.trimBST(root.left, low, high)
root.right = self.trimBST(root.right, low, high)
# 返回更新后的剪枝过的当前节点root
return root
18、将有序数组转换为二叉搜索树
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def traversal(self,nums,left,right):
if left>right:
return None
mid = (right+left)//2
mid_root = TreeNode(nums[mid])
mid_root.left = self.traversal(nums,left,mid-1)
mid_root.right = self.traversal(nums,mid+1,right)
return mid_root
def sortedArrayToBST(self, nums):
"""
:type nums: List[int]
:rtype: TreeNode
"""
root = self.traversal(nums,0,len(nums)-1)
return root
19、二叉搜索树的种类
class Solution(object):
def numTrees(self, n):
"""
:type n: int
:rtype: int
"""
G = [0]*(n+1)
G[0] = 1
G[1] = 1
for i in range(2,n+1):
for j in range(1,i+1):
G[i] += G[j-1]*G[i-j]
return G[n]
20、对称二叉树
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def dfs(self,left,right):
if not left and not right:
return True
if not left or not right:
return False
if left.val!=right.val:
return False
return self.dfs(left.right,right.left) and self.dfs(left.left,right.right)
def isSymmetric(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
if not root:
return None
return self.dfs(root.left,root.right)
21、从前序与中序遍历序列构造二叉树
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def helper(self,preorder,preLeft,preRight,InDict,inLeft,inRight):
if preLeft>preRight or inLeft>inRight:
return
num = preorder[preLeft]
root = TreeNode(num)
pIndex = InDict[num]
root.left = self.helper(preorder,preLeft+1,pIndex+preLeft-inLeft,InDict,inLeft,pIndex-1)
root.right = self.helper(preorder,pIndex+preLeft-inLeft+1,preRight,InDict,pIndex+1,inRight)
return root
def buildTree(self, preorder, inorder):
"""
:type preorder: List[int]
:type inorder: List[int]
:rtype: TreeNode
"""
InDict = {}
for i in range(len(inorder)):
InDict[inorder[i]] = i
return self.helper(preorder,0,len(preorder)-1,InDict,0,len(preorder)-1)
22、二叉树展开为链表
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def inOrder(self,root):
if root:
self.res.append(root.val)
else:
return
self.inOrder(root.left)
self.inOrder(root.right)
def flatten(self, root):
"""
:type root: TreeNode
:rtype: None Do not return anything, modify root in-place instead.
"""
self.res = []
if not root:
return self.res
self.inOrder(root)
p = root
for i in range(1,len(self.res)):
if p.left:
p.left = None
if p.right:
p.right.val = self.res[i]
else:
p.right = TreeNode(self.res[i])
p = p.right
return root
23、实现 Trie (前缀树)(不会!!!)
class Node(object):
def __init__(self):
self.children = collections.defaultdict(Node)
self.isword = False
class Trie(object):
def __init__(self):
self.root = Node()
def insert(self, word):
current = self.root
for w in word:
current = current.children[w]
current.isword = True
def search(self, word):
current = self.root
for w in word:
current = current.children.get(w)
if current == None:
return False
return current.isword
def startsWith(self, prefix):
current = self.root
for w in prefix:
current = current.children.get(w)
if current == None:
return False
return True
24、二叉树的最近公共祖先
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
if not root:
return
if root==p or root==q:
return root
root.left = self.lowestCommonAncestor(root.left,p,q)
root.right = self.lowestCommonAncestor(root.right,p,q)
if not root.left:
return root.right
if not root.right:
return root.left
return root
25、路径总和 III(不会)
class Solution:
def pathSum(self, root, targetSum):
def rootSum(root, targetSum):
if root is None:
return 0
ret = 0
if root.val == targetSum:
ret += 1
ret += rootSum(root.left, targetSum - root.val)
ret += rootSum(root.right, targetSum - root.val)
return ret
if root is None:
return 0
ret = rootSum(root, targetSum)
ret += self.pathSum(root.left, targetSum)
ret += self.pathSum(root.right, targetSum)
return ret
26、把二叉搜索树转换为累加树
倒序遍历相加
之前是“左中右”的顺序
现在是“右中左”的顺序
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def traversal(self,cur):
if not cur:
return
self.traversal(cur.right)
cur.val += self.pre
self.pre = cur.val
self.traversal(cur.left)
def convertBST(self, root):
"""
:type root: TreeNode
:rtype: TreeNode
"""
self.pre = 0
self.traversal(root)
return root
27、二叉树的直径(不会)
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def depth(self,root):
if not root:
return 0
L = self.depth(root.left)
R = self.depth(root.right)
self.ans = max(self.ans,L+R+1)
return max(L,R)+1
def diameterOfBinaryTree(self, root):
"""
:type root: TreeNode
:rtype: int
"""
self.ans = 1
self.depth(root)
return self.ans-1
7、回溯法
1、组合总和
回溯法
class Solution(object):
def helper(self,idx):
if sum(self.tmp)==self.target:
self.ans.append(self.tmp[:])
return
if sum(self.tmp)>self.target:
return
for i in range(idx,len(self.candidates)):
self.tmp.append(self.candidates[i])
self.helper(i)
self.tmp.pop()
def combinationSum(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
self.tmp = []
self.ans = []
self.candidates = sorted(candidates)
self.target = target
self.helper(0)
return self.ans
2、组合总和 II
class Solution(object):
def helper(self,idx,candidates):
if sum(self.tmp)==self.target:
if self.tmp not in self.ans:
self.ans.append(self.tmp[:])
return
if sum(self.tmp)>self.target:
return
for i in range(idx,len(candidates)):
self.tmp.append(candidates[i])
if i==0:
candidates_i = candidates[1:]
else:
candidates_i = candidates[:i]+candidates[i+1:]
self.helper(i,candidates_i)
self.tmp.pop()
def combinationSum2(self, candidates, target):
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
self.tmp = []
self.ans = []
self.target = target
candidates = sorted(candidates)
self.helper(0,candidates)
return self.ans
超时~~~~~~
# **回溯+巧妙去重(省去使用used**
class Solution:
def __init__(self):
self.paths = []
self.path = []
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
'''
类似于求三数之和,求四数之和,为了避免重复组合,需要提前进行数组排序
'''
self.paths.clear()
self.path.clear()
# 必须提前进行数组排序,避免重复
candidates.sort()
self.backtracking(candidates, target, 0, 0)
return self.paths
def backtracking(self, candidates: List[int], target: int, sum_: int, start_index: int) -> None:
# Base Case
if sum_ == target:
self.paths.append(self.path[:])
return
# 单层递归逻辑
for i in range(start_index, len(candidates)):
# 剪枝,同39.组合总和
if sum_ + candidates[i] > target:
return
# 跳过同一树层使用过的元素
if i > start_index and candidates[i] == candidates[i-1]:
continue
sum_ += candidates[i]
self.path.append(candidates[i])
self.backtracking(candidates, target, sum_, i+1)
self.path.pop() # 回溯,为了下一轮for loop
sum_ -= candidates[i] # 回溯,为了下一轮for loop
# **回溯+去重(使用used)**
class Solution:
def __init__(self):
self.paths = []
self.path = []
self.used = []
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
'''
类似于求三数之和,求四数之和,为了避免重复组合,需要提前进行数组排序
本题需要使用used,用来标记区别同一树层的元素使用重复情况:注意区分递归纵向遍历遇到的重复元素,和for循环遇到的重复元素,这两者的区别
'''
self.paths.clear()
self.path.clear()
self.usage_list = [False] * len(candidates)
# 必须提前进行数组排序,避免重复
candidates.sort()
self.backtracking(candidates, target, 0, 0)
return self.paths
def backtracking(self, candidates: List[int], target: int, sum_: int, start_index: int) -> None:
# Base Case
if sum_ == target:
self.paths.append(self.path[:])
return
# 单层递归逻辑
for i in range(start_index, len(candidates)):
# 剪枝,同39.组合总和
if sum_ + candidates[i] > target:
return
# 检查同一树层是否出现曾经使用过的相同元素
# 若数组中前后元素值相同,但前者却未被使用(used == False),说明是for loop中的同一树层的相同元素情况
if i > 0 and candidates[i] == candidates[i-1] and self.usage_list[i-1] == False:
continue
sum_ += candidates[i]
self.path.append(candidates[i])
self.usage_list[i] = True
self.backtracking(candidates, target, sum_, i+1)
self.usage_list[i] = False # 回溯,为了下一轮for loop
self.path.pop() # 回溯,为了下一轮for loop
sum_ -= candidates[i] # 回溯,为了下一轮for loop
作者:代码随想录
链接:https://leetcode.cn/problems/combination-sum-ii/solutions/857552/dai-ma-sui-xiang-lu-dai-ni-xue-tou-hui-s-ig29/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
3、全排列
回溯
class Solution(object):
def helper(self,nums):
if len(nums)==0:
self.ans.append(self.tmp[:])
return
for i in range(len(nums)):
self.tmp.append(nums[i])
if i==0:
nums_i = nums[1:]
else:
nums_i = nums[:i]+nums[i+1:]
self.helper(nums_i)
self.tmp.pop()
def permute(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
self.tmp = []
self.ans = []
self.helper(nums)
return self.ans
4、全排列II
class Solution(object):
def helper(self,nums,used):
if len(self.tmp)==len(nums):
self.ans.append(self.tmp[:])
return
for i in range(len(nums)):
if not used[i]:
if i>0 and nums[i]==nums[i-1] and not used[i-1]:
continue
used[i] = 1
self.tmp.append(nums[i])
self.helper(nums,used)
self.tmp.pop()
used[i]=0
def permuteUnique(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
self.tmp = []
self.ans = []
used = [0]*len(nums)
nums = sorted(nums)
self.helper(nums,used)
return self.ans
5、子集
对于每个值都问它要不要加,加完以后判定一次,删掉以后再判定一次,当所有的值都问完了把它加到ans里。
class Solution(object):
def helper(self,idx,deepth):
if deepth == self.n:
self.ans.append(self.tmp[:])
return
self.tmp.append(self.nums[idx])
self.helper(idx+1,deepth+1)
self.tmp.pop()
self.helper(idx+1,deepth+1)
def subsets(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
self.ans = []
self.tmp = []
self.nums = nums
self.n = len(nums)
self.helper(0,0)
return self.ans
6、子集II
class Solution(object):
def helper(self,nums,index):
self.ans.append(self.tmp[:])
if index==len(nums):
return
for i in range(index,len(nums)):
if i>index and nums[i]==nums[i-1]:
continue
self.tmp.append(nums[i])
self.helper(nums,i+1)
self.tmp.pop()
def subsetsWithDup(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
self.ans = []
self.tmp = []
nums = sorted(nums)
self.helper(nums,0)
return self.ans
7、组合
class Solution(object):
def helper(self,index,num):
if len(self.tmp)==self.k:
# self.tmp = sorted(self.tmp)
if self.tmp not in self.ans:
self.ans.append(self.tmp[:])
return
for i in range(num+1,self.n+1):
self.tmp.append(i)
self.helper(index+1,i)
self.tmp.pop()
def combine(self, n, k):
"""
:type n: int
:type k: int
:rtype: List[List[int]]
"""
self.ans = []
self.tmp = []
self.n = n
self.k = k
self.helper(0,0)
return self.ans
8、组合总和 III
class Solution(object):
def helper(self,index,num):
if index==self.k and sum(self.tmp)==self.n:
self.ans.append(self.tmp[:])
return
if index>self.k or sum(self.tmp)>self.n:
return
for i in range(num+1,self.maxNum+1):
self.tmp.append(i)
self.helper(index+1,i)
self.tmp.pop()
def combinationSum3(self, k, n):
"""
:type k: int
:type n: int
:rtype: List[List[int]]
"""
self.tmp = []
self.ans = []
self.maxNum = 9
self.k = k
self.n = n
self.helper(0,0)
return self.ans
9、分割回文串
10、分割回文串
class Solution(object):
def helper(self,s,index):
if index>=len(s):
self.ans.append(self.tmp[:])
return
for i in range(index,len(s)):
text = s[index:i+1]
if text==text[::-1]:
self.tmp.append(text)
self.helper(s,i+1)
self.tmp.pop()
else:
continue
def partition(self, s):
"""
:type s: str
:rtype: List[List[str]]
"""
self.tmp = []
self.ans = []
self.helper(s,0)
return self.ans
11、复原 IP 地址
class Solution(object):
def helper(self,s,index):
if index>=len(s) and len(self.tmp)==4:
tmptext = '.'.join(self.tmp)
self.ans.append(tmptext)
return
if index>=len(s) or len(self.tmp)==4:
return
for i in range(index,len(s)):
text = s[index:i+1]
if (len(text)==1 and int(text)==0) or (text[0]!='0' and int(text)>0 and int(text)<=255):
self.tmp.append(text)
self.helper(s,i+1)
self.tmp.pop()
else:
continue
def restoreIpAddresses(self, s):
"""
:type s: str
:rtype: List[str]
"""
self.tmp = []
self.ans = []
self.helper(s,0)
return self.ans
12、递增子序列
class Solution(object):
def helper(self,nums,index):
if len(self.tmp)>=2 and self.tmp not in self.ans:
self.ans.append(self.tmp[:])
if index==len(nums):
return
for i in range(index+1,len(nums)):
if self.tmp and nums[i]<self.tmp[-1]:
continue
self.tmp.append(nums[i])
self.helper(nums,i)
self.tmp.pop()
def findSubsequences(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
self.ans = []
self.tmp = []
self.helper(nums,-1)
return self.ans
8、贪心算法
1、分发饼干
这里的局部最优就是大饼干喂给胃口大的,充分利用饼干尺寸喂饱一个,全局最优就是喂饱尽可能多的小孩。
被吃了才能往前走。
- 可以尝试使用贪心策略,先将饼干数组和小孩数组排序。
- 然后从后向前遍历小孩数组,用大饼干优先满足胃口大的,并统计满足小孩数量。
class Solution(object):
def findContentChildren(self, g, s):
"""
:type g: List[int]
:type s: List[int]
:rtype: int
"""
g = sorted(g)
s = sorted(s)
start,count = len(s)-1,0
for index in range(len(g)-1,-1,-1):
if start>=0 and g[index]<=s[start]:
start -= 1
count += 1
return count
2、摆动序列
一个一个往前后找,后面那个数-前面那个数=当前差,要是当前差和之前的差相乘<=0 而且当前差不等于0,则res+1,之前的差=当前差。
class Solution(object):
def wiggleMaxLength(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
preC,curC,res = 0,0,1
for i in range(len(nums)-1):
curC = nums[i+1]-nums[i]
if curC*preC<=0 and curC!=0:
res += 1
preC = curC
return res
3、买卖股票的最佳时机 II
假如第0天买入,第3天卖出,那么利润为:prices[3] - prices[0]。
相当于(prices[3] - prices[2]) + (prices[2] - prices[1]) + (prices[1] - prices[0])。
此时就是把利润分解为每天为单位的维度,而不是从0天到第3天整体去考虑!
那么根据prices可以得到每天的利润序列:(prices[i] - prices[i - 1])…(prices[1] - prices[0])。
其实我们需要收集每天的正利润就可以,收集正利润的区间,就是股票买卖的区间,而我们只需要关注最终利润,不需要记录区间。
class Solution(object):
def maxProfit(self, prices):
"""
:type prices: List[int]
:rtype: int
"""
profit = 0
for i in range(1,len(prices)):
profit_i = prices[i]-prices[i-1]
if profit_i>0:
profit += profit_i
return profit
4、跳跃游戏
一个一个往后找,要是maxLen能跳到,就比较maxLen和nums[i]+i哪个大,取那个大的。如果maxLen大于len(nums)就为True,否则为False。
class Solution(object):
def canJump(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
maxLen = 0
for i in range(len(nums)):
if maxLen>=i:
maxLen = max(maxLen,nums[i]+i)
return maxLen>=i
5、跳跃游戏 II
class Solution(object):
def jump(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if len(nums)==1:
return 0
ans = 0
curDistance = 0
nextDistance = 0
for i in range(len(nums)):
nextDistance = max(i+nums[i],nextDistance)
if i==curDistance:
if curDistance!=len(nums)-1:
ans += 1
curDistance = nextDistance
if nextDistance >= len(nums)-1:
break
return ans
6、K 次取反后最大化的数组和
- 将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小
- 从前向后遍历,遇到负数将其变为正数,同时K–
- 如果K还大于0,那么反复转变数值最小的元素,将K用完
- 求和
class Solution(object):
def largestSumAfterKNegations(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
nums = sorted(nums,key=abs,reverse=True)
for i in range(len(nums)):
if k>0 and nums[i]<0:
nums[i] = -nums[i]
k -= 1
while k:
nums[-1] = -nums[-1]
k -= 1
return sum(nums)
7、加油站
直接从全局进行贪心选择,情况如下:
- 如果gas的总和小于cost总和,那么无论从哪里出发,一定是跑不了一圈的
- rest[i] = gas[i]-cost[i]为一天剩下的油,i从0开始计算累加到最后一站,如果累加没有出现负数,说明从0出发,油就没有断过,那么0就是起点。
- 如果累加的最小值是负数,汽车就要从非0节点出发,从后向前,看哪个节点能把这个负数填平,能把这个负数填平的节点就是出发节点。
class Solution(object):
def canCompleteCircuit(self, gas, cost):
"""
:type gas: List[int]
:type cost: List[int]
:rtype: int
"""
n = len(gas)
cur_sum = 0
min_sum = float('inf')
for i in range(n):
cur_sum += gas[i]-cost[i]
min_sum = min(min_sum,cur_sum)
if cur_sum<0:
return -1
if min_sum>=0:
return 0
for j in range(n-1,0,-1):
min_sum+=gas[j]-cost[j]
if min_sum>=0:
return j
return -1
8、柠檬水找零
情况一:账单是5,直接收下。
情况二:账单是10,消耗一个5,增加一个10
情况三:账单是20,优先消耗一个10和一个5,如果不够,再消耗三个5
class Solution(object):
def lemonadeChange(self, bills):
"""
:type bills: List[int]
:rtype: bool
"""
five, ten = 0, 0
for bill in bills:
if bill == 5:
five += 1
elif bill == 10:
if five < 1: return False
five -= 1
ten += 1
else:
if ten > 0 and five > 0:
ten -= 1
five -= 1
elif five > 2:
five -= 3
else:
return False
return True
9、用最少数量的箭引爆气球
class Solution(object):
def findMinArrowShots(self, points):
"""
:type points: List[List[int]]
:rtype: int
"""
if len(points)==0:
return 0
points = sorted(points)
result = 1
for i in range(1,len(points)):
if points[i][0]>points[i-1][1]:
result += 1
else:
points[i][1]=min(points[i-1][1],points[i][1])
return result
10、无重叠区间
class Solution(object):
def eraseOverlapIntervals(self, intervals):
"""
:type intervals: List[List[int]]
:rtype: int
"""
if len(intervals)==0:
return 0
intervals.sort(key=lambda x:x[1])
count = 1
end = intervals[0][1]
for i in range(1,len(intervals)):
if end<=intervals[i][0]:
count += 1
end = intervals[i][1]
return len(intervals)-count
11、划分字母区间
- 统计每一个字符最后出现的位置
- 从头遍历字符,并更新字符的最远出现下标,如果找到字符最远出现位置下标和当前下标相等了,则找到了分割点
class Solution(object):
def partitionLabels(self, s):
"""
:type s: str
:rtype: List[int]
"""
Dict = {}
for i in range(len(s)):
Dict[s[i]] = i
left = 0
right = 0
result = []
for i in range(len(s)):
right = max(right,Dict[s[i]])
if i==right:
result.append(right-left+1)
left = i+1
return result
12、单调递增的数字
数字:332,从前向后遍历的话,那么就把变成了329,此时2又小于了第一位的3了,真正的结果应该是299。
class Solution(object):
def monotoneIncreasingDigits(self, n):
"""
:type n: int
:rtype: int
"""
a = list(str(n))
for i in range(len(a)-1,0,-1):
if int(a[i])<int(a[i-1]):
a[i-1] = str(int(a[i-1])-1)
a[i:] = '9'*(len(a)-i)
return int("".join(a))
9、动态规划
0、01背包和完全背包
- 区别:
- 01背包:只能选一个或者不选
- 完全背包:能选n个或者不选
- 01背包
物品0 重量1 价值15
物品1 重量3 价值20
物品2 重量4 价值30
背包最大重量为4
(1)二维
- dp[i][j]:0-i个物品任取放到容量为j的容器里的最大价值
- 不放物品i: d p [ i − 1 ] [ j ] dp[i-1][j] dp[i−1][j]
- 放物品i: d p [ i − 1 ] [ j − w e i g h t [ i ] ] + v a l u e [ i ] dp[i-1][j-weight[i]]+value[i] dp[i−1][j−weight[i]]+value[i]
- 求两种情况的最大值
- 画出 物品数量*背包容量 的dp二维数组(dp是由上方和左上方推导出来的)
- 当背包容量为0时,dp[i][0]=0
- 当考虑是否放物品0,求dp[0][j],当j=1、2、3、4都能放下物品0,那么dp[0][1-4]=15
- 往下推导
-
- 情况1:
for(背包):
for (物品重量)
是一行一行的下来
-
- 情况2:
for(物品重量):
for (背包)
是一列一列的下来
直观的,两层数组的遍历顺序是可以颠倒的,对于二维dp数组的实现。
(2)一维
把上一层拷贝到当前层,直接在当前层计算,就可以实现二维变一维。
- dp[j]含义:容量为j的背包所背最大价值
- 不放物品i是 d p [ j ] = d p [ j ] dp[j]=dp[j] dp[j]=dp[j]【对应二维: d p [ i − 1 ] [ j ] dp[i-1][j] dp[i−1][j]】
- 放物品i是 d p [ j − w e i g h t [ i ] ] + v a l u e [ i ] dp[j-weight[i]]+value[i] dp[j−weight[i]]+value[i]【对应二维: d p [ i − 1 ] [ j − w e i g h t [ i ] ] + v a l u e [ i ] dp[i-1][j-weight[i]]+value[i] dp[i−1][j−weight[i]]+value[i]】
- 得到 d p [ j ] = m a x ( d p [ j ] , d p [ j − w e i g h t [ i ] ] + v a l u e [ i ] ) dp[j]=max(dp[j],dp[j-weight[i]]+value[i]) dp[j]=max(dp[j],dp[j−weight[i]]+value[i])
- 初始化:dp[0]=0;由于max取最大,所以dp[j]本身初始化为0
- 遍历顺序:
for i in range(物品数量):【物品】
for j in range(背包大小,weight[i]-1,-1):【背包倒序】
-
- 为什么要倒序?保证每个物品只被添加一次**【也就是说完全背包,就是正序!!!】**
eg:
如果正序遍历:
dp[1]=dp[1-1]+15=15
dp[2]=dp[2-1]+15=30说明物品0往里面加了2次
倒序遍历:
dp[2]=dp[2-1]+15=15
dp[1]=dp[1-1]+15=15
- 为什么要倒序?保证每个物品只被添加一次**【也就是说完全背包,就是正序!!!】**
-
- 为什么二维不需要倒序?因为二维当前层大小不受上一层大小的影响
-
- 为什么先遍历物品再遍历背包?就变成了一个物品的数值啦
1、最大子数组和
动态规划:
f
(
n
)
=
m
a
x
(
f
(
n
−
1
)
+
n
u
m
s
[
i
]
,
n
u
m
s
[
i
]
)
f(n)=max(f(n-1)+nums[i],nums[i])
f(n)=max(f(n−1)+nums[i],nums[i])
class Solution(object):
def maxSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if len(nums)==1:
return nums[0]
dp = [0]*len(nums)
dp[0] = nums[0]
for i in range(1,len(nums)):
dp[i] = max(nums[i],dp[i-1]+nums[i])
return max(dp)
2、判断你是否能够到达最后一个下标
class Solution(object):
def canJump(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
maxLen = 0
for i in range(len(nums)):
if maxLen>=i:
maxLen = max(maxLen,nums[i]+i)
return maxLen>=i
3、网格求不同路径
动态规划:
f
(
i
,
j
)
=
f
(
i
−
1
,
j
)
+
f
(
i
,
j
−
1
)
f(i,j)=f(i-1,j)+f(i,j-1)
f(i,j)=f(i−1,j)+f(i,j−1)
f
(
n
)
=
f
(
n
)
+
f
(
n
−
1
)
f(n)=f(n)+f(n-1)
f(n)=f(n)+f(n−1)
class Solution(object):
def uniquePaths(self, m, n):
"""
:type m: int
:type n: int
:rtype: int
"""
dp = [1]*n
for i in range(1,m):
for j in range(1,n):
dp[j] += dp[j-1]
return dp[-1]
4、不同路径II
class Solution(object):
def uniquePathsWithObstacles(self, obstacleGrid):
"""
:type obstacleGrid: List[List[int]]
:rtype: int
"""
m = len(obstacleGrid)
n = len(obstacleGrid[0])
if m==0 and n==0:
return 0
dp = [[0]*n for _ in range(m)]
flag_1 = 0
for i in range(n):
if obstacleGrid[0][i] == 1:
flag_1 = 1
if flag_1==1:
dp[0][i] = 0
else:
dp[0][i] = 1
flag_2 = 0
for i in range(m):
if obstacleGrid[i][0] == 1:
flag_2 = 1
if flag_2==1:
dp[i][0] = 0
else:
dp[i][0] = 1
for i in range(1,m):
for j in range(1,n):
if obstacleGrid[i][j]==0:
dp[i][j] = dp[i-1][j]+dp[i][j-1]
return dp[-1][-1]
4、最小路径和
- 先计算边界,边界就是该值加上前面那个值,因为必须走过前面那个值才能到这个值(如果要总和最小的话)。
- 然后再算中间的值, d p [ r ] [ c ] = m i n ( d p [ r − 1 ] [ c ] , d p [ r ] [ c − 1 ] ) + g r i d [ r ] [ c ] dp[r][c]=min(dp[r-1][c],dp[r][c-1])+grid[r][c] dp[r][c]=min(dp[r−1][c],dp[r][c−1])+grid[r][c]
class Solution(object):
def minPathSum(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
rows, columns = len(grid), len(grid[0])
dp = [[0] * columns for _ in range(rows)]#背出来非常重要
dp[0][0] = grid[0][0]
for r in range(1,len(grid)):
dp[r][0] = dp[r-1][0]+grid[r][0]
for c in range(1,len(grid[0])):
dp[0][c] = dp[0][c-1]+grid[0][c]
for r in range(1,len(grid)):
for c in range(1,len(grid[0])):
dp[r][c] = min(dp[r-1][c],dp[r][c-1])+grid[r][c]
return dp[-1][-1]
5、 爬楼梯
class Solution(object):
def climbStairs(self, n):
"""
:type n: int
:rtype: int
"""
dp = [0]*2
dp[0] = 1
dp[1] = 2
for i in range(2,n):
dp[i%2] = dp[(i-1)%2]+dp[(i-2)%2]
return dp[(n-1)%2]
5、使用最小花费爬楼梯[做错了]
跳完以后再付!!!
dp[i] = min(dp[i-2]+cost[i-2],dp[i-1]+cost[i-1])
class Solution(object):
def minCostClimbingStairs(self, cost):
"""
:type cost: List[int]
:rtype: int
"""
#dp[i] = min(dp[i-2]+cost[i-2],dp[i-1]+cost[i-1])
dp = [0]*(1+len(cost))
for i in range(2,len(cost)+1):
dp[i] = min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])
return dp[-1]
6、单词搜索
从任意的位置开始,上下左右查找,要是找到的字母不满足要求就False,要是整个字符串都找到了就True。为了不重复查找,把查到的字母的位置信息放入visited。如果不查这个字母了就把信息从visited弄出来。
class Solution:
def helper(self,i,j,k):
if self.board[i][j]!=self.word[k]:
return False
if k==len(self.word)-1:
return True
self.visited.add((i,j))
result = False
for di,dj in self.direction:
newi = di+i
newj = dj+j
if newi>=0 and newi<len(self.board) and newj>=0 and newj<len(self.board[0]) and (newi,newj) not in self.visited:
if self.helper(newi,newj,k+1):
result = True
break
self.visited.remove((i,j))
return result
def exist(self, board, word):
self.board = board
self.word = word
self.visited = set()
self.direction = [(-1,0),(1,0),(0,-1),(0,1)]
for i in range(len(board)):
for j in range(len(board[0])):
if self.helper(i,j,0):
return True
return False
7、单词拆分
找一个s[j:i]的字符串,要是能够找到并且dp[j]=1,那么dp[i]=1;否则dp[i]=0
class Solution(object):
def wordBreak(self, s, wordDict):
"""
:type s: str
:type wordDict: List[str]
:rtype: bool
"""
wordDictSet = list(set(wordDict))
dp = [0]*(len(s)+1)
dp[0] = 1
for i in range(1,len(s)+1):
for j in range(i):
if dp[j] and s[j:i] in wordDictSet:
dp[i] = 1
break
return bool(dp[-1])
8、乘积最大子数组
由于乘积有正有负,所以要存储最大值和最小值,然后每次把最大值和最小值一起判定。
class Solution(object):
def maxProduct(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
maxDp = [0]*len(nums)
minDp = [0]*len(nums)
maxDp[0] = 1
minDp[0] = 1
for i in range(len(nums)):
maxDp[i] = max(nums[i],maxDp[i-1]*nums[i],minDp[i-1]*nums[i])
minDp[i] = min(nums[i],minDp[i-1]*nums[i],maxDp[i-1]*nums[i])
maxDp = sorted(maxDp)
minDp = sorted(minDp)
return max(maxDp[-1],minDp[-1])
9、打家劫舍
f
(
i
)
=
m
a
x
(
f
(
i
−
2
)
+
n
u
m
s
[
i
]
,
f
(
i
−
1
)
)
f(i)=max(f(i-2)+nums[i],f(i-1))
f(i)=max(f(i−2)+nums[i],f(i−1))
class Solution(object):
def rob(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if len(nums)==1:
return nums[0]
dp = [0]*2
dp[0] = nums[0]
dp[1] = max(nums[0],nums[1])
for i in range(2,len(nums)):
dp[i%2] = max(dp[(i-2)%2]+nums[i],dp[(i-1)%2])
return max(dp)
10、岛屿数量
从任意岛屿开始,上下左右去查,如果查过了就标注为2,其实就是使用visited来查询有多少岛屿。
class Solution(object):
def isArea(self,i,j):
if i>=0 and i<len(self.grid) and j>=0 and j<len(self.grid[0]):
return True
return False
def helper(self,i,j):
if not self.isArea(i,j) or self.grid[i][j]!="1":
return
self.grid[i][j] = "2"
for di,dj in self.direction:
newi,newj = i+di,j+dj
self.helper(newi,newj)
def numIslands(self, grid):
"""
:type grid: List[List[str]]
:rtype: int
"""
num = 0
self.grid =grid
self.direction = [(0,1),(0,-1),(1,0),(-1,0)]
for i in range(len(grid)):
for j in range(len(grid[0])):
if grid[i][j]=="1":
self.helper(i,j)
num += 1
return num
11、最大正方形
从任意为1的位置开始,
对于值为1的地方来说
f
(
i
+
1
,
j
+
1
)
=
m
i
n
(
f
(
i
+
1
,
j
)
,
f
(
i
,
j
+
1
)
,
f
(
i
,
j
)
)
f(i+1,j+1)=min(f(i+1,j),f(i,j+1),f(i,j))
f(i+1,j+1)=min(f(i+1,j),f(i,j+1),f(i,j))
m
a
x
S
i
z
e
=
m
a
x
(
m
a
x
S
i
z
e
,
f
(
i
+
1
,
j
+
1
)
)
maxSize=max(maxSize,f(i+1,j+1))
maxSize=max(maxSize,f(i+1,j+1))
答案是maxSize**2
class Solution(object):
def maximalSquare(self, matrix):
"""
:type matrix: List[List[str]]
:rtype: int
"""
rows = len(matrix)
cols = len(matrix[0])
dp = [[0]*(cols+1) for _ in range(rows+1)]
maxSize = 0
for i in range(rows):
for j in range(cols):
if matrix[i][j] == "1":
dp[i+1][j+1] = min(dp[i+1][j],dp[i][j+1],dp[i][j])+1
maxSize = max(maxSize,dp[i+1][j+1])
return maxSize**2
12、最长递增子序列
站在i的门口,j从0开始找,找到i,如果说这个j对应的值大于i:
f
(
i
)
=
m
a
x
(
f
(
i
)
,
f
(
j
)
+
1
)
f(i)=max(f(i),f(j)+1)
f(i)=max(f(i),f(j)+1)
求
m
a
x
(
d
p
)
max(dp)
max(dp)
class Solution(object):
def lengthOfLIS(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
dp = [1]*len(nums)
for i in range(len(nums)):
for j in range(i):
if nums[j]<nums[i]:
dp[i] = max(dp[i],dp[j]+1)
return max(dp)
13、最佳买卖股票时机含冷冻期
d
p
[
i
]
[
0
]
dp[i][0]
dp[i][0]表示我们目前持有一支股票,对应的「累计最大收益」
d
p
[
i
]
[
1
]
dp[i][1]
dp[i][1]表示我们目前不持有任何股票,并且处于冷冻期中,对应的「累计最大收益」
d
p
[
i
]
[
2
]
dp[i][2]
dp[i][2]表示我们目前不持有任何股票,并且不处于冷冻期中,对应的「累计最大收益」
- 对于 d p [ i ] [ 0 ] dp[i][0] dp[i][0]: d p [ i ] [ 0 ] = m a x ( d p [ i ] [ 0 ] , d p [ i − 1 ] [ 2 ] − p r i c e s [ i ] ) dp[i][0]=max(dp[i][0],dp[i-1][2]-prices[i]) dp[i][0]=max(dp[i][0],dp[i−1][2]−prices[i])
-
- 可能是第i-1天就持有的,对应的状态是 d p [ i − 1 ] [ 0 ] dp[i-1][0] dp[i−1][0]
-
- 可能是第i天买入的,那么第i-1天就必须不能有股票并且不处于冷冻期,对应的状态是 d p [ i − 1 ] [ 2 ] − p r i c e s [ i ] dp[i-1][2]-prices[i] dp[i−1][2]−prices[i]
- 对于 d p [ i ] [ 1 ] dp[i][1] dp[i][1]: d p [ i ] [ 1 ] = d p [ i − 1 ] [ 0 ] + p r i c e s [ i ] dp[i][1]=dp[i-1][0]+prices[i] dp[i][1]=dp[i−1][0]+prices[i]
- 对于 d p [ i ] [ 2 ] dp[i][2] dp[i][2]: d p [ i ] [ 2 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 2 ] ) dp[i][2]=max(dp[i-1][1],dp[i-1][2]) dp[i][2]=max(dp[i−1][1],dp[i−1][2])
-
- 说明第i-1天不持有任何股票,处于冷冻期的状态是 d p [ i − 1 ] [ 1 ] dp[i-1][1] dp[i−1][1],不处于冷冻期,对应的状态是 d p [ i − 1 ] [ 2 ] dp[i-1][2] dp[i−1][2]
class Solution(object):
def maxProfit(self, prices):
"""
:type prices: List[int]
:rtype: int
"""
dp = [[0]*3 for _ in range(len(prices))]
dp[0][0] = -prices[0]
dp[0][1] = 0
dp[0][2] = 0
n = len(prices)
for i in range(1,n):
dp[i][0] = max(dp[i-1][0],dp[i-1][2]-prices[i])
dp[i][1] = dp[i-1][0]+prices[i]
dp[i][2] = max(dp[i-1][1],dp[i-1][2])
return max(dp[n-1][1],dp[n-1][2])
13、买卖股票的最佳时机含手续费
设置两个数组,一个是现在有股票,一个是现在没股票
- 对于现在有股票来说,可能是前一天有股票或者前一天的没股票这一天刚刚买入股票。
- 对于现在没股票来说,可能是前一天就没有股票,或者说前一天有股票今天刚刚卖掉。
class Solution(object):
def maxProfit(self, prices, fee):
"""
:type prices: List[int]
:type fee: int
:rtype: int
"""
n = len(prices)
dp = [[0]*2 for _ in range(n)]
dp[0][0] = -prices[0]
for i in range(1,n):
dp[i][0] = max(dp[i-1][0],dp[i-1][1]-prices[i])
dp[i][1] = max(dp[i-1][1],dp[i-1][0]+prices[i]-fee)
return max(dp[-1][0],dp[-1][1])
14、零钱兑换
- dp[j]:凑足总额为j所需钱币的最少个数为dp[j]
- 凑足总额为 j − c o i n s [ i ] j - coins[i] j−coins[i]的最少个数为 d p [ j − c o i n s [ i ] ] dp[j - coins[i]] dp[j−coins[i]],那么只需要加上一个钱币 c o i n s [ i ] coins[i] coins[i]即 d p [ j − c o i n s [ i ] ] + 1 dp[j - coins[i]] + 1 dp[j−coins[i]]+1就是 d p [ j ] dp[j] dp[j]
- 所以
d
p
[
j
]
dp[j]
dp[j] 要取所有
d
p
[
j
−
c
o
i
n
s
[
i
]
]
+
1
dp[j - coins[i]] + 1
dp[j−coins[i]]+1 中最小的。递推公式:
d p [ j ] = m i n ( d p [ j − c o i n s [ i ] ] + 1 , d p [ j ] ) dp[j] = min(dp[j - coins[i]] + 1, dp[j]) dp[j]=min(dp[j−coins[i]]+1,dp[j]) - 首先凑足总金额为0所需钱币的个数一定是0,那么dp[0] = 0
- 如果我把coins[i]加进去那么必然是j大于coins[i],所以外面的循环时coins[i],里面的循环是比coins[i]大比amount小的j
因为amount是从0开始的,所以dp大小是amount+1
class Solution(object):
def coinChange(self, coins, amount):
"""
:type coins: List[int]
:type amount: int
:rtype: int
"""
if amount==0:
return 0
maxCoin = 2e31-1
dp = [maxCoin]*(amount+1)
dp[0] = 0
for coin in coins:
for i in range(coin,amount+1):
dp[i] = min(dp[i],dp[i-coin]+1)
if dp[amount]==maxCoin:
return -1
else:
return dp[amount]
14、零钱兑换 II
class Solution(object):
def change(self, amount, coins):
"""
:type amount: int
:type coins: List[int]
:rtype: int
"""
dp = [0]*(amount+1)
dp[0] = 1
for i in range(len(coins)):
for j in range(coins[i],amount+1):
dp[j] += dp[j-coins[i]]
return dp[amount]
15、打家劫舍 II
两种情况:不偷第一个房间,不偷最后一个房间,取最大值
class Solution(object):
def roblist(self,nums):
l=len(nums)
dp=[0]*l
dp[0]=nums[0]
for i in range(1,l):
if i==1:
dp[i]=max(dp[i-1],nums[i])
else:
dp[i]=max(dp[i-1],dp[i-2]+nums[i])
return dp[-1]
def rob(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
#在198入门级的打家劫舍问题上分两种情况考虑
#一是不偷第一间房,二是不偷最后一间房
if len(nums)==1:#题目中提示nums.length>=1,所以不需要考虑len(nums)==0的情况
return nums[0]
val1=self.roblist(nums[1:])#不偷第一间房
val2=self.roblist(nums[:-1])#不偷最后一间房
return max(val1,val2)
15、打家劫舍 III
要不就是偷root,要不就是偷左右子树,ls表示偷左子树,ln表示不偷左子树,rs表示偷右子树,rn表示不偷右子树。两种情况
- 偷root: r o o t + l n + r n root+ln+rn root+ln+rn
- 不偷root: m a x ( l s , l n ) + m a x ( r s , r n ) max(ls,ln)+max(rs,rn) max(ls,ln)+max(rs,rn)
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def helper(self,root):
if not root:
return 0,0
ls,ln = self.helper(root.left)
rs,rn = self.helper(root.right)
return root.val+ln+rn,max(ls,ln)+max(rs,rn)
def rob(self, root):
"""
:type root: TreeNode
:rtype: int
"""
return max(self.helper(root))
16、分割等和子集
- dp[j] 表示: 容量为j的背包,所背的物品价值最大可以为dp[j]
class Solution(object):
def canPartition(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
s = sum(nums)
if s%2!=0:
return False
s = s//2
dp = [0]*(s+1)
for num in nums:
for j in range(s,num-1,-1):
dp[j] = max(dp[j],dp[j-num]+num)
return s==dp[s]
17、岛屿的最大面积
class Solution(object):
def helper(self,i,j):
self.grid[i][j] = 2
area = 1
for di,dj in self.direction:
newi = di+i
newj = dj+j
if newi>=0 and newi<len(self.grid) and newj>=0 and newj<len(self.grid[0]) and self.grid[newi][newj]==1:
area += self.helper(newi,newj)
return area
def maxAreaOfIsland(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
m = len(grid)
n = len(grid[0])
self.grid = grid
self.direction = [(1,0),(-1,0),(0,1),(0,-1)]
maxArea = 0
for i in range(m):
for j in range(n):
if self.grid[i][j] ==1:
area = self.helper(i,j)
maxArea = max(maxArea,area)
return maxArea
18、矩阵中的最长递增路径【不会!!!】
class Solution(object):
def dfs(self,matrix,m,n,i,j,memo):
if memo[i][j]!=0:
return memo[i][j]
ans = 1
for di,dj in self.dirs:
newi = i+di
newj = j+dj
if newi>=0 and newi<m and newj>=0 and newj<n and matrix[newi][newj]>matrix[i][j]:
ans = max(ans,self.dfs(matrix,m,n,newi,newj,memo)+1)
memo[i][j] = ans
return ans
def longestIncreasingPath(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: int
"""
m = len(matrix)
n = len(matrix[0])
memo = [[0]*n for _ in range(m)]
ans = 0
self.dirs = [(0,1),(0,-1),(1,0),(-1,0)]
for i in range(m):
for j in range(n):
if memo[i][j]==0:
ans = max(ans,self.dfs(matrix,m,n,i,j,memo))
return ans
19、整数拆分【不会!!!!】
假设对正整数 i 拆分出的第一个正整数是 j(1 <= j < i),则有以下两种方案:
- 将 i 拆分成 j 和 i−j 的和,且 i−j 不再拆分成多个正整数,此时的乘积是 j ∗ ( i − j ) j * (i-j) j∗(i−j)
- 将 i 拆分成 j 和 i−j 的和,且 i−j 继续拆分成多个正整数,此时的乘积是 j ∗ d p [ i − j ] j * dp[i-j] j∗dp[i−j]
class Solution(object):
def integerBreak(self, n):
"""
:type n: int
:rtype: int
"""
dp = [0] * (n + 1)
dp[2] = 1
for i in range(3, n + 1):
for j in range(1, i / 2 + 1):
dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j]))
return dp[n]
20、不同的二叉搜索树
- dp[i] : 1到i为节点组成的二叉搜索树的个数为dp[i]。
- 确定递推公式
在上面的分析中,其实已经看出其递推关系, dp[i] += dp[以j为头结点左子树节点数量] * dp[以j为头结点右子树节点数量]
j相当于是头结点的元素,从1遍历到i为止。
所以递推公式: d p [ i ] + = d p [ j − 1 ] ∗ d p [ i − j ] dp[i] += dp[j - 1] * dp[i - j] dp[i]+=dp[j−1]∗dp[i−j]; ,j-1 为j为头结点左子树节点数量,i-j 为以j为头结点右子树节点数量 - dp数组如何初始化
初始化,只需要初始化dp[0]就可以了,推导的基础,都是dp[0]。
所以初始化dp[0] = 1。否则乘法的结果就都变成0了。 - 确定遍历顺序
首先一定是遍历节点数,从递归公式:dp[i] += dp[j - 1] * dp[i - j]可以看出,节点数为i的状态是依靠 i之前节点数的状态。
class Solution(object):
def numTrees(self, n):
"""
:type n: int
:rtype: int
"""
G = [0]*(n+1)
G[0] = 1
G[1] = 1
for i in range(2,n+1):
for j in range(1,i+1):
G[i] += G[j-1]*G[i-j]
return G[n]
21、最后一块石头的重量 II
本题其实就是尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小,这样就化解成01背包问题了。
class Solution(object):
def lastStoneWeightII(self, stones):
"""
:type stones: List[int]
:rtype: int
"""
sumweight = sum(stones)
target = sumweight//2
dp = [0]*(target+1)
for i in range(len(stones)):
for j in range(target,stones[i]-1,-1):
dp[j] = max(dp[j],dp[j-stones[i]]+stones[i])
return sumweight-2*dp[target]
22、目标和
left组合-right组合=target
left组合+right组合=sum
2*left组合=target+sum
left组合=(target+sum)/2
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
sumValue = sum(nums)
if abs(target) > sumValue or (sumValue + target) % 2 == 1:
return 0
bagSize = (sumValue + target) // 2
dp = [0] * (bagSize + 1)
dp[0] = 1
for i in range(len(nums)):
for j in range(bagSize, nums[i] - 1, -1):
dp[j] += dp[j - nums[i]]
return dp[bagSize]
23、一和零
- dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。
- dp[i][j] 可以由前一个strs里的字符串推导出来,strs里的字符串有zeroNum个0,oneNum个1。dp[i][j] 就可以是 dp[i - zeroNum][j - oneNum] + 1。然后我们在遍历的过程中,取dp[i][j]的最大值。所以递推公式:
d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ i − z e r o N u m ] [ j − o n e N u m ] + 1 ) dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1) dp[i][j]=max(dp[i][j],dp[i−zeroNum][j−oneNum]+1)
这就是一个典型的01背包! 只不过物品的重量有了两个维度而已。 - 01背包的dp数组初始化为0就可以。
class Solution:
def findMaxForm(self, strs, m, n):
dp = [[0] * (n + 1) for _ in range(m + 1)] # 默认初始化0
# 遍历物品
for str in strs:
ones = str.count('1')
zeros = str.count('0')
# 遍历背包容量且从后向前遍历!
for i in range(m, zeros - 1, -1):
for j in range(n, ones - 1, -1):
dp[i][j] = max(dp[i][j], dp[i - zeros][j - ones] + 1)
return dp[m][n]
24、组合总和 Ⅳ【不会!!!】
由于顺序不同的序列被视作不同的组合。那么本题,两个for循环的先后顺序可就有说法了。
- dp[i]: 凑成目标正整数为i的排列个数为dp[i]
- dp[i](考虑nums[j])可以由 dp[i - nums[j]](不考虑nums[j]) 推导出来。因为只要得到nums[j],排列个数dp[i - nums[j]],就是dp[i]的一部分。
- 因为递推公式dp[i] += dp[i - nums[j]]的缘故,dp[0]要初始化为1,这样递归其他dp[i]的时候才会有数值基础。
class Solution(object):
def combinationSum4(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
dp = [0]*(target+1)
dp[0] = 1
for i in range(1,target+1):
for j in nums:
if i>=j:
dp[i]+=dp[i-j]
return dp[-1]
25、最长重复子数组
class Solution(object):
def findLength(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: int
"""
dp = [[0]*(len(nums2)+1) for _ in range(len(nums1)+1)]
result = 0
for i in range(1,len(nums1)+1):
for j in range(1,len(nums2)+1):
if nums1[i-1]==nums2[j-1]:
dp[i][j] = dp[i-1][j-1]+1
result = max(result,dp[i][j])
return result
26、不相交的线
本题说是求绘制的最大连线数,其实就是求两个字符串的最长公共子序列的长度!
class Solution(object):
def maxUncrossedLines(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: int
"""
dp = [[0] * (len(nums2)+1) for _ in range(len(nums1)+1)]
for i in range(1, len(nums1)+1):
for j in range(1, len(nums2)+1):
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[-1][-1]
27、判断子序列
class Solution(object):
def isSubsequence(self, s, t):
"""
:type s: str
:type t: str
:rtype: bool
"""
dp = [[0]*(len(t)+1) for _ in range(len(s)+1)]
for i in range(1,len(s)+1):
for j in range(1,len(t)+1):
if s[i-1]==t[j-1]:
dp[i][j] = dp[i-1][j-1]+1
else:
dp[i][j] = dp[i][j-1]
if dp[-1][-1]==len(s):
return True
return False
28、两个字符串的删除操作
- dp[i][j]:表示使得 w o r d 1 [ 0 : i ] word_1[0:i] word1[0:i]和 w o r d 2 [ 0 : j ] word_2[0:j] word2[0:j]相同的最少删除操作次数
- 当i=0时, w o r d 1 [ 0 : i ] word_1[0:i] word1[0:i]为空,空字符串和任何字符串要变成相同,只有将另外一个字符串的字符全部删除,因此 d p [ 0 ] [ j ] = j dp[0][j]=j dp[0][j]=j
- 当j=0时, w o r d 2 [ 0 : j ] word_2[0:j] word2[0:j]为空,空字符串和任何字符串要变成相同,只有将另外一个字符串的字符全部删除,因此 d p [ i ] [ 0 ] = i dp[i][0]=i dp[i][0]=i
- 当 w o r d 1 [ i − 1 ] = w o r d 2 [ j − 1 ] word1[i - 1] =word2[j - 1] word1[i−1]=word2[j−1]相同的时候,增加了一个相同的字符,删除情况不变 d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] dp[i][j] = dp[i - 1][j - 1] dp[i][j]=dp[i−1][j−1];
- 当 w o r d 1 [ i − 1 ] ! = w o r d 2 [ j − 1 ] word1[i - 1] != word2[j - 1] word1[i−1]!=word2[j−1]不相同的时候,有三种情况:
-
- 情况一:删 w o r d 1 [ i − 1 ] word1[i - 1] word1[i−1],最少操作次数为 d p [ i − 1 ] [ j ] + 1 dp[i - 1][j] + 1 dp[i−1][j]+1
-
- 情况二:删 w o r d 2 [ j − 1 ] word2[j - 1] word2[j−1],最少操作次数为 d p [ i ] [ j − 1 ] + 1 dp[i][j - 1] + 1 dp[i][j−1]+1
-
- 情况三:同时删 w o r d 1 [ i − 1 ] 和 w o r d 2 [ j − 1 ] word1[i - 1]和word2[j - 1] word1[i−1]和word2[j−1],操作的最少次数为 d p [ i − 1 ] [ j − 1 ] + 2 dp[i - 1][j - 1] + 2 dp[i−1][j−1]+2
class Solution(object):
def minDistance(self, word1, word2):
"""
:type word1: str
:type word2: str
:rtype: int
"""
dp = [[0]*(len(word2)+1) for _ in range(len(word1)+1)]
for i in range(len(word1)+1):
dp[i][0] = i
for j in range(len(word2)+1):
dp[0][j] = j
for i in range(1,len(word1)+1):
for j in range(1,len(word2)+1):
if word1[i-1] == word2[j-1]:
dp[i][j] = dp[i-1][j-1]
else:
dp[i][j] = min(dp[i-1][j-1] + 2, dp[i-1][j] + 1, dp[i][j-1] + 1)
return dp[-1][-1]
29、最长回文子序列【不会】
- dp[i][j]:字符串s在[i, j]范围内最长的回文子序列的长度为dp[i][j]。
- 如果s[i]与s[j]相同,那么dp[i][j] = dp[i + 1][j - 1] + 2;
- 如果s[i]与s[j]不相同,说明s[i]和s[j]的同时加入 并不能增加[i,j]区间回文子序列的长度,那么分别加入s[i]、s[j]看看哪一个可以组成最长的回文子序列。
-
- 加入s[j]的回文子序列长度为dp[i + 1][j]。
-
- 加入s[i]的回文子序列长度为dp[i][j - 1]。
- 加入s[i]的回文子序列长度为dp[i][j - 1]。
class Solution(object):
def longestPalindromeSubseq(self, s):
"""
:type s: str
:rtype: int
"""
dp = [[0]*len(s) for _ in range(len(s))]
for i in range(len(s)):
dp[i][i] = 1
for i in range(len(s)-1,-1,-1):
for j in range(i+1,len(s)):
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][-1]
10、单调栈
1、下一个更大元素 I
-
情况一:当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况。此时满足递增栈(栈头到栈底的顺序),所以直接入栈。
-
情况二:当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况。如果相等的话,依然直接入栈,因为我们要求的是右边第一个比自己大的元素,而不是大于等于!
-
情况三:当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况。此时如果入栈就不满足递增栈了,这也是找到右边第一个比自己大的元素的时候。
判断栈顶元素是否在nums1里出现过,(注意栈里的元素是nums2的元素),如果出现过,开始记录结果。