leetcode
- 4. 寻找两个正序数组的中位数
- 5. 最长回文子串
- 11.盛最多水的容器
- 15.三数之和
- 17.电话号码的数字组合
- 22. 括号生成
- 23. 合并K个升序链表
- 25. K 个一组翻转链表
- 31.下一个排列
- 32.最长有效括号
- 33.搜索旋转排序数组
- 72.编辑距离
- 76. 最小覆盖子串
- 148. 排序链表
- 207.课程表
- 215.数组中的第k个最大元素
- 234.回文链表
- 300.最长上升子序列
- 301.删除无效的括号
- 312.戳气球
- 322.零钱兑换
- 338.比特位计数
- 394.字符串编码
- 399.除法求值
- 406. 根据身高重建队列
- 416. 分割等和子集
- 437. 路径总和 III
- 438. 找到字符串中所有字母异位词
- 494.目标和
- 581.最短无序连续子数组
- 621. 任务调度器
- 647. 回文子串
- 739.每日温度
4. 寻找两个正序数组的中位数
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
思路: 二分查找两个数组中排序为k的值或k,k+1的均值,每次两个数组各取第前k/2个比较,小的那个以及后面都淘汰,该数组指数右移到后一位,循环操作,直到k==1或有一个数组淘汰完。
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
def getkth(k):
offset1 = offset2 = 0
while True:
# 终止条件,有一个数组到底,返回另一数组第k个值,k=1返回当前位置min
if len(nums1) == offset1:
return nums2[offset2 + k - 1]
if len(nums2) == offset2:
return nums1[offset1 + k - 1]
if k == 1:
return min(nums1[offset1], nums2[offset2])
# 计算新节点位置,注意控制边界
noffset1 = min(offset1 + k//2 - 1, len(nums1) - 1)
noffset2 = min(offset2 + k//2 - 1, len(nums2) - 1)
# 比较两个值,k减去淘汰数,offset右移后一位
if nums1[noffset1] < nums2[noffset2]:
k -= noffset1 - offset1 + 1
offset1 = noffset1 + 1
else:
k -= noffset2 - offset2 + 1
offset2 = noffset2 + 1
s = len(nums1) + len(nums2)
if s % 2 == 0:
return (getkth(s//2) + getkth(s//2 + 1))/ 2
return getkth(s // 2 + 1)
5. 最长回文子串
给你一个字符串 s,找到 s 中最长的回文子串。
思路: 动态规划二维数组i到j为回文则为True,
class Solution:
def longestPalindrome(self, s: str) -> str:
n = len(s)
max_len, begin = 1, 0
dp = [[False]*n for i in range(n)]
for L in range(n):
for left in range(n):
if left + L >= n:
break
right = left + L
if s[left] == s[right]:
if L <= 2:
dp[left][right] = True
else:
dp[left][right] = dp[left+1][right-1]
if dp[left][right] and right - left + 1 > max_len:
max_len = right - left + 1
begin = left
return s[begin: begin + max_len]
11.盛最多水的容器
给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器。
class Solution:
def maxArea(self, height: List[int]) -> int:
j, i, res = len(height) - 1, 0, 0
while i < j:
if height[i] < height[j]:
res = max(res, (j-i)*height[i])
i += 1
else:
res = max(res, (j-i)*height[j])
j -= 1
return res
15.三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
思路: 双指针收缩寻找和为0,找到和为0避免重复,比较后一数字是否相同,相同跳过。
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
if len(nums) < 3:
return []
nums.sort()
ret = []
for i in range(len(nums) - 2):
# 提前结束
if nums[i] > 0:
break
# 避免重复
if i > 0 and nums[i] == nums[i-1]:
continue
L, R = i + 1, len(nums) - 1
while L < R:
s = nums[i] + nums[L] + nums[R]
if s == 0:
ret.append([nums[i],nums[L],nums[R]])
# 避免重复
while L < R and nums[L] == nums[L+1]:
L += 1
while L < R and nums[R] == nums[R-1]:
R -= 1
# 找到后收缩
L += 1
R -= 1
elif s > 0:
R -= 1
else:
L += 1
return ret
17.电话号码的数字组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
if not digits: return []
phoneMap = {
"2": "abc",
"3": "def",
"4": "ghi",
"5": "jkl",
"6": "mno",
"7": "pqrs",
"8": "tuv",
"9": "wxyz",
}
ret, cached = [], []
def dfs(k):
if k == len(digits):
ret.append("".join(cached))
return
for c in phoneMap[digits[k]]:
cached.append(c)
dfs(k + 1)
cached.pop()
dfs(0)
return ret
22. 括号生成
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
思路: dfs,这里对左右括号数同时搜索,左括号比右括号少直接返回,小于n时放入括号,最后返回结果
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
if n <= 0:
return []
ret = []
cached = []
def dfs(l, r):
if l == n and r == n:
ret.append(''.join(cached))
return
if l < r:
return
if l < n:
cached.append('(')
dfs(l + 1, r)
cached.pop()
if r < n:
cached.append(')')
dfs(l, r + 1)
cached.pop()
dfs(0, 0)
return ret
23. 合并K个升序链表
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
思路: heapq模拟优先队列,现将每个链表头入堆,链表指针指向下一个,然后出队列,每次出将非空的当前链表下一个入队列,最后返回
# 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:
prior = []
root = head = ListNode(-1)
for i in range(len(lists)):
if lists[i]:
heapq.heappush(prior, (lists[i].val, i))
lists[i] = lists[i].next
while prior:
v, i = heapq.heappop(prior)
root.next = ListNode(v)
root = root.next
if lists[i]:
heapq.heappush(prior, (lists[i].val, i))
lists[i] = lists[i].next
return head.next
分治:
# 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:
n = len(lists)
def merge(l1, l2):
if not l1: return l2
if not l2: return l1
if l1.val < l2.val:
l1.next = merge(l1.next, l2)
return l1
else:
l2.next = merge(l1, l2.next)
return l2
def mergeList(left, right):
if left == right:
return lists[left]
mid = left + (right - left) // 2
l = mergeList(left, mid)
r = mergeList(mid + 1, right)
return merge(l, r)
if not lists: return None
return mergeList(0, len(lists) - 1)
25. K 个一组翻转链表
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
进阶:
你可以设计一个只使用常数额外空间的算法来解决此问题吗?
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
思路: 记录pre和end(开始时相同),找到要交换的start(pre.next)和end(走k格),存储end.next,然后置为None(方便翻转),rev进行交换返回翻转后的头结点,pre接上这个节点,start接上保存的next,然后进入下个循环。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
def rev(st):
pre = None
cur = st
while cur != None:
nxt = cur.next
cur.next = pre
pre = cur
cur = nxt
return pre
L = ListNode(-1)
L.next = head
pre, end = L, L
while end.next != None:
for i in range(k):
end = end.next
if not end: break
if not end: break
nxt = end.next
end.next = None
sta = pre.next
pre.next = rev(sta)
sta.next = nxt
pre = sta
end = pre
return L.next
31.下一个排列
实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须 原地 修改,只允许使用额外常数空间。
从后面找到第一个升序位置,从后面找到第一个大与该位置值,进行交换,然后把后面的升序部分reverse
class Solution:
def nextPermutation(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
for i in range(len(nums)-1,-1,-1):
if i >= 1 and nums[i-1] < nums[i]:
for j in range(len(nums)-1,i-1,-1):
if nums[j] > nums[i-1]:
nums[i-1], nums[j] = nums[j], nums[i-1]
break
break
l, r = i, len(nums) - 1
while l < r:
nums[l], nums[r] = nums[r], nums[l]
l += 1
r -= 1
return
32.最长有效括号
给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度
**思路:**两次遍历,从左到右,计数左右括号,然后左右相等时取max,右大于左归0,但这样会漏掉一种情况,就是遍历的时候左括号的数量始终大于右括号的数量,即 ((), 所以要在进行一次,从右往左反之。
class Solution:
def longestValidParentheses(self, s: str) -> int:
l = r = 0
max_len = 0
for i in range(len(s)):
if s[i] == '(': l += 1
else: r += 1
if l == r: max_len = max(max_len, l + r)
elif l < r: l = r = 0
l = r = 0
for i in range(len(s) - 1, -1, -1):
if s[i] == '(': l += 1
else: r += 1
if l == r: max_len = max(max_len, l + r)
elif l > r: l = r = 0
return max_len
辅助栈,栈顶保存匹配前一个index
class Solution:
def longestValidParentheses(self, s: str) -> int:
stack = [-1]
max_len = 0
for i,c in enumerate(s):
if c == '(':
stack.append(i)
else:
stack.pop()
if stack:
max_len = max(i - stack[-1], max_len)
else: stack.append(i)
return max_len
动态规划:
遍历s,如果当前位置为’)’,前一位置为’(’,则记为前两个位置加2,若当前位置为’)’,前一位置为’)’,则进入未匹配位置(s[i - dp[i - 1] - 1])判断,若这个位置为’(’,匹配成功,当前位置 加上 dp[i - 1] 和 dp[i - dp[i - 1] - 2] 以及 2。
class Solution:
def longestValidParentheses(self, s: str) -> int:
max_len = 0
n = len(s)
dp = [0]*n
for i in range(1, n):
if s[i] == ')':
if s[i-1] == '(':
dp[i] = dp[i - 2] + 2 if i >= 2 else 2
elif i > dp[i - 1] and s[i - dp[i - 1] - 1] == '(':
dp[i] = dp[i-1] + (dp[i - dp[i - 1] - 2] + 2 if i >= dp[i - 1] + 2 else 2)
max_len = max(max_len, dp[i])
return max_len
33.搜索旋转排序数组
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
二分:
class Solution:
def search(self, nums: List[int], target: int) -> int:
l, r = 0, len(nums) - 1
# 终止条件带等号
while l <= r:
mid = l + (r - l)//2
if target == nums[mid]:
return mid
# 划分mid属于哪个区间 》= 0,都是前面
if nums[mid] >= nums[0]:
if nums[0] <= target < nums[mid]:
r = mid - 1
else: l = mid + 1
else:
if nums[mid] < target <= nums[-1]:
l = mid + 1
else: r = mid - 1
return -1
72.编辑距离
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
插入一个字符
删除一个字符
替换一个字符
动态规划:
d
p
[
i
−
1
]
[
j
]
插
入
一
个
字
符
d
p
[
i
]
[
j
−
1
]
删
除
一
个
字
符
d
p
[
i
−
1
]
[
j
−
1
]
替
换
一
个
字
符
dp[i-1][j] 插入一个字符 \\ dp[i][j-1] 删除一个字符 \\ dp[i-1][j-1]替换一个字符
dp[i−1][j]插入一个字符dp[i][j−1]删除一个字符dp[i−1][j−1]替换一个字符
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
m, n = len(word1), len(word2)
if m * n == 0: return m + n
dp = [[0]*(n + 1) for _ in range(m + 1)]
for i in range(1, m + 1):
dp[i][0] = dp[i-1][0] + 1
for j in range(1, n + 1):
dp[0][j] = dp[0][j-1] + 1
for i in range(1, m + 1):
for j in range(1, n + 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], min(dp[i-1][j], dp[i][j-1])) + 1
return dp[-1][-1]
76. 最小覆盖子串
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
思路: 滑动窗口,前后两个指针构成滑动窗口,每次右移右指针,如果当前字符计数大于0,减去need计数,每次遍历都在cnt哈希表中减一,当need计数为0时,说明当前窗口已经包含了,这时要找最小窗口,开始循环左移左指针,直到左指针位置哈希表计数为0,说明当前位置不可删除,当前最小窗口找到了,跟之前比较去最小值,然后左移一位左指针,遍历下一个窗口。
class Solution:
def minWindow(self, s: str, t: str) -> str:
cnt = collections.defaultdict(int)
for c in t:
cnt[c] += 1
need = len(t)
i = 0
res = (0, len(s) + 1)
for j in range(len(s)):
if cnt[s[j]] > 0:
need -= 1
cnt[s[j]] -= 1
if need == 0:
while True:
if cnt[s[i]] == 0:
break
cnt[s[i]] += 1
i += 1
if j - i < res[1] - res[0]:
res = (i, j)
cnt[s[i]] += 1
i += 1
need += 1
return s[res[0]:res[1] + 1] if res[1] != len(s) + 1 else ''
148. 排序链表
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
进阶:
你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?
思路:
归并排序
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def sortList(self, head: ListNode) -> ListNode:
if not head or not head.next:
return head
slow = head
fast = head.next
while fast and fast.next:
fast = fast.next.next
slow = slow.next
mid, slow.next = slow.next, None
l = self.sortList(head)
r = self.sortList(mid)
root = res = ListNode()
while l and r:
if l.val < r.val:
root.next = l
l = l.next
else:
root.next = r
r = r.next
root = root.next
root.next = l if l else r
return res.next
207.课程表
你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。
例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。
思路: 判断图里有没有环,有环说明是不行的
dfs
class Solution:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
dic = collections.defaultdict(set)
for i, j in prerequisites:
dic[j].add(i)
visited = [0] * numCourses
self.hasring = False
def dfs(k):
visited[k] = 1
for node in dic[k]:
if visited[node] == 1:
self.hasring=True
return
elif visited[node] == 0:
dfs(node)
if self.hasring: return
visited[k] = 2
for i in range(numCourses):
if visited[i] == 0:
dfs(i)
if self.hasring: return False
return True
bfs
记录每个节点的入度,入度为0可以访问进队列,记录访问数做判断
class Solution:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
dic = collections.defaultdict(set)
indegree = [0] * numCourses
for i, j in prerequisites:
dic[j].add(i)
indegree[i] += 1
visited = 0
q = []
for i in range(numCourses):
if indegree[i] == 0:
q.append(i)
while q:
k = q.pop(0)
visited += 1
for node in dic[k]:
indegree[node] -= 1
if indegree[node] == 0:
q.append(node)
return visited == numCourses
215.数组中的第k个最大元素
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
1.维护k个元素小顶堆,把大于堆顶元素入堆
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
h = []
for i in range(k):
heapq.heappush(h,nums[i])
for i in range(k,len(nums)):
if nums[i] > h[0]:
heapq.heappushpop(h, nums[i])
return h[0]
2.快排分治
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
def quick_sort(begin, end):
if begin >= end: return
i, j = begin, end
tar = nums[begin]
while i < j:
while i < j and nums[j] <= tar:
j -= 1
while i < j and nums[i] >= tar:
i += 1
nums[i], nums[j] = nums[j], nums[i]
nums[i], nums[begin] = nums[begin], nums[i]
if i < k - 1:
quick_sort(i+1, end)
elif i > k - 1:
quick_sort(begin, i - 1)
return
n=len(nums)
quick_sort(0, n-1)
return nums[k-1]
234.回文链表
请判断一个链表是否为回文链表。
反转其半部分再判定
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
slow = fast = head
pre=prepre=None
while fast and fast.next:
fast = fast.next.next
pre = slow
slow = slow.next
pre.next = prepre
prepre = pre
if fast: slow = slow.next
while slow and pre:
if pre.val != slow.val:
return False
pre = pre.next
slow = slow.next
return True
300.最长上升子序列
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
动态规划,每个位置去找之前位置比自己小的数的最长长度,在此基础上加1
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
dp = [1] * len(nums)
max_len = 1
for i in range(1, len(nums)):
for j in range(i):
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j] + 1)
max_len = max(max_len, dp[i])
return max_len
二分
维护tail数组,tail数组保存以数组下标结尾的上升序列的最后一个数,每次遍历二分查找当前数n在tail中位置,插入到比n大一些的位置,如果插入位置是当前右指针,则右移右指针,以为二分j=m,遇到大于当前tail中所有数字才会达到右指针位置。res 则为结果
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
tails, res = [0] * len(nums), 0
for n in nums:
i, j = 0, res
while i < j:
m = (i+j) // 2
if tails[m] < n: i = m + 1
else: j = m
tails[i] = n
if i == res: res += 1
return res
301.删除无效的括号
给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。
返回所有可能的结果。答案可以按 任意顺序 返回。
递归回溯,先遍历找出多余的括号数
dfs,根据当前char判断选项,若为括号可以选择删或者不删,记录现有的lc,rc用于剪枝
class Solution:
def removeInvalidParentheses(self, s: str) -> List[str]:
lremove = rremove = 0
for c in s:
if c == '(': lremove += 1
elif c == ')':
if lremove > 0: lremove -= 1
else: rremove += 1
res = set()
def dfs(idx, l, r, ll, rr, st):
if idx == len(s):
if not ll and not rr:
res.add(st)
return
if s[idx] == '(' and ll:
dfs(idx+1, l, r, ll-1, rr, st)
elif s[idx] == ')' and rr:
dfs(idx+1, l, r, ll, rr-1, st)
if s[idx] not in '()':
dfs(idx+1, l, r, ll, rr, st+s[idx])
elif s[idx] == '(':
dfs(idx+1, l+1, r, ll, rr, st+s[idx])
elif l > r:
dfs(idx+1, l, r+1, ll, rr, st+s[idx])
return
dfs(0,0,0,lremove,rremove,'')
return list(res)
312.戳气球
有 n 个气球,编号为0 到 n - 1,每个气球上都标有一个数字,这些数字存在数组 nums 中。
现在要求你戳破所有的气球。戳破第 i 个气球,你可以获得 nums[i - 1] * nums[i] * nums[i + 1] 枚硬币。 这里的 i - 1 和 i + 1 代表和 i 相邻的两个气球的序号。如果 i - 1或 i + 1 超出了数组的边界,那么就当它是一个数字为 1 的气球。
求所能获得硬币的最大数量。
思路: 动态规划,现在数组两边做两个哨兵,dp[i][j]表示i,j开区间的戳气球最大分数,对区间长度进行遍历,从小到大,每个区间计算分数
class Solution:
def maxCoins(self, nums: List[int]) -> int:
nums = [1] + nums + [1]
n = len(nums)
dp = [[0]*n for _ in range(n)]
ret = 0
def chuo(i, j):
res = 0
for k in range(i+1, j):
res = max(res, dp[i][k] + nums[i]*nums[k]*nums[j] +dp[k][j])
return res
for L in range(2, n):
for i in range(0, n - L):
j = i + L
dp[i][j] = chuo(i, j)
return dp[0][-1]
322.零钱兑换
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
思路: dp数组保存每个amount对应的最少数量,每次从减掉coin从头找
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
dp = [-1] * (amount + 1)
coins.sort()
dp[0] = 0
for i in range(1, amount + 1):
m = float('inf')
for coin in coins:
if coin > i: break
if dp[i-coin] != -1:
m = min(m, dp[i-coin]+1)
if m != float('inf'):
dp[i] = m
return dp[-1]
338.比特位计数
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
分奇偶,奇数为前一个偶数加一,偶数为除2对应值。
class Solution:
def countBits(self, n: int) -> List[int]:
dp = [0]*(n+1)
for i in range(1, n+1):
if i & 1:
dp[i] = dp[i-1] + 1
else:
dp[i] = dp[i//2]
return dp
394.字符串编码
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。
两种方法区别:栈方法先处理前面的结果然后入栈,入栈时处理下个括号的,所以初始化,出栈的时候已经处理完了括号的,出栈的是括号之前的;dfs方法先处理括号内的返回括号内的然后跟当前拼接,所以返回的是后面括号位置的值,再由当前multi进行乘,遇到有括号返回当前res,每次进入函数时初始化。
思路: 借助栈,栈里保存之前的字符串,和括号前的数字(表示括号里重复几次),每次遇到左括号,说明要先处理括号内的,所以进栈,遇到有括号,说明当前位置处理完毕,出栈得到之前res和当前乘数,其他正常处理
class Solution:
def decodeString(self, s: str) -> str:
stack, res, n = [], '', 0
for i in range(len(s)):
if s[i] == '[':
stack.append((res, n))
res, n = '', 0
elif s[i] == ']':
last_res, cur_cnt = stack.pop()
res = last_res + cur_cnt * res
elif '0' <= s[i] <= '9':
n = n * 10 + int(s[i])
else:
res += s[i]
return res
递归
遇到左括号进入递归,返回括号的res和最后idx,然后当前res加上计数乘以递归得到的res,这个multi已经用了再置0,遇到有括号返回。
class Solution:
def decodeString(self, s: str) -> str:
def dfs(i):
res, multi = "", 0
while i < len(s):
if s[i] == '[':
next_res, i = dfs(i+1)
res += multi * next_res
multi = 0
elif s[i] == ']':
return res, i
elif '0' <= s[i] <= '9':
multi = multi * 10 + int(s[i])
else:
res += s[i]
i += 1
return res
return dfs(0)
399.除法求值
给你一个变量对数组 equations 和一个实数值数组 values 作为已知条件,其中 equations[i] = [Ai, Bi] 和 values[i] 共同表示等式 Ai / Bi = values[i] 。每个 Ai 或 Bi 是一个表示单个变量的字符串。
另有一些以数组 queries 表示的问题,其中 queries[j] = [Cj, Dj] 表示第 j 个问题,请你根据已知条件找出 Cj / Dj = ? 的结果作为答案。
返回 所有问题的答案 。如果存在某个无法确定的答案,则用 -1.0 替代这个答案。如果问题中出现了给定的已知条件中没有出现的字符串,也需要用 -1.0 替代这个答案。
注意:输入总是有效的。你可以假设除法运算中不会出现除数为 0 的情况,且不存在任何矛盾的结果。
class Solution:
def calcEquation(self, equations: List[List[str]], values: List[float], queries: List[List[str]]) -> List[float]:
# 构建图,保存每个节点对应终点和权值
g = collections.defaultdict(dict)
for (i, j), v in zip(equations, values):
g[i][j] = v
g[j][i] = 1/v
# 深度搜索,找从i到j的路径,遍历i的终点,深度优先搜索
def dfs(i, j):
if i not in g: return -1
if i == j: return 1
visited.add(i)
for node in g[i]:
if node in visited: continue
if node == j: return g[i][node]
res = dfs(node, j)
if res != -1: return g[i][node]*res
return -1
res = [] # visited保存遍历节点,防止重复无穷递归
for i,j in queries:
visited = set()
res.append(dfs(i, j))
return res
406. 根据身高重建队列
假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。
请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。
思路: 排序后插入,身高降序,人数升序,插入时,在ret里都是大于等于当前值的,且人数升序,不会影响已经插入的
class Solution:
def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]:
people.sort(key=lambda x: (-x[0], x[1]))
ret=[]
for p in people:
if len(ret) <= p[1]:
ret.append(p)
else:
ret.insert(p[1], p)
return ret
416. 分割等和子集
给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
思路: 动态规划,以sum一半为target,dp[i][j]表示前i个数能不能取出和为j的数。
class Solution:
def canPartition(self, nums: List[int]) -> bool:
s = sum(nums)
if s & 1: return False
target = s // 2 + 1
dp = [[False] * target for _ in range(len(nums))]
for i in range(len(nums)): dp[i][0] = True
for i in range(len(nums)):
for j in range(1, target):
dp[i][j] = dp[i-1][j]
if j >= nums[i]: dp[i][j]|= dp[i-1][j-nums[i]]
return dp[-1][-1]
空间优化,将数组变成一维,因为每次遍历向前取值,所以倒序遍历,提前剪枝
class Solution:
def canPartition(self, nums: List[int]) -> bool:
s = sum(nums)
if s & 1: return False
target = s // 2 + 1
dp = [False] * target
dp[0] = True
for i in range(len(nums)):
for j in range(target-1, 0, -1):
if j >= nums[i]:
dp[j] |= dp[j-nums[i]]
if dp[-1] == True: return True
return dp[-1]
437. 路径总和 III
给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。
路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
深度搜索,dfs寻找当前root为起点的为target的所有数量,外层递归遍历所有节点,寻找这些节点开始的数量。
# 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 pathSum(self, root: TreeNode, targetSum: int) -> int:
if not root: return 0
def dfs(root, tar):
if not root: return 0
tar -= root.val
res = 0
if tar == 0: res = 1
return res + dfs(root.left, tar) + dfs(root.right, tar)
return dfs(root, targetSum) + self.pathSum(root.left, targetSum) + self.pathSum(root.right, targetSum)
前缀和,新建一个字典保存对应前缀和出现的次数,一次dfs,每次传入之前的前缀和,当前前缀和为之前的加上当前节点值,从字典中找以本层为终点的和数组,再把本层前缀和放入字典,加上左右子树的res,最后回退时要在字典中减去当前节点的前缀和
# 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 pathSum(self, root: TreeNode, targetSum: int) -> int:
if not root: return 0
d = collections.defaultdict(int)
d[0] = 1
def dfs(root, cursum) -> int:
if not root: return 0
cursum += root.val
res = d[cursum - targetSum]
d[cursum] += 1
res += dfs(root.left, cursum)
res += dfs(root.right, cursum)
d[cursum] -= 1
return res
return dfs(root, 0)
438. 找到字符串中所有字母异位词
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指字母相同,但排列不同的字符串。
思路: dic保存计数,正数为缺的,负数为多的,diff保存窗口和正确的差距,滑动窗口,每次踢掉的字母计数小于0,则diff减少(多了),反之,diff增加(踢了就缺了),维护字典,每次加入的字母计数小于0,则diff增加(多了不需要了还来),反之,diff减少(缺你你快来),每次滑动进行判定diff,diff==0加入结果list。
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
if len(p) > len(s): return []
d, ret, diff, k = {}, [], len(p), len(p)
for c in p:
if c not in d: d[c] = 1
else: d[c] += 1
for i in range(k):
if s[i] in d:
d[s[i]] -= 1
if d[s[i]] >= 0: diff -= 1
else: diff += 1
if diff == 0: ret.append(0)
for i in range(k, len(s)):
if s[i-k] == s[i]:
if diff == 0: ret.append(i - k + 1)
continue
if s[i-k] in d:
if d[s[i-k]] < 0: diff -= 1
else: diff += 1
d[s[i-k]] += 1
if s[i] in d:
if d[s[i]] > 0: diff -= 1
else: diff += 1
d[s[i]] -= 1
if diff == 0: ret.append(i - k + 1)
return ret
494.目标和
给你一个整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
思路:
转化成01背包问题,将数组分成两部分 x + y = sum(nums), x - y = target,所以就是找 x = (sum(nums) + target) // 2 的子数组,类似461分割等和子集相同处理,注意不同的是取和,所以必须遍历改变到dp[0]位置
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
sm = sum(nums) + target
if sm & 1: return 0
sm //= 2
dp = [1] + [0] * sm
for i in range(len(nums)):
for j in range(sm, -1, -1):
if j > nums[i] - 1:
dp[j] += dp[j - nums[i]]
return dp[-1]
581.最短无序连续子数组
给你一个整数数组 nums ,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。
请你找出符合题意的 最短 子数组,并输出它的长度。
思路: 数组分成abc三部分,a为升序,c为升序,b为中间乱序,若b中有小于a的数,a的长度应该缩到那个数前,同理c
所以单次遍历,从左到右找大数,先找右边c的边界,若遍历位置更大,满足升序更新max不记录,若当前位置比之前最大值小,说明至少是这个位置边界记录一下,同理,反向遍历找小数,最后指标停在闭区间边界,计算的到长度
class Solution:
def findUnsortedSubarray(self, nums: List[int]) -> int:
n = len(nums)
maxn, minn = float('-inf'), float('inf')
left = right = -1
for i in range(n):
if nums[i] >= maxn:
maxn = nums[i]
else:
right = i
if nums[n - i - 1] <= minn:
minn = nums[n - i - 1]
else:
left = n - i - 1
return 0 if left == -1 else right - left + 1
621. 任务调度器
给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表。其中每个字母表示一种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。在任何一个单位时间,CPU 可以完成一个任务,或者处于待命状态。
然而,两个 相同种类 的任务之间必须有长度为整数 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。
你需要计算完成所有任务所需要的 最短时间 。
桶子法,计算最多的数量,桶子深度为n+1,若填不满 则 res=(M-1)*(n+1) + x (x为并列最多的个数)如图一
若上面已经全部填满此时size > res,直接返回res就行
class Solution:
def leastInterval(self, tasks: List[str], n: int) -> int:
l = len(tasks)
wordlist = [[i,0] for i in range(26)]
for c in tasks:
wordlist[ord(c) - ord("A")][1] += 1
wordlist.sort(key=lambda x: -x[1])
m = wordlist[0][1]
cnt = 0
for w in wordlist:
if w[1] != m:
break
cnt += 1
res = (m-1)*(n+1)+cnt
return max(res, l)
647. 回文子串
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
动态规划:
类似 4
class Solution:
def countSubstrings(self, s: str) -> int:
n = len(s)
dp = [[0]*n for _ in range(n)]
sm = n
for i in range(n):
dp[i][i] = 1
for L in range(2, n + 1):
for i in range(n- L + 1):
j = i + L - 1
if s[i] == s[j]:
if L > 2:
dp[i][j] = dp[i+1][j-1]
else:
dp[i][j] = 1
if dp[i][j]:
sm += 1
return sm
中心扩展法
class Solution:
def countSubstrings(self, s: str) -> int:
n = len(s)
res = 0
for i in range(2*n - 1):
left = i // 2
right = left + i % 2
while 0 <= left and right < n and s[left] == s[right]:
res += 1
left -= 1
right += 1
return res
739.每日温度
请根据每日 气温 列表 temperatures ,请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。
单调栈
递减栈,若当前值大于栈顶值,说明找到比之前大的温度了,依次出栈记录差值。
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
stack = []
res = [0] * len(temperatures)
for i, tp in enumerate(temperatures):
while stack and temperatures[stack[-1]] < tp:
idx = stack.pop()
res[idx] = i - idx
stack.append(i)
return res