d1
leet1: 两数之和
https://leetcode-cn.com/problems/two-sum
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
【hashmap存储】
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
# 边界值判断
if not nums or target == None:
return []
has = {} # hash型map存值对应的"索引"
for i, num in enumerate(nums):
if target - num in has:
return [has[target - num], i]
has[num] = i
return []
leet4: 两个数组中的中位数
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5
【奇偶判断】
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
if not nums1 and not nums2:
return None
nums = []
nums.extend(nums1)
nums.extend(nums2)
nums.sort()
# 偶数个数情况,求均值;"//"获取整数索引
return nums[len(nums)//2] if len(nums) % 2 else (nums[len(nums)//2] + nums[len(nums)//2 - 1]) / 2
leet5: 最长回文子串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
【双循环贪心】
class Solution:
def longestPalindrome(self, s: str) -> str:
if len(s) <= 1:
return s
# 最大长度开始判别,符合条件的字符串即最大回文子串
for length in range(len(s), 0, -1): # len(s)次,倒序
for i in range(0, len(s) - length + 1):
newS = s[i : i + length]
if newS == newS[::-1]: # '反转'判别
return newS
d2
leet9: 回文数
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
输入: 121
输出: true
【折半比较】
class Solution:
def isPalindrome(self, x: int) -> bool:
if x == None:
return False
if len(str(x)) <= 1:
return True
# 折半搜索
for i in range(len(str(x)) // 2):
if str(x)[i] != str(x)[-i - 1]:
return False
return True
leet409 最长回文串
给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。
在构造过程中,请注意区分大小写。比如 "Aa" 不能当做一个回文字符串。
注意:
假设字符串的长度不会超过 1010。
示例:
输入:
"abccccdd"
输出:
7
【集合更新与判空】(add remove, 放入可回文的字符,无重复)
class Solution:
def longestPalindrome(self, s: str) -> int:
if len(s) < 2:
return len(s)
num = 0
set0 = set()
for i in s:
if i in set0: # 有两个字符则能组成回文
set0.remove(i)
num += 2
else:
set0.add(i) # 更新可回文元素
# 只要还有没有移除的set0元素,就更新元素个数+1
return num + 1 if len(set0)!=0 else num
leet10: 正则表达式匹配
给你一个字符串 s
和一个字符规律 p
,请你来实现一个支持 '.'
和 '*'
的正则表达式匹配。
'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 **整个 **字符串 s
的,而不是部分字符串。
说明:
-
s
可能为空,且只包含从a-z
的小写字母。 -
p
可能为空,且只包含从a-z
的小写字母,以及字符.
和*
。
示例 1:
输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
【正则包与结果项】
import re
class Solution:
def isMatch(self, s:str, p:str)->bool:
if re.match(p, s):
return re.match(p, s).group() == s
return False
d3
leet26: 删除排序数组的重复项
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
【新计数索引】
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
if not nums:
return 0
count = 0
# 修改数组,同时计数
for i in range(1, len(nums)): # 第0个一定不排除
if nums[i] != nums[count]:
count += 1
nums[count] = nums[i] # 仅需修改前若干个元素为关不重复元素即可,后面元素不改变
# 返回排序数组中不重复的元素个数
return count + 1
leet27: 移除元素
给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例:
给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
你不需要考虑数组中超出新长度后面的元素。
【while val存在】
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
if not nums:
return None
# while循环存有目标的情况,不断删除
while val in nums:
nums.remove(val)
return len(nums)
leet28: strStr()
实现 strStr() 函数:
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
示例:
输入: haystack = "hello", needle = "ll"
输出: 2
【str.index(p)】
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
# 判断needle的存在性,后检测初始出现位置
return haystack.index(needle) if needle in haystack else -1
d4
leet50: pow(x, n)
【分支递归乘数】
class Solution:
def myPow(self, x: float, n: int) -> float:
if n == 0:
return 1
elif n < 0:
return 1 / self.myPow(x, -n) # 调用自身
# 奇数情况
elif n % 2:
return x * self.myPow(x, n - 1)
# 减少为一般乘数
return self.myPow(x * x, n / 2)
leet303: 区域和检索-数组不变情况【线段树】
给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 i, j 两点。
给定 nums = [-2, 0, 3, -5, 2, -1],求和函数为 sumRange()
sumRange(0, 2) -> 1
sumRange(2, 5) -> -1
sumRange(0, 5) -> -3
【缓存和】
class NumArray:
# 缓存机制便于快速查找区间和,不超时
def __init__(self, nums: List[int]):
self.nums = [0] + nums # 前加入0便于防止0越界:self.nums[j] - self.nums[i - 1]
# 从1开始求和, 更新self.nums
for i in range(1, len(self.nums)): # 更新的数组长度不变
self.nums[i] = self.nums[i - 1] + self.nums[i]
def sumRange(self, i: int, j: int) -> int:
if not self.nums:
return 0
return self.nums[j + 1] - self.nums[i] # 索引向前+1
# Your NumArray object will be instantiated and called as such:
# obj = NumArray(nums)
# param_1 = obj.sumRange(i,j)
leet487:最大连续1的个数
给定一个二进制数组,你可以最多将 1 个 0 翻转为 1,找出其中最大连续 1 的个数。
输入:[1,0,1,1,0]
输出:4
解释:翻转第一个 0 可以得到最长的连续 1。
当翻转以后,最大连续 1 的个数为 4。
【最近0的出现位置】
class Solution:
def findMaxConsecutiveOnes(self, nums: List[int]) -> int:
if nums == None:
return 0
maxNum, num = 0,0
lastZero = -1
# 遍历更新0的最近出现位置,与更新后的1计数num,与更改后的1个数maxNum
for i in range(len(nums)):
if nums[i] == 1:
num += 1
else:
maxNum = max(maxNum, num)
num = i - lastZero # 更新最近出现的0的位置
lastZero = i
return max(maxNum, num)
d5
leet507: 完美数
对于一个 正整数,如果它和除了它自身以外的所有正因子之和相等,我们称它为“完美数”
输出: True
解释: 28 = 1 + 2 + 4 + 7 + 14
【折平根比较】
class Solution:
def checkPerfectNumber(self, num: int) -> bool:
if num == None or num == 1:
return False
i = 2
sum0 = 1 # 任何的数都有因数1
# 折平方根搜索
while i * i <= num:
if num % i == 0:
sum0 += num / i + i
i += 1
return sum0 == num
leet665: 非递减数列
修改一个数,则变成非递减数列,输出True
【嵌套条件分支】
class Solution:
def checkPossibility(self, nums: List[int]) -> bool:
if nums == None:
return False
count = 0
for i in range(len(nums)-1):
if nums[i] > nums[i + 1]: # 两种情况只能出现一遍
# 大数(i位置)变小(i+1位置)
if i == 0 or nums[i + 1] > nums[i - 1]: # 4(i=0时) 2 1 or 2 4(i) 3
nums[i] = nums[i + 1]
# 小数(i+1位置)变大(i位置)
else: # 2 2(i) 1 3
nums[i + 1] = nums[i]
count += 1
return count <= 1
leet400: 第N个数字
123456789101112...求出第N个数字对应的单字符数值
【while True中的数量级t和求解条件m】
class Solution:
def findNthDigit(self, n: int) -> int:
if n == None or n <= 0:
return None
# 9: t=1 m=9 9≤9:n=8 return '9'(1+8)[0]即0
# 19: t=1 m=9 n=10 i=2;t=10 m=180 n≤180 n=9 return '14'(10+9//2)[9%2]即4
i = 1
while True:
# 数量级递增
t = 10 ** (i - 1)
# 求解终止条件,9 180
m = t * 9 * i
# m作为求解出口t条件
if n <= m:
n -= 1
return int(str(t + n // i)[n % i])
n -= m
i += 1
d6
leet219: 存在重复元素ii
列表中存在最大长度为k的两个相等的数,则返True
给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的绝对值最大为 k。如输入: nums = [1,2,3,1], k = 3, 输出: true
【集合表示滑动窗口】
class Solution:
def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
if not nums or k == None:
return False
set0 = set() # set存储两个规定距离的相等值
for i in range(len(nums)):
if nums[i] in set0: # 唯一的判别条件
return True
set0.add(nums[i]) # 添加元素【add】
# 只允许k长度内的元素存在集合内,否则删除最先进入集合中的第i-k个元素。
# 如:1 2 3(i处) 2 k=2
# 集合中元素数超过k !
if len(set0) > k:
set0.remove(nums[i - k])
return False
leet面试题22: 链表中倒数第k个节点
返回链表中倒数第k个节点,如1->2->3->4->5, k = 2:
q先走2步,之后p/q一共走4步p指向4=>输出4->5
【双指针追踪】
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
if not head or k <= 0:
return None
p = head
q = head
for _ in range(k):
q = q.next
# p最后倒数的位置与k循环的次数(q之前走过的长度)相同
while q:
q = q.next
p = p.next
return p
leet1346: 整数及其两倍数是否存在
两个数不是同一个数,存在某数2倍数的一个数则返回True
【判双零】
class Solution:
def checkIfExist(self, arr: List[int]) -> bool:
if not arr:
return False
if 0 in arr:
arr.remove(0)
if 0 in arr: # 两个零存在才满足条件
return True
for i in range(len(arr)):
if 2 * arr[i] in arr:
return True
return False
d7
leet415: 字符串相加
两个数值型字符串,相加后返回结果字符串
【while中低位进位】
class Solution:
def addStrings(self, num1: str, num2: str) -> str:
if not num1:
return num2
elif not num2:
return num1
i, j = len(num1)-1, len(num2)-1
carry = 0 # 进位数
res = ''
# 只要还有没有相加完成的位, 9 + 99 = 108
while i >= 0 or j >= 0:
n1 = int(num1[i]) if i >= 0 else 0 # 没有位参加运算则赋0
n2 = int(num2[j]) if j >= 0 else 0
tmp = n1 + n2 + carry
carry = tmp // 10 # 高位
res = str(tmp % 10) + res # 低位
i -= 1
j -= 1
# 最终含有进位则添加结果项中的进位
return '1' + res if carry else res
# -----or 直接简化的int()转换--------
return str(int(num1) + int(num2))
leet1026: 节点与其祖先之间的最大差
求节点与其祖先之间的最大差,如:
输入:[8,3,10,1,6,null,14,null,null,4,7,13]
输出:7
解释:
我们有大量的节点与其祖先的差值,其中一些如下:
|8 - 3| = 5
|3 - 7| = 4
|8 - 1| = 7
|10 - 13| = 3
在所有可能的差值中,最大值 7 由 |8 - 1| = 7 得出
【左右递归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 maxAncestorDiff(self, root: TreeNode) -> int:
def dfs(node, ma = -999999, mi = 999999):
if not node:
return ma - mi
# 递归
l = dfs(node.left, max(ma, node.val), min(mi, node.val))#祖先与左孩子的最大差
r = dfs(node.right, max(ma, node.val), min(mi, node.val))
return max(l, r)
return dfs(root)
leet135:分发糖果
一排人领糖果, 相邻的人rating高的获得较多糖,每人都有糖果。
老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。
你需要按照以下要求,帮助老师给这些孩子分发糖果:
每个孩子至少分配到 1 个糖果。
相邻的孩子中,评分高的孩子必须获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?如输入: [1,0,2], 输出: 5
解释: 你可以分别给这三个孩子分发 2、1、2 颗糖果。
【左右规则】
class Solution:
def candy(self, ratings: List[int]) -> int:
if not ratings:
return 0
left = [1 for _ in range(len(ratings))] # 每人一颗糖
right = left[:] # 复制一份left列表,【非引用】
# 左右规则均从(倒数)第二个数开始与之前比较
# 左规则: 初始每个人都有糖,从左向右遍历1~len(ratings)-1索引的元素
for i in range(1, len(ratings)):
if ratings[i] > ratings[i - 1]:
left[i] = left[i - 1] + 1
# 右规则: 初始count为最右边的元素值,从右到左, 0~len(ratings)-2索引的元素
count = left[-1]
for i in range(len(ratings) - 2, -1, -1):
if ratings[i] > ratings[i + 1]:
right[i] = right[i + 1] + 1
# 取左右规则中最大的糖果数累加
count += max(left[i], right[i])
return count
d8
leet面试02.02: 倒数第k个节点值
返回链表中倒数第k个节点值
【双指针追踪】
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def kthToLast(self, head: ListNode, k: int) -> int:
if not head or k<=0:
return None
# 双指针测量路径长
p = head
q = head
for _ in range(k):
q = q.next
while q:
q = q.next
p = p.next
return p.val
leet面试04.04/ 55: 平衡二叉树
是否平衡二叉树,即任意一个节点其两颗子树的高度差不超过一。
【递归返回是否平衡和树高】
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
# 返回当前是否平衡树与高度差
def check(node):
if not node:
return True, 0 # 空树是平衡树,高度差为0
lres, lheight = check(node.left)
rres, rheight = check(node.right)
# 分别返回是否当前为平衡树、当前树高(在子树高度基础上加一)
return lres and rres and abs(lheight - rheight) <= 1, max(lheight, rheight) + 1
# 返回结果中第一个值
return check(root)[0]
leet2/leet面试02.05: 链表求和
【三重while中低位进位】
# 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:
res = ''
carry = 0
# 链表的首位对应数值的低位,即从链表收为开始计数、进位
while l1 and l2:
n1 = l1.val
n2 = l2.val
tmp = n1 + n2 + carry
carry = tmp // 10
res = str(tmp % 10) + res
l1 = l1.next
l2 = l2.next
while l1:
n1 = l1.val
tmp = n1 + carry
carry = tmp // 10
res = str(tmp % 10) + res
l1 = l1.next
while l2:
n2 = l2.val
tmp = n2 + carry
carry = tmp // 10
res = str(tmp % 10) + res
l2 = l2.next
res = '1' + res if carry else res
# 反向输出
head = ListNode(0)
p = head
for i in range(len(res)-1, -1, -1):
p.next = ListNode(res[i])
p = p.next
return head.next
d9
leet面试18: 删除链表中节点
删除链表中的节点
【制造伪头结点】
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def deleteNode(self, head: ListNode, val: int) -> ListNode:
if not head:
return None
# 情况一:删除头结点
if head.val == val:
return head.next
# 情况二:非头结点
dummy = head # 制造伪头结点
while head and head.next:
if head.next.val == val: # 与head.next.val比较
head.next = head.next.next
head = head.next # 逐次向后
return dummy
# --------------------or 类似【leet203的移除链表中所有元素,递归】------------
if not head:
return None
head.next = self.deleteNode(head.next, val)
return head.next if head.val == val else head
leet面试32: 从上到下之字形打印二叉树
之字形层次遍历二叉树,如:
3
/ \
9 20
/ \
15 7
返回其层次遍历结果:
[
[3],
[20,9],
[15,7]
]
【递归含层数的层次遍历与折返判断】
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
# 层次遍历
def output(node, level):
if not root:
return
list0 = [[]] # 二维数组
# 奇偶行赋值
if level % 2:
list0[level - 1].append(node.val) # 奇数行索引从0开始
else:
list0[level - 1].insert(0, node.val) # 偶数行索引为奇数,头插法放入列表中
# 本行初始为空列表
if len(list0) == level:
list0.append([])
output(node.left, level + 1)
output(node.right, level + 1)
output(root, 1)
return list0[:-1] # 列表元素集中除去最后一个
leet724: 寻找数组的中心索引
如果一个数的左边数组和与右边数组和相等,则返回此中间数的索引。
【从左累加与总和比较】
class Solution:
def pivotIndex(self, nums: List[int]) -> int:
if not nums:
return -1
lsum, sum0 = 0, 0
for i in range(len(nums)):
sum0 += nums[i]
# 左规则: 左边累计和加上下一个元素的和等于总和,则下一个索引是所求
for i in range(len(nums)):
if sum0 == lsum * 2 + nums[i]:
return i
lsum += nums[i]
return -1
d10
leet面试27: 二叉树的镜像
输出二叉树的镜像, 如:
输入:
4
/ \
2 7
/ \ / \
1 3 6 9
镜像输出:
4
/ \
7 2
/ \ / \
9 6 3 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 mirrorTree(self, root: TreeNode) -> TreeNode:
if not root:
return
# 递归自身
node0 = root.left # 创建临时节点,防止左节点被覆盖
root.left = self.mirrorTree(root.right)
root.right = self.mirrorTree(node0) # 防止左节点被覆盖
return root
leet107: 层次遍历二叉树
自底向上遍历二叉树,如:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回其自底向上的层次遍历为:
[
[15,7],
[9,20],
[3]
]
【递归层次遍历】
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def levelOrderBottom(self, root: TreeNode) -> List[List[int]]:
list0 = [[]] # 新建二维数组
def output(node, level):
if not node:
return
# 赋值节点进入二维数组
list0[level - 1].append(node.val)
# 当前层已有赋值,则新增下一层的节点赋值空间
if len(list0) == level:
list0.append([])
output(node.left, level + 1)
output(node.right, level + 1)
output(root, 1)
# list0[:-1]后反转列表
return list0[:-1][::-1]
leet662: 二叉树的最大宽度
给定一个二叉树,编写一个函数来获取这个树的最大宽度。树的宽度是所有层中的最大宽度。
这个二叉树与满二叉树(full binary tree)结构相同,但一些节点为空。
每一层的宽度被定义为两个端点(该层最左和最右的非空节点,两端点间的null节点也计入长度)之间的长度。如输入:
1
/ \
3 2
/ \ \
5 3 9
输出: 4
解释: 最大值出现在树的第 3 层,宽度为 4 (5,3,null,9)。
【含hashmap[depth]的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 widthOfBinaryTree(self, root: TreeNode) -> int:
hashmap = {} # hashmap初始, 外部变量记录每层的宽度
def dfs(node, width, depth):
if not node:
# 没有节点则宽度返回0
return 0
if depth not in hashmap:
hashmap[depth] = width # 深度depth对应的宽度width
# 递归
l = dfs(node.left, 2 * width, depth +1)
# 即使没有左节点,右分支还要加null的一个节点数
r = dfs(node.right, 2 * width + 1, depth + 1)
return max(l, r, width - hashmap[depth] + 1)
return dfs(root, 0, 0) # 初始深度与宽度从1开始
d11
leet29: 两数相除
返回除法结果,不越界[-2**31, 2**31-1]
【正负/零划分】
class Solution:
def divide(self, dividend: int, divisor: int) -> int:
if divisor == 0:
return None
# 被除数为0
if dividend == 0:
return 0
# 同正同负
elif (dividend < 0 and divisor < 0) or (dividend > 0 and divisor > 0):
# 同为负数时,最小负数//(-1)结果超越最大正数2**31-1
return (dividend // divisor) if (dividend // divisor) <= 2 ** 31 - 1 else 2 ** 31 - 1
# 一正一负
else:
return -(abs(dividend) // abs(divisor))
leet191: 位1的个数
一个数的二进制表示中1的个数
【带奇偶判断的逐右移位】
class Solution:
def hammingWeight(self, n: int) -> int:
if not n:
return 0
# 位依次右移通过n的奇偶性计数,奇数则加一
res = 0
while n:
res += (n % 2)
n >>= 1 # 右移1 or 【n //= 2】
return res
# --------or------
# n = n&(n-1)位运算依次加一
res = 0
while n:
n &= n - 1
res += 1
return res
leet203: 移除链表元素
移除所有值为val的节点,返回链表。如输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
【含有目标比较的自身递归】
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def removeElements(self, head: ListNode, val: int) -> ListNode:
if not head or val == None:
return None
# 自身递归, 下一个节点指代的链表移除目标后的指向,是head.next
head.next = self.removeElements(head.next, val)
# head是目标则返回head.next, 否则返回当前head
return head.next if head.val == val else head
# --------or 不简洁方法--------------
# 存入列表,后逐次移除,再建立链表
if not head or val == None:
return None
list0 = []
while head:
list0.append(head.val)
head = head.next
while val in list0:
list0.remove(val)
dummy = ListNode(0)
p = dummy
for i in list0:
p.next = ListNode(i)
p = p.next
return dummy.next
d12
leet69: x的平方根
实现int sqrt(int x)函数
【牛顿迭代】
class Solution:
def mySqrt(self, x: int) -> int:
# 牛顿迭代法
if x <= 1:
return x # 条件中非负整数x
res = x
while res > x / res:
res = (res + x / res) // 2
return int(res)
# -----------or 直接的库函数------------
return int(math.sqrt(x))
leet617: 合并二叉树
合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。如输入:
Tree 1 Tree 2
1 2
/ \ / \
3 2 1 3
/ \ \
5 4 7
输出:
合并后的树:
3
/ \
4 5
/ \ \
5 4 7
【两次判空与全存在的各自相加】
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def mergeTrees(self, t1: TreeNode, t2: TreeNode) -> TreeNode:
# 返回其中仅一个的有值节点
if not t1: return t2
if not t2: return t1
# 在两者均存在情况下同一位置节点值相加
t1.val += t2.val
# 递归
t1.left = self.mergeTrees(t1.left, t2.left) # 合并左节点为新的t1节点
t1.right = self.mergeTrees(t1.right, t2.right) # 合并右节点为新的t1节点
# 返回新的节点
return t1
leet113: 路径总和
求出根到叶子节点路径总和为固定值的所有路径集
给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。
说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
返回:
[
[5,4,11,2],
[5,8,4,5]
]
【目标递减下叶子和当前更新的路径和与目标值的比较】
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
res = []
def dfs(node, tmp, sum0):
if not node:
return
# 叶节点且是等于最后减下来的差值
if not node.left and not node.right and node.val == sum0:
tmp.append(node.val) # 放入当前路径中的叶子节点
res.append(tmp) # 存储当前完整的路径
# 左右子树依次遍历,目标长度值逐次减去当前值
dfs(node.left, tmp + [node.val], sum0 - node.val) # 节点逐次放入候选路径集
dfs(node.right, tmp + [node.val], sum0 - node.val)
dfs(root, [], sum) # []: 当前符合条件的一条路径中的节点
return res
d13
leet1207: 独一无二的出现次数
数组中是否有独一无二的出现次数
【hashmap计数与值数组判重】
class Solution:
def uniqueOccurrences(self, arr: List[int]) -> bool:
if not arr:
return True
count = {} # hashmap存储每个值出现的个数
# 初始化每个数的长度count,逐次加一
for i in arr: count[i] = 0
for i in arr: count[i] += 1
# hashmap中的值如果有重复则返回False, 若重则set后长度减少
return len(set(count.values())) == len(count.values())
leet300: 最长上升子序列
给定一个无序的整数数组,找到其中最长上升子序列的长度。如输入: [10,9,2,5,3,7,101,18]
输出: 4 ,解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
【双循环动态规划】
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
if not nums:
return 0
# 动态规划,初始为长度1的【len(nums)】长序列
dp = [1] * len(nums)
for i in range(len(nums)): # (1, len(nums)):
for j in range(i):
# i与j相比较,逐次:【仅当】组成长为2的上升序列才更新dp[i]
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j] + 1) # 取当前原始长度值与更新后的长度值中较大者
# 返回【最长者】
return max(dp)
leet669: 修剪二叉搜索树
【递归判断】
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def trimBST(self, root: TreeNode, L: int, R: int) -> TreeNode:
if not root:
return None
# 当前节点值>R, 则修剪后的二叉数在当前节点左侧, 即"返回"修剪左侧后的二叉树; 返回修剪右侧后的二叉树同理。
if root.val > R: return self.trimBST(root.left, L, R)
if root.val < L: return self.trimBST(root.right, L, R)
# 否则(root.val处于L,R中间), 分别修剪二叉树左右分支
root.left = self.trimBST(root.left, L, R)
root.right = self.trimBST(root.right, L, R)
# 最终返回修改完成的二叉树
return root
d14
leet121: 买卖股票的最佳时机
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。
注意你不能在买入股票前卖出股票。如输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
【最低股价与最大利润同时更新】
class Solution:
def maxProfit(self, prices: List[int]) -> int:
if not prices:
return 0 # 没有则获利0
min0 = 999999
maxp = -999999
# 逐次更新
for i in prices:
min0 = min(min0, i) # 最低股价
maxp = max(maxp, i - min0) # 最大利润
return maxp
leet面试42: 连续子数组的最大和
输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。如输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6, 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
【max0 sum0初始索引0赋值,与sum0>0判断】
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
if not nums:
return 0
max0 = nums[0]
sum0 = nums[0]
# 从第二个数开始判断累加
for i in range(1, len(nums)):
# 上次累计和小于0则sum0置当前值
if sum0 < 0:
sum0 = nums[i]
else:
sum0 += nums[i] # 否则累加
max0 = max(max0, sum0) # 逐次更新连续子数组的最大和
return max0
leet279: 完全平方数
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。如输入: n = 12
输出: 3 ,解释: 12 = 4 + 4 + 4
【动态规划双循环解背包问题】
class Solution:
def numSquares(self, n: int) -> int:
if n == None:
return 0
dp = [0] * (n + 1)
# 从1开始循环n遍
for i in range(1, n + 1):
dp[i] = i # 最坏情况每次加1
for j in range(1, int(math.sqrt(i)) + 1):
# 最少完全平方数的个数
dp[i] = min(dp[i], dp[i - j * j] + 1) # 动态转移方程
return dp[-1]
d15
leet204: 计数质数
统计所有小于非负整数 n 的质数的数量。如输入: 10
输出: 4,解释: 小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。
【筛去法】
class Solution:
def countPrimes(self, n: int) -> int:
if n == None or n <= 2:
return 0
count = 0
isPrime = [1] * n # 假设初始全部是素数
for i in range(2, n):
if isPrime[i]:
count += 1
# 筛去i的倍数
j = 2 * i # 从2倍数开始
# 在是素数的数的位置上置其为非素数
while j < n:
isPrime[j] = 0 # I的倍数的值置非素数
j += i
return count
leet264: 丑数ii
找出第 n 个丑数。
丑数就是只包含质因数 2, 3, 5 的正整数。如输入: n = 10
输出: 12, 解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
【三指针动态规划】
class Solution:
def nthUglyNumber(self, n: int) -> int:
if n == None:
return None
# 第0个丑数为1, 求出前n个丑数
# 0r: dp = [1 for _ in range(n)]
dp = [1] * n
i2 = 0
i3 = 0
i5 = 0
# 三指针在取最小的丑数为对应索引上的数,逐次动态更新, 遍历n-1次即可
for i in range(1, n):
dp[i] = min(2 * dp[i2], 3 * dp[i3], 5 * dp[i5])
if 2 * dp[i2] == dp[i]:
i2 += 1
if 3 * dp[i3] == dp[i]:
i3 += 1
if 5 * dp[i5] == dp[i]:
i5 += 1
# 最后一个数字为第n个丑数
return dp[-1]
leet714: 买卖股票的最佳时机含手续费
给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。
你可以无限次地完成交易,但是你每次交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。
返回获得利润的最大值。如输入: prices = [1, 3, 2, 8, 4, 9], fee = 2
输出: 8,解释: 能够达到的最大利润:
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8.
【买入卖出的动态规划】
class Solution:
def maxProfit(self, prices: List[int], fee: int) -> int:
if not prices or fee < 0:
return None
cash = 0 # 不持股票时的最大利
hold = -prices[0] # 持有股票时的最大利
# 比较两种操作的收益现金cash/hold
for i in range(1, len(prices)):
# 同一天卖出再买入(亏手续费)不比不进行操作好
cash = max(cash, hold + prices[i] - fee) # 卖出时出手续费, cash在持有股票的利润的基础上卖出
hold = max(hold, cash - prices[i]) # 买入
return cash
d16
leet122: 多次买卖股票的最大利
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
如输入: [7,1,5,3,6,4]
输出: 7
解释:
在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出,
这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,
这笔交易所能获得利润 = 6-3 = 3 。
【同leet714 多次买卖的动态规划】
class Solution:
def maxProfit(self, prices: List[int]) -> int:
if not prices:
return 0
cash = 0
hold = -prices[0] # 初始买入的第一支股票
for i in range(1, len(prices)):
cash = max(cash, hold + prices[i]) # 卖出
hold = max(hold, cash - prices[i]) # 买入
return cash
leet100: 相同的树
是否是相同的树
【同空、不同空判断后的自身递归】
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
# 同时存在则是True
if not p and not q: return True
# 有一个不存在则False
if not q or not q: return False
if p.val != q.val:
return False
return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)
leet437: 路径总和iii
不特定根节点和叶子节点,返回路径总和为固定值的路径数
【自身递归与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 pathSum(self, root: TreeNode, sum: int) -> int:
def dfs(node, sum0):
if not node:
return 0 # 无解
count = 0
if sum0 == node.val: count = 1
# 当前节点是一个解, 递归求解左右两边,均从给定数值中减去当前节点值
return count + dfs(node.left, sum0 - node.val) + dfs(node.right, sum0 - node.val)
if not root:
return 0
# 从自身左右边、根节点开始,寻找到符合条件的路径则计数,最终返回总数
return self.pathSum(root.left, sum) + self.pathSum(root.right, sum) + dfs(root, sum)
d17
lee315线段树
找出右边的值小于当前值的数的个数:
使用线段树构建左右子树,并从右到左计算前面的数小于当前值的数的个数,最终返回依次进入结果数组的list的倒置值。
class Node:
def __init__(self, begin, end):
self.begin = begin
self.end = end
self.mid = (begin + end) // 2
self.left = None
self.right = None
self.count = 0
# 要是当前值比最小值都小就返回0,否则比较最小值大则说明有一个比较当前值小的值了。
# 线段树递归自身的过程即是计算前方比当前值小的数的个数
def add(self, num):
self.count += 1
if self.begin == self.end:
return 0
self.left = Node(self.begin, self.mid) if not self.left else self.left
self.right = Node(self.mid + 1, self.end) if not self.right else self.right
# 分情况提交左右子树结果
if num <= self.mid:
return self.left.add(num)
else:
return self.left.count + self.right.add(num) # 左子树结果+右递归结果
class Solution:
def countSmaller(self, nums: List[int]) -> List[int]:
if not nums:
return []
# 使用自定义类初始化根节点
root = Node(min(nums), max(nums))
res = []
# 依次返回倒叙数组中前面的数比较当前数小的值的个数
for i in range(len(nums)-1, -1, -1):
res.append(root.add(nums[i]))
# 返回倒过来的正确数组
return res[::-1]
leet1013: 划分为和的三个部分:
【list复制法】
将遍历到的前两个和数都去掉,双重遍历T(n)= O(n)
class Solution:
def canThreePartsEqualSum(self, A: List[int]) -> bool:
if not A:
return False
for i in range(len(A)-2):
list0 = A[:]
sum0 = sum(A[:i+1])
for ii in range(0, i+1):
list0.remove(A[ii])
for k in range(i+1, len(A)-1):
list1 = list0[:]
sum1 = sum(A[i+1 : k+1])
for kk in range(i+1, k+1):
list1.remove(A[kk])
if sum1 == sum0 == sum(list1):
return True
return False
leet71:简化路径
规范的路径表示写法转换为最简路径
【栈进栈出,判断'' '.' '..'()】
class Solution:
def simplifyPath(self, path: str) -> str:
st = []
for i in path.split('/'):
if i not in ['','.','..']:
st.append(i)
elif i == '..' and st:
st.pop()
return '/' + '/'.join(st)
d18
leet792 匹配子序列的单词数
【复制字符库,逐次后移匹配库起始点】
class Solution:
def numMatchingSubseq(self, S: str, words: List[str]) -> int:
if not S or not words:
return 0
resnum = 0
for i in words:
list0 = S[:] # 每次都要还原字符库
point = 0# 匹配成功数
# 遍历每个元素中的每个字母
for ii in i:
if ii in list0:
list0 = list0[list0.index(ii)+1 : ] # 在字符库中则从命中索引往后(子"序列"),更新作剩余字符库
point += 1
if point == len(i):
resnum += 1
return resnum
leet410 分割数组的最大值
按要求分一个数组为m份,求其中子数组的最大值中最小一个
【二分查找、设定子数组最大的和mid作划分节点】
class Solution:
def splitArray(self, nums: List[int], m: int) -> int:
if not nums or not m:
return None
# 最小的最大值一定在l,r中徘徊,在终止条件l mid:
subsum = i
count += 1
if count > m: # 划分子区间太多了,要提高mid
l = mid + 1
else:
r = mid
return r
leet108 有序数组转换为平衡二叉搜索树
【二分思想,拆分数组递归】(不用判断平衡)
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
if not nums:
return None
# 二分处为root,构建平衡搜索树; 其他递归求左右
root = TreeNode(nums[len(nums) // 2])
lnums = nums[ : len(nums) // 2]
rnums = nums[len(nums) // 2 + 1 : ]
root.left = self.sortedArrayToBST(lnums)
root.right = self.sortedArrayToBST(rnums)
return root
d19(52%)
leet 1208 尽可能使字符串相等
两个字符串对应位置移动总次数在指定步数内(ascii值的差),求字符串的最大可变换长
【滑窗记录更新最初位置、不断更新当前滑窗内移动步数最小值】
class Solution:
def equalSubstring(self, s: str, t: str, maxCost: int) -> int:
if not s or not t:
return 0
# 经典滑动窗口,每一次一动一个格子
l = 0 # 记录好滑窗起始判断的位置
cost = 0
maxlen = 0
for i in range(len(s)):
cost += abs(ord(s[i]) - ord(t[i]))
while cost > maxCost: # 该窗口不可用,代价减掉旧的初始点的代价
cost -= abs(ord(s[l]) - ord(t[l]))
l += 1 # 窗口起始位置递增
maxlen = max(maxlen, i - l + 1) # 更新最大长,为当前走到的滑窗内的最大长
return maxlen
leet374:猜数字大小
原本的数字比猜出的数字小则返回1,求最后的正确数字
【二分法压缩区间】
# The guess API is already defined for you.
# @param num, your guess
# @return -1 if my number is lower, 1 if my number is higher, otherwise return 0
# def guess(num: int) -> int:
class Solution:
def guessNumber(self, n: int) -> int:
if not n:
return None
l,r = 1, n
# 二分思想收缩区间
while l < r:
m = (l + r) // 2
if guess(m) == 1:
l = m + 1
elif guess(m) == -1:
r = m
else:
return m
return l # 总有对的时候
leet949 给定数字能组成的最大时间
如:1 2 3 4组成的最大数字为'23:41'
【判断分钟数、三重遍历后求index4】
class Solution:
def largestTimeFromDigits(self, A: List[int]) -> str:
if not A or min(A) >2 or len(A) != 4:
return ''
hour = 0
minu = 0
max0 = -1
for i in range(4):
for j in range(4):
if j != i:
for k in range(4):
if k != j and k != i:
l = 6 - i - j - k # 计算没有用到的数索引index4
hour = A[i] * 10 + A[j]
minu = A[k] * 10 + A[l]
if hour < 24 and minu < 60:
max0 = max(max0, hour * 60 + minu) # 最大分钟数更新
# 返回整数串;没有满足条件的结果返回'';不足的格式用0填充
return '%02d:%02d'% (max0//60,max0%60) if max0 >= 0 else ''
d20(51%)
leet997 找到小镇的法官
法官不trust人任何人,任何人(除法官外)都trust法官
【图遍历每个人,出度为0入度N-者】
class Solution:
def findJudge(self, N: int, trust: List[List[int]]) -> int:
if not N: # 至少一个人
return -1
if not trust:
return N if N == 1 else -1
indegree = [0] * (N+1) # 因为人的编号从1开始
outdegree = [0] * (N+1)
for i in trust:
outdegree[i[0]] += 1
indegree[i[1]] += 1
for i in range(1, N+1): # 第0个值是空
# 其他N-1个人都相信他,自己不相信任何人(出度为0入度
if outdegree[i] == 0 and indegree[i] == N - 1:
return i
return -1
leet225 队列进出
模拟queue;peak返回值不弹出
【list只pop(0)作移除元素或pop()】
class MyQueue:
def __init__(self):
"""
Initialize your data structure here.
"""
self.queue = []
def push(self, x: int) -> None:
"""
Push element x to the back of queue.
"""
self.queue.append(x)
def pop(self) -> int:
"""
Removes the element from in front of queue and returns that element.
"""
if not self.empty():
val = self.queue.pop(0)
return val
def peek(self) -> int:
"""
Get the front element.
"""
if not self.empty():
return self.queue[0] # 与pop()均返回第一个元素即可
def empty(self) -> bool:
"""
Returns whether the queue is empty.
"""
return not self.queue
# Your MyQueue object will be instantiated and called as such:
# obj = MyQueue()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.peek()
# param_4 = obj.empty()
leet423 从英文中重建数字
【s.count计数、独特性】
class Solution:
def originalDigits(self, s: str) -> str:
if not s:
return ''
# list0 =['zero', 'one','two','three','four','five','six','seven','eight','nine']
# 默认是被打乱的英文字母,所以只要知道特有字母对应的数字格式即可,如'w'只在2中出现
n0 = s.count('z')
n2 = s.count('w')
n6 = s.count('x')
n8 = s.count('g')
# 依次可以逐步确定对应的数字个数,不能使用多次出现的字符如nine one的n
n3 = s.count('t') - n2 - n8
n7 = s.count('s') - n6
n5 = s.count('v') - n7
n4 = s.count('f') - n5
n9 = s.count('i') - n5 - n6 - n8
n1 = s.count('o') - n0 - n2 - n4
list0 = [n0, n1, n2, n3, n4, n5, n6, n7, n8, n9]
return ''.join( [str(i) * n for i,n in enumerate(list0)] ) # 从小到大
d21(100%)
leet7 整数反转
如-321转换为-123
【两种情况判断、str逆转】
class Solution:
def reverse(self, x: int) -> int:
if x == None:
return None
newx = int(str(x)[::-1]) if x >=0 else int('-' + str(x)[1:][::-1])
return 0 if newx > 2**31 - 1 or newx < -2**31 else newx
leet165 比较版本号
如输入: version1 = "7.5.2.4", version2 = "7.5.3"
输出: -1,即v1
class Solution:
def compareVersion(self, version1: str, version2: str) -> int:
if not version1 or not version2:
return None
while version1 or version2:
# 两个都没有点
if '.' not in version1 and '.' not in version2:
if int(version1) > int(version2):
return 1
elif int(version1) < int(version2):
return -1
else:
return 0
# return ('222'.split('.')[1:]) # true:[] 如果有一个不存在.也没有关系
if int(version1.split('.')[0]) > int(version2.split('.')[0]):
return 1
elif int(version1.split('.')[0]) < int(version2.split('.')[0]):
return -1
else:
# 比较下层
version1 = '.'.join(str(int(i)) for i in version1.split('.')[1:] ) if '.' in version1 else '0' # 消除01这样的版本表示为1
version2 = '.'.join(str(int(i)) for i in version2.split('.')[1:] ) if '.' in version2 else '0'
return None
leet515 找出二叉树每行中的最大值
输入:
1
/
3 2
/ \ \
5 3 9
输出: [1, 3, 9]
【有层次号标识的层次遍历】
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def largestValues(self, root: TreeNode) -> List[int]:
if not root:
return []
list0 = []
l = 0
def l_visit(node, l):
if node:
if len(list0)
d22 (67%)
leet141 环形链表
链表有环则返回True
【重置已遍历的值】
# 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:
return False
while head: # 只要之前遍历过的就是有环
if head.val =='findme':
return True
head.val = 'findme'
head = head.next
return False
leet142 环形链表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:
return None
while head: # 遍历,如果有环则返回当前节点
if head.val == 'visited':
return head
head.val = 'visited'
head = head.next
return None
leet491 递增子序列
找到数组的所有递增子序列,len>2、不用连续
【保存当前起始位置、逐次选后面满足条件元素的位置加入,递归生成有效集】
class Solution:
def findSubsequences(self, nums: List[int]) -> List[List[int]]:
if not nums:
return []
set0 = set()
def gen(curloc, tmp):
# 元组长大于2才能加入结果集合
if len(tmp) > 1 and tmp not in set0:
set0.add(tmp)
# 在当前值集合tmp的条件下,依次加入后面大于该tmp最大值的元素
for i in range(curloc + 1, len(nums)):
if tmp[-1] <= nums[i]:
gen(i, tmp + (nums[i],) ) # 更新tmp后满足"不递减"条件的起点位置
# 遍历列表的每个位置,生成当前位置为起点满足条件的子集
for i, e in enumerate(nums):
gen(i, (e, )) # i放入元组中
return [list(i) for i in set0]
d23 (60%)
leet102 二叉树层序遍历
层序遍历输出各个层次的节点值,分别用list表示
【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 levelOrder(self, root: TreeNode) -> List[List[int]]:
if not root:
return []
list0 = []
def bfs(node, l):
if node:
if len(list0) < l+1:
list0.append([])
list0[l].append(node.val)
bfs(node.left, l+1)
bfs(node.right, l+1)
bfs(root, 0)
return list0
leet662 二叉树最大宽度
【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 widthOfBinaryTree(self, root: TreeNode) -> int:
if not root:
return 0
# bfs非递归
queue = [(root, 0, 0)] # 元列表, 分别表示当前节点、depth、左右节点宽度位置loc(分别两倍、两倍+1个宽度)
curdepth = 0 # 当前层次
start, loc = 0, 0
max0 = 0 # 最大宽度
# 逐渐变多的元组集(左、右)
for node, depth, loc in queue:
if node:
queue.append([node.left, depth+1, 2*loc]) # 逐增层次的同时,将左右节点原先宽度位置更新:2*x和2x+1
queue.append([node.right, depth+1, 2*loc+1])
# 逐增当前的深度值,并更新当前深度、起始位置
if curdepth != depth:
curdepth = depth
start = loc
max0 = max(max0, loc - start + 1)
return max0
【dfs递归:默认每层起始位置、更新最大宽、hash0.setdefault()】
class Solution:
def widthOfBinaryTree(self, root: TreeNode) -> int:
if not root:
return 0
self.max0 = 0 # 最大宽
start = {}
def dfs(node, depth=0, loc=0):
if node:
start.setdefault(depth, loc) # 就是当前层depth的value不会再变了
# 更新现在的最大宽
self.max0 = max(self.max0, loc - start[depth] + 1)
# dfs,每增加一层depth,左边的就要翻倍,右边的就要翻倍加一;
# 即只要右边有节点,就是"2*loc+1 - 起始位置 + 1"的宽度,如例中的9:其宽度为2*(2*0+1)+1 - 0 + 1 = 4
# 如果是例2的对称例,start则与右子树的loc相等
dfs(node.left, depth+1, 2*loc)
dfs(node.right, depth+1, 2*loc+1)
dfs(root)
return self.max0 # 闭包外用到,故self方式
leet453 最小移动次数使数组元素相等
输入:
[1,2,3]
输出:
3, 解释: 只需要3次移动(注意每次移动会增加两个元素的值):
[1,2,3] => [2,3,3] => [3,4,3] => [4,4,4]
【数学题,移动后总和sum'(nums)与最小值的变化幅度m】
class Solution:
def minMoves(self, nums: List[int]) -> int:
# m次移动后,sum(nums)+m(n-1) = len(nums)*mean,且min(nums) + m = mean即原本"最小值"m次加一最终变mean!
# sum(nums) + m * (len(nums)-1) = len(nums)* (min(nums)+m)
# 以上求m : m = sunsum(nums) - len(nums) * min(nums)
if not nums:
return 0
return sum(nums) - len(nums) * min(nums)
d24
leet22 括号生成
【初始'()'后循环n-1次、对所有上一次结果遍历更新、找打所有空位进行set0.update】
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
if not n:
return []
res = set(['()'])
for _ in range(n-1): # n个括号
tmp = set()
for r in res: # 对之前结果所有都更新
# 每个结果string有len(string)个空位;update支持迭代
tmp.update( set(r[:j]+'()'+r[j:] for j in range(len(r))) ) #
res = tmp
return list(res)
leet13 罗马数字转整数
输入: "LVIII"
输出: 58, 解释: L = 50, V= 5, III = 3.
【hash、小值字符在大值之前】
class Solution:
def romanToInt(self, s: str) -> int:
if not s:
return 0
has = {'M':1000, 'D':500, 'C':100, 'L':50, 'X':10, 'V':5, 'I':1 }
res = 0
for i in range(len(s)):
# 当前字符是最后一个,当前字符对应值<后一个
if i < len(s) - 1 and has[s[i]] < has[s[i+1]]: # 减法
res -= has[s[i]]
else:
res += has[s[i]] # 加法
return res
leet227 基本运算器2
实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式仅包含非负整数,+, - ,*,/ 四种运算符和空格 。 整数除法仅保留整数部分。
输入: " 3+5 / 2 "
输出: 5
【stack、has_ops[op]对上个运算符和之后数字运算、转为stack和的运算】
import operator
class Solution:
def calculate(self, s: str) -> int:
if not s:
return None
stack = []
has_ops = {'+':lambda x: stack.append(x),
'-':lambda x: stack.append(-x),
'*':lambda x: stack.append(stack.pop()*x),
'/':lambda x: stack.append( int( operator.truediv(stack.pop(), x) ) ) } # 仅保留整数即可
op = '+'
num = 0
for i in s+'+':# 为了对于最后的操作符和数字进行计算
if i.isdigit():
num = num*10 + int(i) # 整个数字
elif i !=' ': # 非空字符' '
# 上上次的操作符对应上次数字,"*"、"/"时与上上上次数字运算后放入加运算栈,加减法时独自放入
has_ops[op](num)
op = i
num = 0
return sum(stack)
d25
leet373 查找和最小的k对数字
输入: nums1 = [1,1,2], nums2 = [1,2,3], k = 2
输出: [1,1],[1,1],解释: 返回序列中的前 2 对数:
[1,1],[1,1],[1,2],[2,1],[1,2],[2,2],[1,3],[1,3],[2,3]
【sorted()中用带有lambda的key】
class Solution:
def kSmallestPairs(self, nums1: List[int], nums2: List[int], k: int) -> List[List[int]]:
if not nums1 or not nums2 or not k:
return []
list0 = []
for i in nums1:
for j in nums2:
list0.append([i,j])
# 对数字对按和升序
list0 = sorted(list0, key = lambda x: x[0]+ x[1] ) # 和为升序条件
return list0[:k] # 返前k个
leet945 使数组唯一的最小增量
输入:[3,2,1,2,1,7]
输出:6,解释:经过 6 次 move 操作,数组将变为 [3, 4, 1, 2, 5, 7],可看出 5 次或 5 次以下的 move 操作是不能让数组的每个值唯一的。
【记录累积的步数、当前可用值】
class Solution:
def minIncrementForUnique(self, A: List[int]) -> int:
if not A:
return 0
A.sort()
moves = 0
cur_avalible = 0
# 升序后逐次遍历,记录步数、可用值
for i in A:
moves += max(cur_avalible - i, 0) # 步数一定大于零,第一个数moves=0
cur_avalible = max(cur_avalible,i) + 1 # 当前可用值=当前值与当前可用值的较大值+1
return moves # sum(set0) - sum(A) # moves
# 如 1 1 2 2 3 7
# moves: 0 1 1+1 2+2 4+2 6+max(6-7,0)=6
# cur_avalible: 2 3 4 5 6
leet2 两数相加
【当前进位、之前进位+分配空间,头建链表】
# 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:
if not l1 and not l2:
return None
list0 = []
i = 0
while l1 or l2:
# 只有一个数存在,有进位则两者相加,否则sum0=当前值
if not l1:
sum0 = l2.val if len(list0) 9:
if len(list0)
d26
1/3
leet 912 排序数组
快速排序
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
if not nums:
return []
def quick(l, r):
if l > r:
return nums
i,j,pivot = l, r, l
while i < j:
while i < j and nums[j] > nums[pivot]:
j -= 1
while i < j and nums[i] <= nums[pivot]:
i += 1
nums[i], nums[j] = nums[j], nums[i]
nums[j], nums[pivot] = nums[pivot], nums[j] # 与J交换
quick(l, j - 1) # 分治左排序
quick(j + 1, r) # 分治右排序
return nums
return quick(0, len(nums) - 1)
2/3
leet 1143 最长公共子序列
【dp pre动态规划】
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
if not text1 or not text2:
return 0
# pre与dp动态规划
pre = [0 for _ in range(len(text2) + 1)]
dp = [0 for _ in range(len(text2) + 1)]
for i in range(len(text1)):
j = 0
for j in range(1, len(text2) + 1):
if text1[i] == text2[j-1]: # 之前最优解+1的条件
dp[j] = pre[j-1] + 1
else:
dp[j] = max(pre[j], dp[j-1]) # 取最优
pre[j-1] = dp[j-1] # 更新pre
pre[j] = dp[j] # 更新pre至len(text2))
return dp[-1]