- 两数之和
暴力法或者map一遍扫描
解法1:
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
length = len(nums)
for i in range(length-1):
for j in range(i+1, length):
if nums[i] + nums[j] == target:
return [i, j]
解法2:
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
length = len(nums)
Dict = {}
for i in range(len(nums)):
if nums[i] not in Dict:
Dict[nums[i]] = i
if target - nums[i] in Dict and i != Dict[target-nums[i]] :#注意不能是同一个元素
return [i, Dict[target-nums[i]]]
- 两数相加
用链表来模拟加法。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
high = 0
head = ListNode(0)
temp = head
while(l1 or l2 or high):
if l1:
a = l1.val
else:
a = 0
if l2:
b = l2.val
else:
b = 0
cur = (a + b + high) % 10
high = (a + b + high) // 10
temp.next = ListNode(cur)
temp = temp.next
if l1:
l1 = l1.next
if l2:
l2 = l2.next
return head.next
- 无重复字符的最长子串
双指针操作,模板题目。
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
if not s:
return 0
window = {}
left = 0
right = 0
max_len = 0
while(right < len(s)):
c = s[right]
right += 1
if c not in window:
window[c] = 1
else:
window[c] += 1
while(window[c] > 1):
d = s[left]
left += 1
window[d] -= 1
max_len = max(max_len, right-left)
return max_len
- Z 字形变换
基本的字符串操作,需要找到相应的规律。
class Solution:
def convert(self, s: str, numRows: int) -> str:
if not s or len(s) == 1 or numRows == 1:
return s
res = ['']*numRows
length = numRows*2 - 2
i = 0
while(i < len(s)):
index = i % length
if index < numRows:
res[index] += s[i]
else:
res[length-index] += s[i]
i += 1
return ''.join(res)
- 整数反转
需要考虑溢出的问题。这里的x是有符号数。
class Solution:
def reverse(self, x: int) -> int:
minNum = -2**31
maxNum = 2**31 -1
ans = 0
flag = 0
if x < 0:
x = -x
flag = 1
while(x):
ans = 10*ans + x %10
x = x // 10
if flag:
ans = -ans
if ans < minNum or ans > maxNum:
return 0
else:
return ans
- 回文数
这个不用考虑数字的溢出,因为如果是回文数,那么一定不会溢出。
class Solution:
def isPalindrome(self, x: int) -> bool:
if x < 0:
return False
ans = 0
temp = x
while(x):
ans = ans*10 + x % 10
x = x // 10
if ans == temp:
return True
else:
return False
- 最长公共前缀
字符串的基本操作。
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
def lcp(str1, str2):
length = min(len(str1), len(str2))
i = 0
while(i < length):
if str1[i] == str2[i]:
i += 1
else:
break
if i== 0:
return ''
else:
return str1[:i]
if not strs:
return ''
temp = strs[0]
for i in range(1, len(strs)):
temp = lcp(strs[i], temp)
if not temp:
return ''
return temp
- 三数之和
三指针操作,这题的重点是需要去除重复状态。首先需要先排序,去除一些极端情况。然后使用i,j,k三个指针,先定住i指针,对j,k指针进行处理,记得除去重复状态。然后对i指针也需要去除重复状态。
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
if not nums:
return []
nums.sort()
if nums[0] > 0 or nums[-1] < 0 or len(nums) < 3:
return []
res = []
for i in range(len(nums)-2):
if nums[i] > 0:
continue
if i >=1 and nums[i] == nums[i-1]: #去除i的重复状态
continue
j = i + 1
k = len(nums) - 1
while(j < k):
if nums[i] + nums[j] + nums[k] ==0:
res.append([nums[i], nums[j], nums[k]])
while(j < k and nums[k] == nums[k-1]):
k -= 1
while(j < k and nums[j] == nums[j+1]):
j += 1
k -= 1
j += 1
elif nums[i] + nums[j] + nums[k] > 0:
k -= 1
else:
j += 1
return res
- 电话号码的字母组合
简单的遍历即可。
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
Dict = {
'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']
}
if digits == '':
return []
self.res = []
def dfs(s, index, route):
if index == len(s):
self.res.append(route)
return
for c in Dict[s[index]]:
dfs(s, index+1, route+c)
dfs(digits, 0, '')
return self.res
- 有效的括号
利用栈来判断括号是否合法。当前字符是左括号的时候就入栈,否者的话先判断一下栈是否是为空,为空就返回False,不然的话就出栈判断是否是对应的字符,最后栈为空的话也返回False。
class Solution:
def isValid(self, s: str) -> bool:
if not s:
return True
stack = []
for c in s:
if c == '(' or c == '{' or c == '[':
stack.append(c)
else:
if not stack:
return False
temp = stack.pop()
if c == ')' and temp != '(':
return False
if c == '}' and temp != '{':
return False
if c == ']' and temp != '[':
return False
if len(stack):
return False
return True
- 合并K个排序链表
归并排序或者是堆。
解法1:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
if not lists:
return None
def recursion(lists, l, r):
if l == r:
return lists[l]
mid = (l + r) // 2
left = recursion(lists, l, mid)
right = recursion(lists, mid+1, r)
return merge(left, right)
def merge(l1, l2):
new_head = ListNode(0)
temp = new_head
while(l1 and l2):
if l1.val < l2.val:
temp.next = l1
l1 = l1.next
else:
temp.next = l2
l2 = l2.next
temp = temp.next
if l1:
temp.next = l1
if l2:
temp.next = l2
return new_head.next
return recursion(lists, 0, len(lists)-1)
解法2:维护一个堆即可,堆为空的时候就终止。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
from heapq import *
class Node:
def __init__(self, node, val):
self.node = node
self.val = val
def __lt__(self, other):
if self.val < other.val:
return True
else:
return False
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
if not lists:
return None
heap = []
for temp in lists:
if temp:
newnode = Node(temp, temp.val)
heappush(heap, newnode)
head = ListNode(0)
temp = head
while(heap):
l = heappop(heap).node
if l.next:
heappush(heap, Node(l.next, l.next.val))
temp.next = l
temp = temp.next
return head.next
- K 个一组翻转链表
链表反转的升级版。这个题目用递归写起来比较好理解。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
if not head or not head.next:
return head
tail = head
count = 0
while(tail):
count += 1
if count == k:
break
tail = tail.next
if count < k:
return head
tail = tail.next
newhead = self.reverse(head, tail)
head.next = self.reverseKGroup(tail, k)
return newhead
def reverse(self, head, tail):
pre = None
cur = head
while(cur != tail):
temp = cur.next
cur.next= pre
pre = cur
cur = temp
return pre
- 删除排序数组中的重复项
基本的双指针操作。
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
length = len(nums)
i = 0
j = 0
while(i < length):
while(i < length-1):
if nums[i] == nums[i+1]:
i += 1
else:
break
nums[j] = nums[i]
j += 1
i += 1
return j
- 移除元素
基本的双指针操作。
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
j = 0
for i in range(len(nums)):
if nums[i] != val:
nums[j] = nums[i]
j += 1
return j
- 实现 strStr()
建议使用双指针。最优的是使用KMP算法,但是我不会。
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
if not needle:
return 0
length1 = len(haystack)
length2 = len(needle)
i = 0
while(i < length1-length2+1):
if haystack[i] == needle[0]:
count = 1
j = 1
i += 1
while(j< length2 and haystack[i]==needle[j]):
count+= 1
i += 1
j += 1
if count == length2:
return i - count
else:
i = i - count + 1
else:
i += 1
return -1
- 下一个排列
从后往前找第一个跳变,如果一直到头都没有找到,那么说明该数组已经是最后一个了,直接反转即可,通过这个跳变将数组分为两个部分,后面的部分一定是逆序的,只有这样才能保证两个排列是紧挨在一起的。接下来的任务就是在后面的部分中找到第一个大于该跳变元素的元素,交换即可,这个时候后面还是逆序的,还需要将后面部分给反转一下。通过这种方式来保证新生成的排列和上一个排列是挨着的。这个题要求在原数组上进行修改,不能使用额外空间。
方法1:
class Solution:
def nextPermutation(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
length = len(nums) - 1
i = length
while(i >= 1):
if nums[i] > nums[i-1]:
break
else:
i -= 1
if i == 0:
nums.reverse()
return
p = length
while(p >= i):
if nums[p] > nums[i-1]:
break
else:
p -= 1
nums[p], nums[i-1] = nums[i-1], nums[p]
l = i
r = length
while(l < r):
nums[l], nums[r] = nums[r], nums[l]
l += 1
r -= 1
方法二:在后面部分查找第一个大于目标的元素,这个可以采用二分来找。这个二分查找比较特殊,不需要查找和目标相等的值,需要找到第一个大于目标元素即可,并且一定存在某个元素大于该目标值。
class Solution:
def nextPermutation(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
length = len(nums) - 1
i = length
while(i >= 1):
if nums[i] > nums[i-1]:
break
else:
i -= 1
if i == 0:
nums.reverse()
return
p = self.search(nums, i, length, nums[i-1])
nums[p], nums[i-1] = nums[i-1], nums[p]
l = i
r = length
while(l < r):
nums[l], nums[r] = nums[r], nums[l]
l += 1
r -= 1
def search(self, nums, l, r, target):
while(l <= r):
mid = (l + r) //2
if nums[mid] > target:
l = mid + 1
else:
r = mid - 1
return r
- 搜索旋转排序数组
这是一个局部有序的数组,我们需要确定出哪一部分是完全有序的。这里我跟之前的写法有所区别,这里采用递归的写法。通过中间值和左边值的比较来确定出有序的范围。再在里面进行二分查找,而剩下的部分又是一个局部有序的数组,这样就变成了之前的子问题,因此可以采用递归的写法。
递归写法:
class Solution:
def search(self, nums: List[int], target: int) -> int:
def search(nums, l, r, target):
while(l <= r):
mid = (l +r) // 2
if nums[mid] == target:
return mid
elif nums[mid] > target:
r = mid - 1
else:
l = mid + 1
return -1
def search_v1(nums, target):
def recursion(nums, l, r):
if l > r:
return -1
mid = (l + r) // 2
if nums[mid] == target:
return mid
if nums[mid] >= nums[l]:
if nums[mid] > target >= nums[l]:
return search(nums, l, mid-1, target)
else:
return recursion(nums, mid+1, r)
else:
if nums[mid] < target <= nums[r]:
return search(nums, mid+1, r, target)
else:
return recursion(nums, l, mid-1)
return recursion(nums, 0, len(nums)-1)
return search_v1(nums, target)
非递归写法:
class Solution:
def search(self, nums: List[int], target: int) -> int:
l = 0
r = len(nums) - 1
while(l <= r):
mid = (l + r) // 2
if nums[mid] == target:
return mid
elif nums[mid] >= nums[l]:
if nums[mid] > target >= nums[l]:
r = mid - 1
else:
l = mid + 1
else:
if nums[mid] < target <= nums[r]:
l = mid + 1
else:
r = mid - 1
return -1
这里补充一下另一道进阶的题目,现在就是这个旋转数组里会存在重复元素值。arr = [5, 6, 7, 8, 5, 5, 5, 5, 5],这个时候nums[mid] >= nums[l]的话,但这一部分却不是局部有序的,需要对右边界进行一个收缩,其他的不存在这个情况。这一题是力扣81题。
class Solution:
def search(self, nums, target) -> int:
def search(nums, l, r, target):
while(l <= r):
mid = (l +r) // 2
if nums[mid] == target:
return mid
elif nums[mid] > target:
r = mid - 1
else:
l = mid + 1
return -1
def search_v1(nums, target):
def recursion(nums, l, r):
if l > r:
return -1
mid = (l + r) // 2
if nums[mid] == target:
return mid
if nums[mid] >= nums[l]:
temp = mid #这5行是对右边界的特殊处理。
while(temp > l and nums[temp] == nums[l]):
temp -= 1
if nums[temp] >= target >= nums[l]:
return search(nums, l, temp, target)
else:
return recursion(nums, mid+1, r)
else:
if nums[mid] < target <= nums[r]:
return search(nums, mid+1, r, target)
else:
return recursion(nums, l, mid-1)
return recursion(nums, 0, len(nums)-1)
if search_v1(nums, target) == -1:
return False
else:
return True
- 搜索插入位置
二分
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
l = 0
r = len(nums) - 1
while(l <= r):
mid = (l + r) // 2
if nums[mid] == target:
return mid
elif nums[mid] > target:
r = mid - 1
else:
l = mid + 1
return l
- 组合总和
要求所有的路径,因此需要用DFS。
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
res = []
def dfs(index, sum, route, target):
if sum > target:
return
if sum == target:
res.append(route)
return
for i in range(index, len(candidates)):
dfs(i, sum+candidates[i], route+[candidates[i]], target)
dfs(0, 0, [], target)
return res
- 组合总和 II
这里是需要取组合情况,每一个元素去多取一次。去组合的方式有两种写法可以使用二叉树的生长方式也可以使用多叉树的生长方式,其中多叉树的生长方式是不完全生长。如果元素中存在重复元素就需要考虑去重的问题,按照二叉树的生长是无法去重的。如果同一层有相同元素就需要去掉,所以最好的办法就是先排序,不然得把当前元素和前面所有的元素全部比较一遍。
解法1:我这里通过使用flags数组来标记,来表明是否是同层元素。
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
res = []
flags = [0]*len(candidates)
candidates.sort()
def dfs(route, sum, index):
if sum > target:
return
if sum == target:
res.append(route)
for i in range(index, len(candidates)):
if flags[i] == 0:
if i > 0 and candidates[i] == candidates[i-1] and flags[i-1] == 0:
continue
flags[i] = 1
dfs(route+[candidates[i]], sum+candidates[i], i+1)
flags[i] = 0
dfs([], 0, 0)
return res
解法2:答案给了另一种比较好的方法
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
res = []
candidates.sort()
def dfs(route, sum, index):
if sum > target:
return
if sum == target:
res.append(route)
for i in range(index, len(candidates)):
if i > index and candidates[i] == candidates[i-1]:#注意i > index
continue
dfs(route+[candidates[i]], sum+candidates[i], i+1)
dfs([], 0, 0)
return res
- 接雨水
单调栈的应用。
class Solution:
def trap(self, height: List[int]) -> int:
ans = 0
stack = []
for i in range(len(height)):
while(stack and height[stack[-1]] < height[i]):
mid = stack.pop()
if not stack:
break
left = stack[-1]
width = i - left - 1
length = min(height[left], height[i]) - height[mid]
ans += width*length
stack.append(i)
return ans
- 跳跃游戏 II
DP,这里的k表示当前位置之前能够走到的最远位置,这里k的计算滞后于当前位置。k表示上一个位置能够的最远距离,k一定要大于或等于i。这个题有两种写法,一种是答案的写法,k的计算滞后于当前位置,就相当于k代表的是上一个位置能够走的最远距离。还有一种写法就是先计算k再来计算当前位置能够走的步数,但是这个写法不推荐,没有第一种方便。
class Solution:
def jump(self, nums: List[int]) -> int:
length = len(nums)
dp = [0]*length
k = 0
for i in range(length):
for j in range(k+1, min(i+nums[i]+1, length)):
dp[j] = dp[i] + 1
k = max(k, i+nums[i])
return dp[-1]
class Solution:
def jump(self, nums: List[int]) -> int:
length = len(nums)
dp = [0]*length
k = nums[0]
for i in range(1, min(k+1, length)):
dp[i] = dp[0] + 1
for i in range(1, length):
k = max(k, i-1+nums[i-1])
for j in range(k+1, min(i+nums[i]+1, length)):
dp[j] = dp[i] + 1
return dp[-1]
- 全排列 II
当前元素如果和同层的前一个元素相同则跳出,通过标记量来判断是否为同一层元素。
解法1:
class Solution:
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
res = []
flags = [0]*len(nums)
def dfs(nums, route, count):
if count == len(nums):
res.append(route)
for i in range(len(nums)):
if flags[i] == 0:
#if i > 0 and nums[i] == nums[i-1] and flags[i-1]==0:
# continue
if i > 0:
p = 0
temp = i-1
while(temp>=0):
if nums[i] == nums[temp] and flags[temp] == 0:
p = 1
break
else:
temp -= 1
if p:
continue
flags[i] = 1
dfs(nums, route+[nums[i]], count+1)
flags[i] = 0
dfs(nums, [], 0)
return res
解法2:可以先排序,这样就只需要判断相邻的元素是否相等了。
class Solution:
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
res = []
nums.sort()
flags = [0]*len(nums)
def dfs(nums, route, count):
if count == len(nums):
res.append(route)
for i in range(len(nums)):
if flags[i] == 0:
if i > 0 and nums[i] == nums[i-1] and flags[i-1]==0:
continue
flags[i] = 1
dfs(nums, route+[nums[i]], count+1)
flags[i] = 0
dfs(nums, [], 0)
return res
- Pow(x, n)
快速幂。需要考虑负数的情况。
class Solution:
def myPow(self, x: float, n: int) -> float:
ans = 1
if n < 0:
x = 1/x
n = -n
while(n):
if n % 2 == 0:
x = x*x
n = n // 2
else:
ans = ans*x
n -= 1
return ans
- 最大子序和
基本DP。
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
length = len(nums)
dp = [0]*length
dp[0] = nums[0]
for i in range(1, length):
if dp[i-1] > 0:
dp[i] = dp[i-1] + nums[i]
else:
dp[i] = nums[i]
return max(dp)
状态压缩:
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
length = len(nums)
temp = nums[0]
res = nums[0]
for i in range(1, length):
if temp > 0:
temp = temp + nums[i]
else:
temp = nums[i]
res = max(res, temp)
return res
- 螺旋矩阵
主要有递归和非递归两种写法,非递归是通过设置4个边界然后一起收缩,而递归写法则是通过DFS来完成路径的模拟。
非递归:
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
if not matrix:
return []
t = 0
b = len(matrix) - 1
l = 0
r = len(matrix[0]) - 1
res = []
while(True):
for i in range(l, r+1):
res.append(matrix[t][i])
t += 1
if t > b:
break
for i in range(t, b+1):
res.append(matrix[i][r])
r -= 1
if l > r:
break
for i in range(r, l - 1, -1):
res.append(matrix[b][i])
b -= 1
if t > b:
break
for i in range(b, t-1, -1):
res.append(matrix[i][l])
l += 1
if l > r:
break
return res
递归写法待定。
- 最后一个单词的长度
字符串的基本操作。
解法1:
class Solution:
def lengthOfLastWord(self, s: str) -> int:
words = s.split()
if not words:
return 0
else:
return len(words[-1])
解法2:
class Solution:
def lengthOfLastWord(self, s: str) -> int:
if not s:
return 0
count = 0
for i in range(len(s)-1, -1, -1):
if s[i] != ' ':
count += 1
elif count and s[i] == ' ':
return count
return count
- 螺旋矩阵 II
四个方向收缩即可。这个题如果不是方形也可以这样做。
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
t = 0
b = n - 1
l = 0
r = n - 1
num = 1
data = [[0]*n for _ in range(n)]
while(True):
for i in range(l, r+1):
data[t][i] =num
num += 1
t += 1
if t > b:
break
for i in range(t, b+1):
data[i][r] = num
num += 1
r -= 1
if l > r:
break
for i in range(r, l-1, -1):
data[b][i] = num
num += 1
b -= 1
if t > b:
break
for i in range(b, t-1, -1):
data[i][l] = num
num += 1
l += 1
if l > r:
break
return data
- 第k个排列
简单的DFS,这个题直接用DFS会超时,剪枝后不会超时,但是python会超时。由于这个题只是让我们求第k个排列,中间的过程不需要我们求,因此可以直接用数学公式将其求出来。
解法1:
class Solution:
def getPermutation(self, n: int, k: int) -> str:
flags = [0]*(n+1)
res = []
self.end = 0
def dfs(route, count):
if count == n:
self.end += 1
if self.end == k:
res.append(route)
return
if self.end == k:
return
for i in range(1, n+1):
if flags[i] == 0:
flags[i] = 1
dfs(route+str(i), count+1)
flags[i] = 0
dfs('', 0)
return res[0]
解法2:
class Solution:
def getPermutation(self, n: int, k: int) -> str:
frac = [i for i in range(0, n+1)]
frac[0] = 1
candidate = [i for i in range(1, n+1)]
for i in range(2, n+1):
frac[i] = frac[i]*frac[i-1]
res = []
k -= 1 #这个是整个代码的关键
for i in range(n-1, -1, -1):
index = k // frac[i]
res.append(candidate[index])
del candidate[index]
k = k - index*frac[i]
return ''.join(list(map(str, res)))
- 旋转链表
需要考虑k值会大于链表的长度,还需要考虑我们要反转的节点是空的情况。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def rotateRight(self, head: ListNode, k: int) -> ListNode:
if not head:
return head
l = head
count = 0
while(l):
count += 1
l = l.next
k = k % count
index = 0
l = head
temp = None
while(l):
index += 1
if index == count - k:
temp = l
break
l = l.next
newhead = temp.next
temp.next = None
l = newhead
if not l:
return head
while(l.next):
l = l.next
l.next = head
return newhead
- 最小路径和
简单的DP问题,注意初始化。
定义一个二维的DP数组。
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
length = len(grid)
width = len(grid[0])
dp = [[0]*width for _ in range(length)]
dp[0][0] = grid[0][0]
for i in range(1, length):
dp[i][0] = dp[i-1][0] + grid[i][0]
for j in range(1, width):
dp[0][j] = dp[0][j-1] + grid[0][j]
for i in range(1, length):
for j in range(1, width):
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
return dp[-1][-1]
原地修改,空间复杂度O(1)
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
length = len(grid)
width = len(grid[0])
for i in range(1, length):
grid[i][0] = grid[i-1][0] + grid[i][0]
for j in range(1, width):
grid[0][j] = grid[0][j-1] + grid[0][j]
for i in range(1, length):
for j in range(1, width):
grid[i][j] = min(grid[i-1][j], grid[i][j-1]) + grid[i][j]
return grid[-1][-1]
将二维的DP数组压缩成一维的,因为结合这个问题,中间的状态是不需要保存的,只需要最终的结果。用一个数组保存每一行的结果,然后算出新的结果后,直接覆盖即可。
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
length = len(grid)
width = len(grid[0])
dp = [0]*width
dp[0] = grid[0][0]
for j in range(1, width):
dp[j] = dp[j-1] + grid[0][j]
for i in range(1, length):
for j in range(width):
if j == 0:
dp[j] = dp[j] + grid[i][j]
else:
dp[j] = min(dp[j-1], dp[j]) + grid[i][j]
return dp[-1]
- 加一
数组的基本操作。
class Solution:
def plusOne(self, digits: List[int]) -> List[int]:
high = 1
for i in range(len(digits)-1, -1, -1):
temp = (digits[i] + high)
digits[i] = temp % 10
high = temp // 10
if digits[0] == 0:
digits.insert(0, 1)
return digits
- 二进制求和
模拟加法。这个题与两数相加非常类似。
class Solution:
def addBinary(self, a: str, b: str) -> str:
res = ['']*(max(len(a), len(b)) + 1)
i = len(a) - 1
j = len(b) - 1
k = len(res) - 1
high = 0
while(i >= 0 or j >= 0):
if i < 0:
temp1 = 0
else:
temp1 = int(a[i])
if j < 0:
temp2 = 0
else:
temp2 = int(b[j])
temp = temp1 + temp2 + high
res[k] = str(temp%2)
k -= 1
high = temp // 2
if i >= 0:
i -= 1
if j >= 0:
j -= 1
if high:
res[0] = '1'
return ''.join(res)
- x 的平方根
简单的二分操作。
class Solution:
def mySqrt(self, x: int) -> int:
l = 0
r = x
while(l <= r):
mid = (l+r) // 2
if mid*mid == x:
return mid
elif mid*mid > x:
r = mid - 1
else:
l = mid + 1
return l-1
- 爬楼梯
基本的DP
解法1:
class Solution:
def climbStairs(self, n: int) -> int:
if n == 1:
return 1
dp = [0]*(n+1)
dp[1] = 1
dp[2] = 2
for i in range(3, n+1):
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
解法2:
> 状态压缩,只需要最终的结果,不需要保留中间的状态
class Solution:
def climbStairs(self, n: int) -> int:
if n == 1:
return 1
a = 1
b = 2
c = 2
for i in range(3, n+1):
c = a + b
a = b
b = c
return c
- 编辑距离
经典DP问题。需要注意边界条件的处理。
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
length = len(word1)
width = len(word2)
dp = [[0]*(width+1) for _ in range(length+1)]
for j in range(1, width+1):
dp[0][j] = dp[0][j-1] + 1
for i in range(1, length+1):
dp[i][0] = dp[i-1][0] + 1
for i in range(1, length+1):
for j in range(1, width+1):
if word1[i-1] == word2[j-1]:
dp[i][j] = dp[i-1][j-1]
else:
dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1
return dp[-1][-1]
状态压缩:
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
length = len(word1) + 1
width = len(word2) + 1
dp = [i for i in range(width)]
for i in range(1, length):
pre = i-1
dp[0] = i
for j in range(1, width):
tmp = dp[j]
if word1[i-1] == word2[j-1]:
dp[j] = pre
else:
dp[j] = min(min(dp[j-1], dp[j]), pre) + 1
pre = tmp
return dp[-1]
- 矩阵置零
这题需要原地修改,如果限制空间复杂度就很麻烦了。需要单独对最左边的一列和最上面的一行进行处理。还需要额外定义两个变量来表示左边的一列和最上面的一行是否存在0。
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
length = len(matrix)
width = len(matrix[0])
r = 0
c = 0
for j in range(width):
if matrix[0][j] == 0:
r = 1
break
for i in range(length):
if matrix[i][0] == 0:
c = 1
break
for i in range(1, length):
for j in range(1, width):
if matrix[i][j] == 0:
matrix[0][j] = 0
matrix[i][0] = 0
for i in range(1, length):
if matrix[i][0] == 0:
for j in range(1, width):
matrix[i][j] = 0
for j in range(1, width):
if matrix[0][j] == 0:
for i in range(1, length):
matrix[i][j] = 0
if (r == 1 and c == 1):
for i in range(length):
matrix[i][0] = 0
for j in range(width):
matrix[0][j] = 0
elif r == 1:
for j in range(width):
matrix[0][j] = 0
elif c == 1:
for i in range(length):
matrix[i][0] = 0
- 颜色分类
这个问题的限制条件比较多,是一个三指针问题。
class Solution:
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
i = 0
pos = 0
j = len(nums) - 1
while(pos <= j): #一定要有等于,可以分类讨论一下
if nums[pos] == 0:
nums[i], nums[pos] = nums[pos], nums[i]
i += 1
pos += 1 #当前位置需要移动,这里也需要分类讨论一下。
elif nums[pos] == 1:
pos += 1
else:
nums[j], nums[pos] = nums[pos], nums[j]
j -= 1
- 组合
按照多叉树的不完全生长来取组合。
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
res = []
def dfs(index, route, count):
if count == k:
res.append(route)
for i in range(index, n+1):
dfs(i+1, route+[i], count+1)
dfs(1, [], 0)
return res
- 搜索旋转排序数组 II
数组中会出现重复元素。
递归写法:
class Solution:
def search(self, nums: List[int], target: int) -> bool:
def search(nums, l, r, target):
while(l <= r):
mid = (l +r) // 2
if nums[mid] == target:
return mid
elif nums[mid] > target:
r = mid - 1
else:
l = mid + 1
return -1
def search_v1(nums, target):
def recursion(nums, l, r):
if l > r:
return -1
mid = (l + r) // 2
if nums[mid] == target:
return mid
if nums[mid] >= nums[l]:
temp = mid #这5行是对右边界的特殊处理。
while(temp > l and nums[temp] == nums[l]):
temp -= 1
if nums[temp] >= target >= nums[l]:
return search(nums, l, temp, target)
else:
return recursion(nums, mid+1, r)
else:
if nums[mid] < target <= nums[r]:
return search(nums, mid+1, r, target)
else:
return recursion(nums, l, mid-1)
return recursion(nums, 0, len(nums)-1)
if search_v1(nums, target) == -1:
return False
else:
return True
非递归写法:
class Solution:
def search(self, nums, target) -> int:
l = 0
r = len(nums) - 1
count = 0
while(l <= r):
mid = (l + r) // 2
if nums[mid] == target:
return True
elif nums[mid] >= nums[l]:
temp = mid
while(temp > l):
if (nums[temp] == nums[l]):
temp -= 1
else:
break
if nums[l] <= target <= nums[temp]:
r = temp
else:
l = mid + 1
else:
if nums[r] >= target > nums[mid]:
l = mid + 1
else:
r = mid - 1
return False
- 删除排序链表中的重复元素 II
这个题用递归写法比较方便。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
if not head or not head.next:
return head
cur = head
if cur.val != cur.next.val:
temp = self.deleteDuplicates(cur.next)
cur.next = temp
return cur
else:
while(cur.next and cur.val == cur.next.val):
cur = cur.next
return self.deleteDuplicates(cur.next)
- 删除排序链表中的重复元素
基本的链表操作,删除一个节点后相当于链表往后移动一位。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
l1 = head
while(l1 and l1.next):
a = l1.val
b = l1.next.val
if a == b:
l1.next = l1.next.next
else:
l1 = l1.next
return head
- 柱状图中最大的矩形
单调栈的一个典型应用。
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
heights.append(0)
stack = []
ans = 0
for i in range(len(heights)):
while(stack and heights[stack[-1]] > heights[i]):
mid = stack.pop()
if not stack:
width = i #一定要注意这一步
else:
left = stack[-1]
width = i - left - 1
ans = max(ans, width*heights[mid])
stack.append(i)
return ans
- 合并两个有序数组
跟这个类似的有合并两个有序链表,在写归并排序的时候也需要合并两个有序的数组。但是这一题给出了一定的限制条件,其中一个数组的长度足够长,并且合并的时候不能开辟额外的空间。这里需要重后往前合并,每次找出较大的一个数放在后面。其余的写法和合并链表一样。
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
k = m + n - 1
m = m-1
n = n-1
while(m >= 0 and n >= 0):
if nums1[m] > nums2[n]:
nums1[k] = nums1[m]
m -= 1
else:
nums1[k] = nums2[n]
n -= 1
k -= 1
if n >= 0: #m有剩余的话就不用考虑了
while(n >= 0):
nums1[k] = nums2[n]
n -= 1
k -= 1
- 子集 II
如果按照二叉树的生长形式是无法去重的,需要按照多叉树的不完全生长方式来去重复。
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
res = []
nums.sort()
length = len(nums)
def dfs(index, route):
res.append(route)
for i in range(index, length):
if i > index and nums[i] == nums[i-1]:
continue
dfs(i+1, route+[nums[i]])
dfs(0, [])
return res
- 复原IP地址
对于当前字符串,最多切4次,每次切的长度不超过3(需要和当前字符串的长度做一个比较0),需要额外写两个函数来保证切出来的字符串部分在0-255之间,并且不能出现00这种形式的字符串。
class Solution:
def restoreIpAddresses(self, s: str) -> List[str]:
res = []
def string2int(s):
sum = 0
for c in s:
sum = sum *10 + int(c)
return sum
def judge(s):
if len(s) > 1:
if s[0] == '0':
return False
return True
def dfs(s, route, count):
if count == 4:
if not s:
res.append('.'.join(route))
return
for i in range(min(3, len(s))):
if 0 <= string2int(s[:i+1]) <= 255 and judge(s[:i+1]):
dfs(s[i+1:], route+[s[:i+1]], count+1)
dfs(s, [], 0)
return res
- 二叉树的中序遍历
非递归写法。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
stack = []
cur = root
res = []
while(cur or stack):
while(cur):
stack.append(cur)
cur = cur.left
temp = stack.pop()
res.append(temp.val)
cur = temp.right
return res
- 不同的二叉搜索树
基本DP。
class Solution:
def numTrees(self, n: int) -> int:
dp = [0]*(n+1)
dp[0] = 1
for i in range(1, n+1):
for j in range(i):
dp[i] += dp[j]*dp[i-1-j]
return dp[-1]
- 验证二叉搜索树
二叉搜索树的每一个节点都有一个范围。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
minNum = -inf
maxNum = inf
def judge(root, minNum, maxNum):
if not root:
return True
if root.val <= minNum or root.val >= maxNum:
return False
return judge(root.left, minNum, root.val) and judge(root.right, root.val, maxNum)
return judge(root, minNum, maxNum)
- 相同的树
简单的递归函数。
# 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 isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
def judge(p, q):
if not p and not q:
return True
if not p:
return False
if not q:
return False
if p.val != q.val:
return False
return judge(p.left, q.left) and judge(p.right, q.right)
return judge(p, q)
- 对称二叉树
BFS和DFS两种办法。
# BFS判断每一层是否对称
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
if not root:
return True
queue = [root]
while(queue):
length = len(queue)
res = []
for _ in range(length):
temp = queue.pop(0)
if temp:
res.append(temp.val)
else:
res.append('null')
if temp:
queue.append(temp.left)
queue.append(temp.right)
res1 = res[:]
res.reverse()
if res1 != res:
return False
return True
# DFS判断每两个节点是否相同。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
def judge(a, b):
if not a and not b:
return True
if not a:
return False
if not b:
return False
if a.val != b.val:
return False
return judge(a.left, b.right) and judge(a.right, b.left)
if not root:
return True
return judge(root.left, root.right)
- 二叉树的锯齿形层次遍历
简单的程序遍历,需要控制每一层的宽度和每一层在树中的深度。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
if not root:
return []
res = []
queue = [root]
depth = 1
while(queue):
length = len(queue)
ans = []
for _ in range(length):
temp = queue.pop(0)
ans.append(temp.val)
if temp.left:
queue.append(temp.left)
if temp.right:
queue.append(temp.right)
if depth % 2 == 0:
ans.reverse()
res.append(ans)
depth += 1
return res
- 三角形最小路径和
入门DP,可以使用状态压缩。
解法1:
class Solution:
def minimumTotal(self, triangle: List[List[int]]) -> int:
length = len(triangle)
dp = [[0]*length for _ in range(length)]
dp[0][0] = triangle[0][0]
for i in range(1, length):
dp[i][i] = dp[i-1][i-1] + triangle[i][i]
for i in range(1, length):
dp[i][0] = dp[i-1][0] + triangle[i][0]
for i in range(1, length):
for j in range(1, i):
dp[i][j] = min(dp[i-1][j], dp[i-1][j-1]) + triangle[i][j]
return min(dp[-1])
解法2:状态压缩,注意是反向状态压缩。
class Solution:
def minimumTotal(self, triangle: List[List[int]]) -> int:
length = len(triangle)
dp = [0]*length
dp[0] = triangle[0][0]
for i in range(1, length):
for j in range(i, -1, -1):
if j == i:
dp[j] = dp[j-1] + triangle[i][j]
elif j == 0:
dp[j] = dp[j] + triangle[i][j]
else:
dp[j] = min(dp[j], dp[j-1]) + triangle[i][j]
return min(dp)
- 买卖股票的最佳时机
入门DP问题。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
if not prices:
return 0
length = len(prices)
dp = [0]*length
for i in range(1, length):
if dp[i-1] > 0:
dp[i] = dp[i-1] + prices[i] - prices[i-1]
else:
dp[i] = prices[i] - prices[i-1]
return max(dp)
- 分发糖果
这个题不是很好想,正常的思路是找到分数最低的一个人,然后给一个糖果,之后在向两边扩展,但是这个操作起来比较困难,并且也不能保证最优解。正确的做法是,给第一个人一个糖果,从左往右扫描,分数比前一个人高的话,就在前一个人的基础上多加一个糖果,否则只给一个糖果;再从后往前扫描,如果前一个人的分数更高且这个人的糖果数没有高于后一个人,那么在后一个人的基础上加一。
class Solution:
def candy(self, ratings: List[int]) -> int:
length = len(ratings)
nums = [0]*length
nums[0] = 1
for i in range(1, length):
if ratings[i] > ratings[i-1]:
nums[i] = nums[i-1] + 1
else:
nums[i] = 1
for i in range(length-2, -1, -1):
if ratings[i] > ratings[i+1] and nums[i] <= nums[i+1]:
nums[i] = nums[i+1] + 1
return sum(nums)
- 复制带随机指针的链表
深拷贝操作。这个题目的正常思路先复制每一个节点,然后再来处理随机指针的赋值的问题。但现在的主要问题是节点复制之后无法知道随机指针的指向,现在有两种办法,一个用字典来存储节点复制前和复制后的关联信息,还有一个是将原始链表扩充成原来的两倍,然后再拆分成两条链表。
解法1:
"""
# Definition for a Node.
class Node:
def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
self.val = int(x)
self.next = next
self.random = random
"""
class Solution:
def copyRandomList(self, head: 'Node') -> 'Node':
if not head:
return None
Dict = {None:None} #处理节点为空的情况
temp = head
while(temp):
node = Node(temp.val)
Dict[temp] = node
temp = temp.next
temp = head
while(temp):
Dict[temp].next = Dict[temp.next]
Dict[temp].random = Dict[temp.random]
temp = temp.next
return Dict[head]
解法2:
"""
# Definition for a Node.
class Node:
def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
self.val = int(x)
self.next = next
self.random = random
"""
class Solution:
def copyRandomList(self, head: 'Node') -> 'Node':
if not head:
return None
temp = head
while(temp):
node = Node(temp.val)
node.next = temp.next
temp.next = node
temp = temp.next.next
temp = head
while(temp):
if temp.random:
temp.next.random = temp.random.next #注意random指针可能为空的情况
temp = temp.next.next
newhead = head.next
l = newhead
temp = head.next.next
head.next = head.next.next
while(temp):
l.next = temp.next
l = l.next
temp.next = temp.next.next
temp = temp.next
return newhead
- 单词拆分
简单的DFS+剪枝
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
def dfs(s, Dict):
if not s:
return True
if s in Dict:
return Dict[s]
for i in range(len(s)):
if s[:i+1] in wordDict:
res = dfs(s[i+1:], Dict)
if res:
Dict[s] = True
return Dict[s]
Dict[s] = False
return Dict[s]
return dfs(s, {})
- 单词拆分 II
这个题要比上一个麻烦一点,需要记录单词切分的路径。需要去除不可切分的状态。
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> List[str]:
res = []
def dfs(s, route, Dict):
if not s:
res.append(" ".join(route))
return True
if s in Dict:
if not Dict[s]:
return False
flag = 0
for i in range(len(s)):
if s[:i+1] in wordDict:
ans = dfs(s[i+1:], route+[s[:i+1]], Dict)
if ans:
flag = 1
if flag == 0:
Dict[s] = False
else:
Dict[s] = True
return Dict[s]
dfs(s, [], {})
return res
- 环形链表
快慢指针。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: ListNode) -> bool:
if not head or not head.next:
return False
slow = head
fast = head.next
while(fast and fast.next):
if slow == fast:
return True
slow = slow.next
fast = fast.next.next
return False
- 环形链表 II
快慢指针找环的入口。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
if not head or not head.next:
return None
flag = 0
slow = head
fast = head.next
while(fast and fast.next):
if slow == fast:
flag = 1
break
slow = slow.next
fast = fast.next.next
if not flag:
return None
else:
fast = None
while(fast != slow):
if not fast:
fast = head
else:
fast = fast.next
slow = slow.next
return slow
- 二叉树的前序遍历
需要使用迭代算法。二叉树的迭代遍历一共四种情况。前序、中序、后序、层序。
参考:https://leetcode-cn.com/problems/binary-tree-preorder-traversal/solution/tu-jie-er-cha-shu-de-si-chong-bian-li-by-z1m/
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
cur = root
stack = []
res = []
while(cur or stack):
while(cur):
stack.append(cur)
res.append(cur.val)
cur = cur.left
temp = stack.pop()
cur = temp.right
return res
- 二叉树的后序遍历
同样需要使用迭代写法。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
cur = root
stack = []
res = []
while(cur or stack):
while(cur):
stack.append(cur)
cur = cur.left
temp = stack[-1]
if not temp.right:
res.append(stack.pop().val)
else:
cur = temp.right
temp.right = None
return res
- 最小栈
正常的栈是无法实现这种功能的,因此需要使用额外的空间来存储最小值。
class MinStack:
def __init__(self):
"""
initialize your data structure here.
"""
self.stack_A = []
self.stack_B = []
def push(self, x: int) -> None:
self.stack_A.append(x)
if not self.stack_B:
self.stack_B.append(x)
else:
if x <= self.stack_B[-1]:
self.stack_B.append(x)
def pop(self) -> None:
temp = self.stack_A.pop()
if temp == self.stack_B[-1]:
self.stack_B.pop()
def top(self) -> int:
return self.stack_A[-1]
def getMin(self) -> int:
return self.stack_B[-1]
# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()
- 相交链表
比较笨的那种方法我就不说了,当一个链表先走到最后位置的时候直接跳转到另一个链表上,如果两个链表没有交点,那么这两个链表都会走到最后的空节点来结束程序。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
l1 = headA
l2 = headB
while(l1 != l2):
if l1:
l1 = l1.next
else:
l1 = headB
if l2:
l2 = l2.next
else:
l2 = headA
return l1
- 阶乘后的零
找5和2的个数,5的个数较少,再找5的个数,不停的除以5即可
class Solution:
def trailingZeroes(self, n: int) -> int:
res = 0
while(n):
res += n // 5
n = n //5
return res
- 最大数
由于这个题中的每一个数字存在多位数,无法通过先排序再用DFS的方式来去重,使用DFS的时间复杂度就是N!,只能通过特殊的排序方式来解决这个问题。cmp_to_key在functools这个包里。
class Solution:
def largestNumber(self, nums: List[int]) -> str:
nums = list(map(str, nums))
def compare(x, y):
if x+y < y+x:
return 1
elif x+y == y+x:
return 0
else:
return -1
nums.sort(key = cmp_to_key(compare))
res = ''.join(nums)
if len(res) > 1 and res[0] == '0':
return '0'
return res
- 旋转数组
数组的基本操作。
解法1:每次只移动一位,从后往前移动,还需要额外定义一个变量。
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
length = len(nums)
k = k % length
for _ in range(k):
temp = nums[-1]
for j in range(length-1, 0, -1):
nums[j] = nums[j-1]
nums[0] = temp
解法2:用双指针进行反转
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
k = k % len(nums)
def rota(nums, i , j):
while(i < j):
nums[i], nums[j] = nums[j], nums[i]
i += 1
j -= 1
rota(nums, 0, len(nums)-1)
rota(nums, 0, k - 1)
rota(nums, k, len(nums)-1)
- 打家劫舍
基本DP。
class Solution:
def rob(self, nums: List[int]) -> int:
length = len(nums)
if length < 1:
return 0
if length < 2:
return nums[0]
dp = [0]*length
dp[0] = nums[0]
dp[1] = max(nums[1], nums[0])#要注意这一个操作。
for i in range(2, length):
dp[i] = max(dp[i-1], nums[i]+dp[i-2])
return max(dp)
- 计数质数
素数筛。
class Solution:
def countPrimes(self, n: int) -> int:
flags = [0]*n
for i in range(2, n):
if flags[i] == 0:
for j in range(2, n):
if i*j > n-1:
break
flags[i*j] = 1
count = 0
for i in range(2, n):
if flags[i] == 0:
count += 1
return count
- 存在重复元素
排序,字典都可以。
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
return len(set(nums)) < len(nums)
- 用队列实现栈
与这个类似的有用两个栈实现一个队列,用两个队列实现一个栈。用一个队列实现栈。用两个队列实现栈的时候需要取队列的长度,python中len()操作的时间复杂度为O(1)。
用两个队列:
class MyStack:
def __init__(self):
"""
Initialize your data structure here.
"""
self.queue_A = []
self.queue_B = []
def push(self, x: int) -> None:
"""
Push element x onto stack.
"""
self.queue_A.append(x)
def pop(self) -> int:
"""
Removes the element on top of the stack and returns that element.
"""
while(len(self.queue_A) > 1):
self.queue_B.append(self.queue_A.pop(0))
res = self.queue_A.pop(0)
while(self.queue_B):
self.queue_A.append(self.queue_B.pop(0))
return res
def top(self) -> int:
"""
Get the top element.
"""
while(len(self.queue_A) > 1):
self.queue_B.append(self.queue_A.pop(0))
res = self.queue_A.pop(0)
self.queue_B.append(res)
while(self.queue_B):
self.queue_A.append(self.queue_B.pop(0))
return res
def empty(self) -> bool:
"""
Returns whether the stack is empty.
"""
if self.queue_A:
return False
else:
return True
# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()
用一个队列:将前面的n-1个数全部反转。
class MyStack:
def __init__(self):
"""
Initialize your data structure here.
"""
self.queue_A = []
def push(self, x: int) -> None:
"""
Push element x onto stack.
"""
self.queue_A.append(x)
length = len(self.queue_A)
i = 0
while(i < length -1):
self.queue_A.append(self.queue_A.pop(0))
i += 1
def pop(self) -> int:
"""
Removes the element on top of the stack and returns that element.
"""
return self.queue_A.pop(0)
def top(self) -> int:
"""
Get the top element.
"""
return self.queue_A[0]
def empty(self) -> bool:
"""
Returns whether the stack is empty.
"""
if self.queue_A:
return False
else:
return True
# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()
- 滑动窗口最大值
需要维护一个单调不增双端队列,需要双端的原因是因为滑动窗口在移动的时候两端都会有信息更新,单调不增的原因是因为要防止较小的数字丢失,由于这里是要取最大值,当遇到较大的元素,比这个小的元素都可以出队列。
这个题的核心代码就7行。
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
queue = []
res = []
for i in range(k):
if not queue or queue[-1] >= nums[i]:
queue.append(nums[i])
else:
while(queue and queue[-1]< nums[i]):
queue.pop()
queue.append(nums[i])
res.append(queue[0])
for i in range(k, len(nums)):
if not queue or queue[-1] >= nums[i]:
queue.append(nums[i])
else:
while(queue and queue[-1]< nums[i]):
queue.pop()
queue.append(nums[i])
if queue[0] == nums[i-k]:
queue.pop(0)
res.append(queue[0])
return res
- 最长上升子序列
经典DP问题,如果需要找到所有的最优解只能用DP,但是DP的时间复杂度为O(N2),但是如果说只需要找到最长上升子序列的长度的话,就只需要利用贪心和二分维护一个递增列表,最终返回这个列表的长度即可。
解法1:
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
if not nums:
return 0
length = len(nums)
dp = [1]*length
for i in range(1, length):
for j in range(i):
if nums[i] > nums[j]:
dp[i] = max(dp[i], dp[j]+1) #每一种情况都需要考虑所以要取最大值。
return max(dp)
解法2:这里的二分需要说明一下,这里需要维护一个递增的列表,但是原数组会出现重复元素的情况,因此会有当前元素等于递增列表中的某一个元素的情况,这个时候返回该元素的下标,就相当于没有进行修改。但是递增的列表中是没有重复元素的,并且递增的列表中一定存在元素大于或等于当前元素。
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
if not nums:
return 0
temp = []
def search(nums, target):
l = 0
r = len(nums) - 1
while(l <= r):
mid = (l +r) // 2
if nums[mid] < target:
l = mid + 1
elif nums[mid] > target:
r = mid - 1
else:
return mid
return l
for num in nums:
if not temp or temp[-1] < num:
temp.append(num)
else:
cur = search(temp, num)
temp[cur] = num
return len(temp)
这里补充一下最长上升子序列的一个进阶题,现在是二维的,我们先要先对第一维度按照升序排列,这个时候我们就只需要对第二维度求一个最长上升子序列即可。有一个需要注意的是,如果第一维度相同,那么第二维度按照降序来,这个非常重要,不然会出错。其他的就没啥了,力扣354就是这样的一个题。
class Solution:
def maxEnvelopes(self, envelopes: List[List[int]]) -> int:
if not envelopes:
return 0
def compare(x, y):
if x[0] > y[0] or (x[0] == y[0] and x[1] < y[1]):
return 1
elif x[0] == y[0] and x[1] == y[1]:
return 0
else:
return -1
def search(nums, target):
l = 0
r = len(nums)-1
while(l <= r):
mid = (l+r) // 2
if nums[mid] == target:
return mid
elif nums[mid] > target:
r = mid - 1
else:
l = mid + 1
return l
envelopes.sort(key = cmp_to_key(compare))
print(envelopes)
temp = []
for env in envelopes:
print(env)
if not temp or env[1] > temp[-1]:
temp.append(env[1])
else:
cur = search(temp, env[1])
temp[cur] = env[1]
return len(temp)
第二种写法
class Solution:
def maxEnvelopes(self, envelopes: List[List[int]]) -> int:
if not envelopes:
return 0
def search(nums, target):
l = 0
r = len(nums)-1
while(l <= r):
mid = (l+r) // 2
if nums[mid] == target:
return mid
elif nums[mid] > target:
r = mid - 1
else:
l = mid + 1
return l
envelopes.sort(key = lambda x: (x[0], -x[1]))
temp = []
for env in envelopes:
print(env)
if not temp or env[1] > temp[-1]:
temp.append(env[1])
else:
cur = search(temp, env[1])
temp[cur] = env[1]
return len(temp)
- 戳气球
经典DP。
class Solution:
def maxCoins(self, nums: List[int]) -> int:
nums.insert(0, 1)
nums.append(1)
length = len(nums)
dp = [[0]*length for _ in range(length)]
for r in range(2, length):
for i in range(length-r):
j = i + r
for k in range(i+1, j):
dp[i][j] = max(dp[i][j], dp[i][k] + dp[k][j] + nums[i]*nums[k]*nums[j])
return dp[0][-1]
- 零钱兑换
完全背包问题。有两种写法,但是为了和01背包统一一般按照这种写法。
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
dp = [inf]*(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[-1] == inf:
return -1
else:
return dp[-1]
- 有效的完全平方数
二分为最优解法。
class Solution:
def isPerfectSquare(self, num: int) -> bool:
l = 1
r = num
while(l <= r):
mid = (l + r)// 2
if mid*mid == num:
return True
elif mid*mid < num:
l = mid + 1
else:
r = mid - 1
return False
- 移掉K位数字
这个题目也是一个单调栈的应用。要求剩下的序列最小,那么希望是升序的,又要求在O(N)的时间复杂度以内就只能使用单调栈了。
class Solution:
def removeKdigits(self, num: str, k: int) -> str:
stack = []
remain = len(num) - k
for c in num:
while(k and stack and stack[-1] > c):
stack.pop()
k -= 1
stack.append(c)
res = stack[0:remain]
temp = ''.join(res).lstrip('0')
if not temp:
return '0'
else:
return temp
- 用 Rand7() 实现 Rand10()
做这个题之前,我们需要知道从random10生成random7是非常简单的,现在我们需要保证数值能够覆盖1-10,并且概率也需要相等。
# 剔除的数字越少,算法效率越高。
# The rand7() API is already defined for you.
# def rand7():
# @return a random integer in the range 1 to 7
class Solution:
def rand10(self):
"""
:rtype: int
"""
while(True):
x = rand7()*7 + rand7()
if x >= 11 and x<=50:
break
return x % 10 + 1
- 零钱兑换 II
完全背包问题。
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp = [0]*(amount+1)
dp[0] = 1
for coin in coins:
for i in range(coin, amount+1):
dp[i] = dp[i] + dp[i-coin]
return dp[-1]
- 24 点游戏
这一题本质就是暴力,因为24的状态是可以枚举的,一共就4个数字,但是这一题的括号不是很好处理,并且减法和除法操作需要考虑数字交换的问题。我们可以这样考虑,对于一个数组,我们每次取出两个元素,进行加减乘除运算,也就是6种可能。这样得到了新的结果,除去已经使用的元素这样就得到了一个新的数组,再次来进行递归操作即可。
class Solution:
def judgePoint24(self, nums: List[int]) -> bool:
def dfs(nums):
if len(nums) == 1:
if abs(nums[0] - 24) < 1e-6:
return True
return False
for i in range(len(nums)-1):
for j in range(i+1, len(nums)):
new_nums = []
for k in range(len(nums)):
if k != i and k != j:
new_nums.append(nums[k])
if dfs(new_nums + [nums[i]+nums[j]]):
return True
if dfs(new_nums + [nums[i]*nums[j]]):
return True
if dfs(new_nums + [nums[i]-nums[j]]):
return True
if dfs(new_nums + [nums[j]-nums[i]]):
return True
if nums[i]:
if dfs(new_nums + [nums[j] / nums[i]]):
return True
if nums[j]:
if dfs(new_nums + [nums[i] / nums[j]]):
return True
return dfs(nums)
- 每日温度
单调栈。
class Solution:
def dailyTemperatures(self, T: List[int]) -> List[int]:
res = [0]*len(T)
stack = []
for i in range(len(T)):
while(stack and T[stack[-1]] < T[i]):
temp = stack.pop()
res[temp] = i - temp
stack.append(i)
return res
- 最长公共子序列
经典DP问题。状态压缩不是很好写。
解法1:
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
length1 = len(text1) + 1
length2 = len(text2) + 1
dp = [[0]*length2 for _ in range(length1)]
for i in range(1, length1):
for j in range(1, length2):
if text1[i-1] == text2[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]
解法2:状态压缩
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
length1 = len(text1) + 1
length2 = len(text2) + 1
dp = [0]*length2
for i in range(1, length1):
pre = 0 #存斜对角的状态,每行需要初始化为0。
for j in range(1, length2):
tmp = dp[j]
if text1[i-1] == text2[j-1]:
dp[j] = pre + 1
else:
dp[j] = max(dp[j-1], dp[j])
pre = tmp #更新之前的状态。
return dp[-1]
解法3:补充
找到最优的子串(找路径)
这个只能够二维DP。
def findpath(dp, text1, text2):
i = len(text1)
j = len(text2)
res = []
while(i > 0 and j > 0):
if dp[i][j] == dp[i-1][j-1] + 1:
res.append(tex1[i-1])
elif dp[i][j] == dp[i-1][j]:
i -= 1
else:
j -= 1
res.reverse()
return res
- 通过投票对团队排名
这个题不要转换成字符串来比较大小,比如10 > 9但是'10' < '9',python可以直接比较列表的大小。
class Solution:
def rankTeams(self, votes: List[str]) -> str:
Dict = {}
k = len(votes[0])
for vote in votes:
for index, c in enumerate(vote):
if c not in Dict:
Dict[c] = [0]*k
Dict[c][index] += 1
# for c in Dict:
# Dict[c] = ''.join(list(map(str, Dict[c])))
temp = list(Dict.items())
temp.sort(key = lambda x:(x[1], -ord(x[0])), reverse=True)
return ''.join(dict(temp).keys())