方法一:哈希表
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
dic = {}
for i, num in enumerate(nums):
if target - num in dic:
return [dic[target-num], i]
dic[num] = i #关键是遍历一次就将其记录下来,避免重复遍历
return
#时间复杂度O(N),空间复杂度O(N)
方法一:双指针
移动短板,min(height[i], height[j]) 可能增大
移动长板,min(height[i], height[j])只可能减小或者不变
因此每次向内移动短板
class Solution:
def maxArea(self, height: List[int]) -> int:
i, j = 0, len(height)-1
res = 0
while i < j:
res = max(min(height[i], height[j]) * (j - i), res)
if height[i] < height[j]: i +=1
else: j -= 1
return res
#答案写法:
class Solution:
def maxArea(self, height: List[int]) -> int:
i, j, res = 0, len(height) - 1, 0
while i < j:
if height[i] < height[j]:
res = max(res, height[i] * (j - i))
i += 1
else:
res = max(res, height[j] * (j - i))
j -= 1
return res
找出和为0的三个数
方法一:排序后用双指针
k用来遍历数组,双指针 i 和 j 分别指向k+1和len(nums) - 1,向中间遍历
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums.sort()
res = []
for k in range(len(nums)-2):
if nums[k] > 0: break # 1. because of j > i > k.
if k > 0 and nums[k] == nums[k-1]: continue # 2. skip the same `nums[k]`.
i, j = k + 1, len(nums)-1
while i < j: # 3. double pointer
s = nums[k] + nums[i] + nums[j]
if s < 0:
i += 1
while i < j and nums[i] == nums[i-1]: i +=1 #每次移动指针都要跳过相同的值
elif s > 0:
j -= 1
while i < j and nums[j] == nums[j+1]: j -= 1
else:
res.append([nums[k], nums[i], nums[j]])
i += 1
j -= 1
while i < j and nums[i] == nums[i-1]: i += 1
while i < j and nums[j] == nums[j+1]: j -= 1
return res
class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
nums.sort()
best = float('inf')
for k in range(len(nums)-2):
if k > 0 and nums[k] == nums[k-1]: continue
i, j = k + 1, len(nums) - 1
while i < j:
s = nums[k] + nums[i] + nums[j]
if s == target:
return s
if abs(s-target) < abs(best-target):
best = s
if s > target: #与15题类似,同样是比较s 和 target,分三种情况
j -= 1
while i < j and nums[j] == nums[j+1]: j -= 1
else:
i += 1
while i < j and nums[i] == nums[i-1]: i += 1
return best
方法一:双指针。
快慢指针
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
if len(nums) == 1: return len(nums)
i = j = 1
while j < len(nums):
if nums[j] != nums[j-1]:
nums[i] = nums[j]
i += 1
j += 1
return i
移除数组中的指定元素
方法一:类似26题快慢指针
#一次过!
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
slow = fast = 0
while fast < len(nums):
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
fast += 1
return slow
方法一:两次双指针
均从后面开始往前找
第一次找相邻的nums[i] < nums[j]
第二次从end到j找[j,end]中第一个大于nums[i]的nums[k]
class Solution:
def nextPermutation(self, nums: List[int]) -> None:
if len(nums) <= 1: return
i, j, k = len(nums) - 2, len(nums) - 1, len(nums) - 1
#找相邻的i, j, 使nums[i] < nums[j]
while i >= 0 and nums[i] >= nums[j]:
i -= 1
j -= 1
#从end到j找[j,end]中第一个大于nums[i]的nums[k]
if i >= 0: #不是最后一个排列
while nums[i] >= nums[k]:
k -= 1
nums[i], nums[k] = nums[k], nums[i]
#reverse nums[j,end]
i, j = j, len(nums) - 1
while i < j:
nums[i], nums[j] = nums[j], nums[i]
i += 1
j -= 1
# nums[j:].reverse()
方法一:二分查找
class Solution:
def search(self, nums: List[int], target: int) -> int:
if not nums:
return -1
l, r = 0, len(nums) - 1
while l <= r:
mid = (l + r) // 2
if nums[mid] == target:
return mid
if nums[0] <= nums[mid]: #mid在左半边, [l, mid]是有序的,接下来还要确定target所在的位置(区间)
if nums[0] <= target < nums[mid]:
r = mid - 1
else: #target=nums[mid]
l = mid + 1
else: #nums[mid] < nums[0],mid在右半边, [mid, r]是有序的
if nums[mid] < target <= nums[len(nums) - 1]:
l = mid + 1
else:
r = mid - 1
return -1
方法一:二分查找
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
if not nums: return [-1, -1]
i, j = 0, len(nums) -1
while i <= j: #找左边界
m = (i+j) // 2
if nums[m] >= target: #等于的时候还要继续减
j = m - 1
elif nums[m] < target:
i = m + 1
left = i
i, j = 0, len(nums) -1
while i <= j: #找右边界
m = (i+j) // 2
if nums[m] > target:
j = m - 1
elif nums[m] <= target:
i = m + 1
right = j
return [-1, -1] if left == len(nums) or nums[left] != target else [left, right]
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
l, r = 0, len(nums) - 1
while l <= r:
m = (l + r) // 2
if nums[m] == target:
return m
elif nums[m] > target:
r = m - 1
else:
l = m + 1
return l
方法一:模拟
#不要以为难,认真想想就做出来了!
class Solution:
def romanToInt(self, s: str) -> int:
res = 0
i = 0
n = len(s)
while i < n:
if s[i] == 'I':
res += 1
if i < n-1 and s[i+1] == 'V':
res += 3
i += 1
if i < n-1 and s[i+1] == 'X':
res += 8
i += 1
elif s[i] == 'V':
res +=5
elif s[i] == 'X':
res += 10
if i < n-1 and s[i+1] == 'L':
res += 30
i += 1
if i < n-1 and s[i+1] == 'C':
res += 80
i += 1
elif s[i] == 'L':
res += 50
elif s[i] == 'C':
res += 100
if i < n-1 and s[i+1] == 'D':
res += 300
i += 1
if i < n-1 and s[i+1] == 'M':
res += 800
i += 1
elif s[i] == 'D':
res += 500
else:
res += 1000
i += 1
return res
#改进写法:
class Solution:
def romanToInt(self, s: str) -> int:
value = {'I':1, 'V':5, 'X':10, 'L':50, 'C':100, 'D':500, 'M':1000}
i = 0
res = 0
n = len(s)
for i in range(n):
if i < len(s)-1 and value[s[i]] < value[s[i+1]]:
res -=value[s[i]]
else:
res += value[s[i]]
return res
方法一:动态规划
dp[i] [j] 根据dp[i+1] [j+1] 和s[i]、s[j]是否相同来判断
class Solution:
def longestPalindrome(self, s: str) -> str:
if len(s) < 2: return s
n = len(s)
max_len = 1
begin = 0
dp = [[False] * n for _ in range(n)] #n*n的矩阵
#dp[i][j]表示s[i:j]是否为回文串
for i in range(n):
dp[i][i] = True #单个的为True
#先枚举长度
for L in range(2, n + 1):
#枚举左边界,由边界由长度和左边界得到
for i in range(n):
j = L + i -1
#如果j超出数组边界,终止本次循环
if j > n-1: break
if s[i] != s[j]:
dp[i][j] = False
else: #即s[i] == s[j]
if L <= 3: #如果长度小于等于3,则必是回文串
dp[i][j] = True
else: #否则看s[i+1][j-1]是否为回文串
dp[i][j] = dp[i+1][j-1]
if dp[i][j] and L > max_len:
max_len = L
begin = i
return s[begin : begin + max_len]
判断是否为回文数,从左到右读和从右到左读是否一样
方法一:双指针
class Solution:
def isPalindrome(self, x: int) -> bool:
s = str(x)
i, j = 0, len(s)-1
while i < j:
if s[i] != s[j]: return False
i += 1
j -= 1
return True
方法二:反转一半数字
用反转的后一半来和前一半比较
class Solution:
def isPalindrome(self, x: int) -> bool:
if x < 0 or (x !=0 and x % 10 == 0):
return False
reverteNumber = 0
while x > reverteNumber: #当原始数字小于或等于反转后的数字,意味着处理了一半位数的数字
reverteNumber = reverteNumber * 10 + x % 10
x //= 10
return x == reverteNumber or x == (reverteNumber//10)
#当数字长度为奇数时,我们可以通过 revertedNumber/10 去除处于中位的数字。
#例如,当输入为 12321 时,在 while 循环的末尾我们可以得到 x = 12,reverteNumber = 123,
#由于处于中位的数字不影响回文(它总是与自己相等),所以我们可以简单地将其去除。
一开始想的是,导出每个字符索引的表达式,方向就错了
但根本不需要找出图中每个字符的索引,只要想把每一行按什么顺序存放在数组中就好了
方法一:按行存放
class Solution:
def convert(self, s: str, numRows: int) -> str:
if numRows < 2: return s
res = ['' for _ in range(numRows)]
flag = True
i = 0
for c in s:
res[i] += c
i += 1 if flag else -1
if i == 0 or i == numRows-1:
flag = not flag
return ''.join(res)
#答案写法:
class Solution:
def convert(self, s: str, numRows: int) -> str:
if numRows < 2: return s
res = ["" for _ in range(numRows)]
i, flag = 0, -1
for c in s:
res[i] += c
if i == 0 or i == numRows - 1: flag = -flag
i += flag
return "".join(res)
class Solution:
def myAtoi(self, s: str) -> int:
s = s.strip()
if not s: return 0
value = {
'0':0,
'1':1,
'2':2,
'3':3,
'4':4,
'5':5,
'6':6,
'7':7,
'8':8,
'9':9,
}
res = 0
neg = False
if s[0] == '-':
neg = True
s = s[1:]
elif s[0] == '+':
s = s[1:]
for c in s:
if c not in value: break
if c == '0':
res *= 10
else:
res = res* 10 + value[c]
if neg:
res = -res
if res < -2**31:
res = -2**31
else:
if res > 2**31 -1:
res = 2**31 -1
return res
# 写法改进:int()函数
class Solution:
def myAtoi(self, s: str) -> int:
s = s.strip()
if not s: return 0
res = 0
sign = 1
if s[0] == '-':
sign = -1
s = s[1:]
elif s[0] == '+':
s = s[1:]
for c in s:
if not '0' <= c <= '9': break
if c == '0':
res *= 10
else:
res = res* 10 + int(c)
res = sign * res
if res < 0:
if res < -2**31:
res = -2**31
else:
if res > 2**31 -1:
res = 2**31 -1
return res
方法二:状态机
读入一个字符看进入什么状态,如果是数字和符号的状态就进行相应的处理
【注意】init 写成int出错
INT_MAX = 2 ** 31 - 1
INT_MIN = -2 ** 31
class Automaton:
def __init__(self):
self.state = 'start'
self.sign = 1
self.ans = 0
self.table = {
'start': ['start', 'signed', 'in_number', 'end'],
'signed': ['end', 'end', 'in_number', 'end'],
'in_number': ['end', 'end', 'in_number', 'end'],
'end': ['end', 'end', 'end', 'end'],
}
def next_state(self, c):
if c.isspace():
return 0
if c == '+' or c == '-':
return 1
if c.isdigit():
return 2
return 3 #其他字符都进入end状态
def get(self, c): #只需要处理数字和符号两个状态,如果进入end状态,接下来的状态只能是end
self.state = self.table[self.state][self.next_state(c)]
if self.state == 'in_number':
self.ans = self.ans * 10 + int(c)
self.ans = min(self.ans, INT_MAX) if self.sign == 1 else min(self.ans, -INT_MIN)
elif self.state == 'signed':
self.sign = 1 if c == '+' else -1
class Solution:
def myAtoi(self, s: str) -> int:
automation = Automaton()
for c in s:
automation.get(c)
return automation.ans * automation.sign
【注意】 sort() 没写括号没报错,出错
#方法一:拿第一个字符串的每个字符去与后面的比较,后面所有字符串都有则将其加入到res中
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
res = ''
i = 0
for i in range(len(strs[0])):
for s in strs[1:]:
if i >= len(s) or strs[0][i] != s[i]: return res
res += strs[0][i]
return res
#方法二:逐个比较单词,更新最长公共子串(不断剪短)
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
res = strs[0]
for s in strs:
while s.find(res) != 0: #返回0说明起始位置是0,
res = res[0 : len(res) - 1] #res逐个减小,看是否满足
if res == '': return '' #可选
return res
#方法三:按字典排序数组,比较第一个和最后一个
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
strs.sort()
s1 = strs[0]
s2 = strs[-1]
res = ''
for i in range(len(s1)):
if i >= len(s2) or s1[i] != s2[i]: break
else:res += s1[i]
#等价于
# if i < len(s2) and s1[i] == s2[i]:
# res += s1[i]
# else: break
return res
方法一:模拟
#关键是字典所有可能出现的字母都列出来了,
#而且300是CCC一个个加上去的,而不是3C,其他的400、500直接表示,认真想想就知道
class Solution:
def intToRoman(self, num: int) -> str:
value = {
1000:'M',
900: 'CM',
500: 'D',
400:'CD',
100: 'C',
90: 'XC',
50: 'L',
40: 'XL',
10: 'X',
9: 'IX',
5: 'V',
4: 'IV',
1: 'I',
}
res = []
for val, symbol in value.items():
while num >= val:
num -= val
# res += symbol #字符串为不变对象,每次加都要新建,效率低
res.append(symbol)
if num == 0:
break
return ''.join(res)
方法二:硬编码
模运算和除法得到各位的数字
class Solution:
THOUSANDS = ["", "M", "MM", "MMM"]
HUNDREDS = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"]
TENS = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"]
ONES = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"]
def intToRoman(self, num: int) -> str:
return Solution.THOUSANDS[num // 1000] + \
Solution.HUNDREDS[num % 1000 // 100] + \
Solution.TENS[num % 100 // 10] + \
Solution.ONES[num % 10]
方法一:每多一个数字,都是在原来的结果上逐个元素地加上该数字对应的字母
#仔细想想就做出来了!
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
if not digits: return []
dic = {
'2': 'abc',
'3': 'def',
'4': 'ghi',
'5': 'jkl',
'6': 'mno',
'7': 'pqrs',
'8': 'tuv',
'9': 'wxyz',
}
res = [i for i in dic[digits[0]]] #比如['a', 'b', 'c']
for digit in digits[1:]:
tmp = []
for s in res: #原来的结果有多少个元素,就要在该元素的基础上,逐个地加入当前数字对应的字母
tmp += [s + i for i in dic[digit]]
res = tmp
return res
方法二:递归
类似于22括号生成
“当题目中出现 “所有组合” 等类似字眼时,我们第一感觉就要想到用回溯。”
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
if not digits: return []
dic = {
'2': 'abc',
'3': 'def',
'4': 'ghi',
'5': 'jkl',
'6': 'mno',
'7': 'pqrs',
'8': 'tuv',
'9': 'wxyz',
}
def backtrack(conbination, digit):
if len(digit) == 0:
res.append(conbination)
else:
for letter in dic[digit[0]]:
backtrack(conbination + letter, digit[1:]) #digit[1:]相当于不断地取下一个
res = []
backtrack('', digits)
return res
方法三:队列
#方法三:队列
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
if not digits: return []
dic = {
'2': 'abc',
'3': 'def',
'4': 'ghi',
'5': 'jkl',
'6': 'mno',
'7': 'pqrs',
'8': 'tuv',
'9': 'wxyz',
}
queue = [''] #相当于方法一中的存储结果
for digit in digits:
for _ in range(len(queue)):
tmp = queue.pop(0)
for letter in dic[digit]:
queue.append(tmp + letter) #每个结果都要加上当前数字的每个元素
return queue
方法一:栈
思路与答案一致!
class Solution:
def isValid(self, s: str) -> bool:
stack = []
left = {'{': '}', '[': ']', '(': ')'}
#遇到左括号则入栈,遇到右括号则查看是否和左括号的栈顶元素相同
for c in s:
if c in left:
stack.append(c)
else:
if not stack or c != left[stack[-1]]:
return False
else:
stack.pop()
return True if not stack else False
方法一:递归
类似于电话号码的字母组合
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
res = []
cur_str = ''
def dfs(cur_str, left, right):
if left == 0 and right == 0:
res.append(cur_str)
return
if left > right: #说明先先放右括号,不满足
return
if left > 0:
dfs(cur_str + '(', left - 1, right)
if right > 0:
dfs(cur_str + ')', left, right - 1)
dfs('', n, n)
return res
方法一:排序+字典
sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。
list 的 sort 方法返回的是对已经存在的列表进行操作,而内建函数 sorted 方法返回的是一个新的 list。
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
dic = {}
res = []
for s in strs:
tmp = s
# s = list(s)
# s.sort()
# s = ''.join(s)
s = ''.join(sorted(s))
if s in dic:
dic[s].append(tmp)
else:
dic[s] = [tmp]
for k, v in dic.items():
res.append(v)
return res
#写法二:
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
dic = collections.defaultdict(list) #类型名称作为初始化函数参数,如果key不存在,就把[]赋给key
for s in strs:
k = ''.join(sorted(s))
dic[k].append(s)
return list(dic.values())
方法一:排序
先按第一个元素排序,逐个处理区间,根据是否重合修改端点或者直接添加。不需要求出每个端点的索引
content.sort(key=lambda x:x[0])
x是参数入口,返回表达式x[0]这个结果
原理:在排序之前,content里的所有元素都会执行key的函数,这里指的就是lambda函数,计算出值之后,赋值给key。然后sort()是针对key进行排序,然后再根据这个key对应的值替换到排好序的content里。
例子:
strings = [‘foo’, ‘card’, ‘bar’, ‘aaaa’, ‘abab’]
根据字符串中不同字母的数量对一个字符串集合进行排序 strings.sort(key=lambda x: len(set(list(x))))
输出结果为:[‘aaaa’, ‘foo’, ‘abab’, ‘bar’, ‘card’]
list中的每个元素传入到 lambda x: len(set(list(x)))中计算出所含字母的数量,再根据这个数量进行排序。
#错解.大方向不是找每个区间端点的位置
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
res = []
i = 0
left, right = intervals[0][0], intervals[0][1]
while i < len(intervals):
j = i + 1
while j < len(intervals):
if intervals[i][1] >= intervals[j][0]:
right = intervals[j][1]
i += 1
j += 1
else:
res.append([left, right])
i += 1
j += 1
left, right = intervals[i][0], intervals[i][1]
return res
#不要试图找到每个区间的左右端点的索引,正确存放到结果就好
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
intervals.sort(key = lambda x: x[0])
res = []
for cur in intervals:
if not res or res[-1][1] < cur[0]: #res为空,或者区间不重合,直接添加到结果
res.append(cur) #当前结果的最大值(右)都小于当前区间的最小值(左),不重合
else: #当结果中最后一个元素的右端点大于当前元素的左端点,则说明当前结果包含当前区间
res[-1][1] = max(res[-1][1], cur[1])
return res
#错解,写复杂了,不需要像快排那样交换
class Solution:
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
i, j = 0, len(nums) - 1
n0 = 0
while i < j:
while i < j and nums[i] == 0:
i += 1
n0 += 1
while i < j and nums[j] != 0: j -= 1
nums[i], nums[j] = nums[j], nums[i]
n0 += 1
i += 1
j -= 1
i = n0 - 1
j = len(nums) - 1
while i < j:
while i < j and nums[i] == 1: i += 1
while i < j and nums[j] != 1: j -= 1
nums[i], nums[j] = nums[j], nums[i]
return
#设置两个指针,遇到0则与前面的交换就好
class Solution:
def sortColors(self, nums: List[int]) -> None:
n = len(nums)
p = 0
for i in range(n):
if nums[i] == 0:
nums[p], nums[i] = nums[i], nums[p]
p += 1 #i走在前面,遇到0则交换,p不会比i先遇到新的0
for i in range(p, n):
if nums[i] == 1:
nums[p], nums[i] = nums[i], nums[p]
p += 1
return
方法二:在增加一个指针,p0交换0,p1交换1
class Solution:
def sortColors(self, nums: List[int]) -> None:
n = len(nums)
p0 = p1 = 0
for i in range(n):
if nums[i] == 1:
nums[i], nums[p1] = nums[p1], nums[i]
p1 += 1
elif nums[i] == 0:
nums[i], nums[p0] = nums[p0], nums[i]
if p0 < p1: #交换到后面的是nums[i]必然是1,所以要将将其换到p1
nums[i], nums[p1] = nums[p1], nums[i]
p0 += 1
p1 += 1 #1相当于整体往后移动1格
方法一:双指针
j指向要插入的数字,遇到第一个比其大的数则交换
有没充分利用排序数组的条件,nums2不是升序也可以
#一次过!
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.
"""
nums1[m:] = nums2 #然后直接nums1.sort()也能通过
j = m
while j < m + n:
for i in range(j):
if nums1[i] > nums1[j]:
nums1[i], nums1[j] = nums1[j], nums1[i]
j += 1
方法二:按一个个取出来比较,在加入到一个数组lst中,最后将其赋值给nums1
时间O(m + n)
空间复杂度较大 O(m + n)
a = [[1, 2, 3], [4, 5, 6]]
b = a, 不是拷贝,只是将地址赋给b,改变一个,另一个也变
浅拷贝:目的是拷贝到另一块内存,但只复制了一层
b = a[ : ], 改变b的元素,a不变,如a[0] = 1 ,b不会改变;但改变第二层则b发生改变,如a[0].append(7)
b = a.copy()
b = [i for i in a]
深拷贝:深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。深拷贝之后,两者互不影响。
b = copy.deepcopy(a)
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
lst = []
p1 = p2 = 0
while p1 < m or p2 < n:
if p1 == m:
lst[p1+p2:] = nums2[p2:]
break
if p2 == n:
lst[p1+p2:] = nums1[p1:m]
break
if nums1[p1] <= nums2[p2]:
lst.append(nums1[p1])
p1 += 1
else:
lst.append(nums2[p2])
p2 += 1
nums1[:] = lst[:]
#nums1 = lst 得不到正确的结果,因为把lst的地址赋给了nums1,但在测试的时候肯定还是用nums1原来的地址
方法三:逆向双指针
从后面开始取大的一个放到nums1的后半部分,节省空间开销
空间复杂度降为O(1)
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
p1 = m - 1
p2 = n - 1
j = m + n - 1
while p1 >= 0 or p2 >= 0:
if p1 < 0:
nums1[j] = nums2[p2]
p2 -= 1
elif p2 < 0:
nums1[j] = nums1[p1]
p1 -= 1
elif nums1[p1] >= nums2[p2]:
nums1[j] = nums1[p1]
p1 -= 1
else:
nums1[j] = nums2[p2]
p2 -= 1
j -= 1
#错解,大方向没错。
#pointer.val >= cur.val: #找到第一个比cur.val大的不好插入;
# 应该找到比cur.val大的前一个,用pre.next.val <= curr.val:来判断是否找到,用pre来从头遍历
#同时用lastSorted来标记已排序链表的最后一个元素,相当于cur_pre,cur的前一个
class Solution:
def insertionSortList(self, head: ListNode) -> ListNode:
res = pointerx_pre = ListNode(0, head)
pointer = cur_pre = head
cur = head.next
while cur:
cur_pre.next = cur.next
if pointer.val>= cur.val: #左边是有序的,找到第一个比cur.val大的即可
cur.next = pointer
pointerx_pre.next = cur
pointerx_pre = res
pointer = res.next
else:
if pointer != cur_pre.next:
pointer = pointer.next
pointerx_pre = pointerx_pre.next
cur = cur.next
cur_pre = cur_pre.next
return res
class Solution:
def insertionSortList(self, head: ListNode) -> ListNode:
if not head:return head
res = ListNode(0, head)
lastSorted = head
cur = head.next
while cur:
if lastSorted.val <= cur.val:
lastSorted = lastSorted.next
else:
pre = res #要插入位置的前一个节点
while pre.next.val <= cur.val: #在lasSorted及其前面必然有一个大于cur.val,因为lastSorted.val > cur.val
pre = pre.next
lastSorted.next = cur.next #因为lastSorted.val > cur.val,所以lastsorted的位置不用变,只需指向下一个删除cur
cur.next = pre.next
pre.next = cur
cur = lastSorted.next
return res.next
方法一:归并排序
不断地从中间分开,分到最后只剩一个,一个元素的链表一定是有序的,再按两个有序链表的方法合并
用快慢指针的方法将链表分为两部分,一个走一步,另一个走两步
#插入排序,O(n^2),超时
class Solution:
def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head: return head
res = ListNode(0, head)
lastSorted = head
cur = head.next
while cur:
if lastSorted.val <= cur.val:
lastSorted = lastSorted.next
else:
pre = res
while pre.next.val <= cur.val:
pre = pre.next
lastSorted.next = cur.next
cur.next = pre.next
pre.next = cur
cur = lastSorted.next #注意不要惯性地cur = cur.next
return res.next
#归并排序
class Solution:
def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head or not head.next: return head
slow, fast = head, head.next
while fast and fast.next: #画图就知道,节点为偶数的时候,最后走到fast.next为空,奇数的时候最后fast为空
slow = slow.next
fast = fast.next.next
mid = slow.next
slow.next = None #从中间断开
left = self.sortList(head)
right = self.sortList(mid)
cur = dummy = ListNode(0)
while left and right:
if left.val <= right.val:
cur.next = left
left = left.next
else:
cur.next = right
right = right.next
cur = cur.next
cur.next = left if left else right
return dummy.next
#归并排序,写法二
class Solution:
def sortList(self, head: ListNode) -> ListNode:
def sortFunc(head: ListNode, tail: ListNode) -> ListNode:
if not head:
return head
if head.next == tail: #相当于只有一个节点的时候,这里tail初始化为None
head.next = None
return head
slow = fast = head
while fast != tail:
slow = slow.next
fast = fast.next
if fast != tail:
fast = fast.next
mid = slow
return merge(sortFunc(head, mid), sortFunc(mid, tail))
def merge(head1: ListNode, head2: ListNode) -> ListNode:
dummyHead = ListNode(0)
temp, temp1, temp2 = dummyHead, head1, head2
while temp1 and temp2:
if temp1.val <= temp2.val:
temp.next = temp1
temp1 = temp1.next
else:
temp.next = temp2
temp2 = temp2.next
temp = temp.next
if temp1:
temp.next = temp1
elif temp2:
temp.next = temp2
return dummyHead.next
return sortFunc(head, None)
方法一:排序
时间O(n logn)还不如哈希表只遍历一次
class Solution:
def majorityElement(self, nums: List[int]) -> int:
nums.sort()
return nums[len(nums)//2]
方法二:哈希表
时间:O(n)
空间:O(n)
class Solution:
def majorityElement(self, nums: List[int]) -> int:
dic = {}
for num in nums:
if num in dic:
dic[num] += 1
else:
dic[num] = 1
return max(dic.keys(), key = dic.get)
#dic.keys()以列表返回字典的所有键
#max(testlist, key),对于每个testlist的元素,先按key制定的函数处理,然后再比较,返回testlist原来的元素
#dic,get(key),获取键的值
方法三:摩尔投票
多数元素与非多数元素两两抵消,至少还会剩余一个多数元素
时间:O(n)
空间:O(1)
#摩尔投票
class Solution:
def majorityElement(self, nums: List[int]) -> int:
count = 0
for num in nums:
if count == 0:
candidate = num
count += 1 if num == candidate else -1
return candidate
方法一:最大堆
只需考虑一个元素,而不像返回前k个最小元素那样得到一整个排序数组
用最大堆即可
将num压入堆的时候,会维持顺序,-maxHeap[0]一直是最大的,heappop(maxHeap)出来的也是最大的
最大堆的实现:https://leetcode-cn.com/problems/kth-largest-element-in-an-array/solution/cpython3java-1da-gen-dui-diao-ku-2shou-l-xveq/
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
maxHeap = []
for num in nums:
heapq.heappush(maxHeap, -num) #取反是大根堆,否则小跟堆
for _ in range(k - 1):
heapq.heappop(maxHeap)
return -maxHeap[0]
方法二:快速排序
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
def quickSort(nums, l, r):
if l >= r: return
i, j = l, r
while i < j:
while i < j and nums[j] <= nums[l]: j -= 1
while i < j and nums[i] >= nums[l]: i += 1
nums[i], nums[j] = nums[j], nums[i]
nums[l], nums[i] = nums[i], nums[l]
quickSort(nums, l, i - 1)
quickSort(nums, i + 1, r)
quickSort(nums, 0, len(nums)-1)
return nums[k-1]
方法三:快速选择(快排的改进)
降序排序
如果哨兵所在的索引 idx 正好是k-1,那么哨兵最是要找的数(因为他的左边都比他小)
如果 idx < k -1,则说明目标还不够小,应该在哨兵的右边,只需要对右边进行排序
否则对左边排序
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
l = 0
r = len(nums) - 1
while True:
idx = quickSort(nums, l, r)
if idx == k - 1:
return nums[idx]
elif idx < k - 1:
l = idx + 1
else:
r = idx - 1
def quickSort(nums, l, r):
i, j = l, r
while i < j:
while i < j and nums[j] <= nums[l]: j -= 1
while i < j and nums[i] >= nums[l]: i += 1
nums[i], nums[j] = nums[j], nums[i]
nums[l], nums[i] = nums[i], nums[l]
return i
判断是否存在重复的元素
方法一:哈希表
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
dic = {}
for num in nums:
if num not in dic:
dic[num] = 1
else:
return True
return False
方法二:排序
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
nums.sort()
for i in range(len(nums)-1):
if nums[i] == nums[i+1]:
return True
return False
方法三:利用set()
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
return len(set(nums)) != len(nums)
方法一:哈希表
class Solution:
def majorityElement(self, nums: List[int]) -> List[int]:
dic = {}
n = len(nums) // 3
res = []
for num in nums:
if num in dic:
dic[num] += 1
else:
dic[num] = 1
for k, v in dic.items():
if v > n: res.append(k)
return res
方法二:摩尔投票
注意到最多有两种元素的数量超过n // 3
三个不同的抵消
剩下的才可能是超过n//3的
但不一定,因为没法保证剩下的一定大于n/3,比如有n-1个a和1个b,都会留下来,但是b只有一个
class Solution:
def majorityElement(self, nums: List[int]) -> List[int]:
element1 = element2 = 0
vote1 = vote2 = 0
for num in nums:
if vote1 > 0 and num == element1: #注意先判断完vote > 0 的情况,否则[2,2]这种样例会将得到结果错误结果[2,2]
vote1 += 1
elif vote2 > 0 and num == element2:
vote2 += 1
elif vote1 == 0:
element1 = num
vote1 += 1
elif vote2 == 0:
element2 = num
vote2 += 1
else: #三者都不同,抵消
vote1 -= 1
vote2 -= 1
res = []
count1 = count2 = 0
#检查剩下的元素是否满足大于n//3的条件
for num in nums:
if vote1 > 0 and num == element1:
count1 += 1
if vote2 > 0 and num == element2:
count2 += 1
if vote1 > 0 and count1 > len(nums)//3:
res.append(element1)
if vote2 > 0 and count2 > len(nums)//3:
res.append(element2)
return res
方法一:递归
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
def dfs(root):
if not root: return
dfs(root.left)
res.append(root.val)
dfs(root.right)
dfs(root)
return res
方法二:利用栈,迭代实现
这题的真正难点在于如何用非递归的方式实现。
递归实现时,是函数自己调用自己,一层层的嵌套下去,操作系统/虚拟机自动帮我们用 栈 来保存了每个调用的函数,现在我们需要自己模拟这样的调用过程。
递归的调用过程是这样的:
dfs(root.left)
dfs(root.left)
dfs(root.left)
为null返回
打印节点
dfs(root.right)
dfs(root.left)
dfs(root.left)
........
递归的调用过程是不断往左边走,当左边走不下去了,就打印节点,并转向右边,然后右边继续这个过程。
我们在迭代实现时,就可以用栈来模拟上面的调用过程。
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
stack = []
while stack or root: #小心别写成and!
if root:
stack.append(root)
root = root.left #不断往左子树方向走,如果左子树不为空,每走一次就将当前节点保存到栈中
else:
tmp = stack.pop() #如果左子树为空,则弹出当前节点,访问,并处理右节点
res.append(tmp.val)
root = tmp.right
return res
方法一:递归
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
def dfs(root):
if not root:
return
res.append(root.val)
dfs(root.left)
dfs(root.right)
dfs(root)
return res
方法二:迭代
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
stack = []
while stack or root:
if root:
res.append(root.val)
stack.append(root) #用栈来维护遍历过的节点一遍回溯
root = root.left
else:
tmp = stack.pop()
root = tmp.right
return res
方法一:递归
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
def dfs(root):
if not root: return
dfs(root.left)
dfs(root.right)
res.append(root.val)
dfs(root)
return res
方法二:递归
用前序遍历的模板,先访问根节点
访问顺序为:根,右,左
但每次都插入队头,结果变为
左, 右, 根
#方法二:迭代
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
stack = []
while stack or root:
if root:
res.insert(0, root.val)
stack.append(root)
root = root.right
else:
tmp =stack.pop()
root = tmp.left
return res
方法一:深度优先搜索
#错解
#如果两个二叉树都为空,则两个二叉树相同!应直接返True
#而且这是个遍历问题,不涉及到改,在遍历过程中 判断即即可
class Solution:
def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
if not p and not q:
return
if (not p or not q) and p.val != q.val:
return False
self.isSameTree(p.left, q.left)
self.isSameTree(p.right, q.right)
return True
class Solution:
def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
if not p and not q: #到空了还没有False的就返回True
return True
elif not p or not q: #在不是两个都为空的条件下,那么至少有一个不为空。如果有一个为空则就是一个为空另一个不为空,那么一定不相同
return False
elif p.val != q.val:
return False
else:
return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)
https://leetcode-cn.com/problems/same-tree/solution/xie-shu-suan-fa-de-tao-lu-kuang-jia-by-wei-lai-bu-/
一、在 BST 中查找一个数是否存在
根据我们的指导思想,可以这样写代码:
boolean isInBST(TreeNode root, int target) {
if (root == null) return false;
if (root.val == target) return true;
return isInBST(root.left, target)
|| isInBST(root.right, target);
}
这样写完全正确,充分证明了你的框架性思维已经养成。现在你可以考虑一点细节问题了:如何充分利用信息,把 BST 这个“左小右大”的特性用上?
很简单,其实不需要递归地搜索两边,类似二分查找思想,根据 target 和 root.val 的大小比较,就能排除一边。我们把上面的思路稍稍改动:
boolean isInBST(TreeNode root, int target) {
if (root == null) return false;
if (root.val == target)
return true;
if (root.val < target)
return isInBST(root.right, target);
if (root.val > target)
return isInBST(root.left, target);
// root 该做的事做完了,顺带把框架也完成了,妙
}
于是,我们对原始框架进行改造,抽象出一套针对 BST 的遍历框架:
void BST(TreeNode root, int target) {
if (root.val == target)
// 找到目标,做点什么
if (root.val < target)
BST(root.right, target);
if (root.val > target)
BST(root.left, target);
}
二、在 BST 中插入一个数
对数据结构的操作无非遍历 + 访问,遍历就是“找”,访问就是“改”。具体到这个问题,插入一个数,就是先找到插入位置,然后进行插入操作。
上一个问题,我们总结了 BST 中的遍历框架,就是“找”的问题。直接套框架,加上“改”的操作即可。一旦涉及“改”,函数就要返回 TreeNode 类型,并且对递归调用的返回值进行接收。
TreeNode insertIntoBST(TreeNode root, int val) {
// 找到空位置插入新节点
if (root == null) return new TreeNode(val);
// if (root.val == val)
// BST 中一般不会插入已存在元素
if (root.val < val)
root.right = insertIntoBST(root.right, val);v //这里比检查一个数是否存在,多了等号
if (root.val > val)
root.left = insertIntoBST(root.left, val);
return root;
}
判断一颗二叉树是否对称
方法一:递归
没有相等就返回Ture这样的操作,而是不等就返回False,都为空在返回Ture
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
def recur(l, r):
if not l and not r: #都为空,相当于到了根节点,返回Ture
return True
elif not l or not r: #排除了都为空,一个为空一个不为空,必然不对称
return False
elif l.val != r.val: #值不等也不对称
return False
else:
return recur(l.left, r.right) and recur(l.right, r.left)
if not root: return True
return recur(root.left, root.right)
方法一:递归
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
if not root: return 0
return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right))
方法二:广度优先搜索
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
if not root: return 0
stack = [root]
res = 0
while stack:
res += 1
for _ in range(len(stack)):
node = stack.pop(0) #记得出队的是队首
if node.left: stack.append(node.left)
if node.right: stack.append(node.right)
return res
判断一个二叉树是否平衡
方法一:暴力遍历每个节点,判断是否满足条件
对于判断这个主函数来说是自顶向下的
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
if not root: return True
# return abs(self.maxDepth(root.left) -self.maxDepth(root.right)) <= 1 #还要判断每个节点是不是平衡的
return abs(self.maxDepth(root.left) -self.maxDepth(root.right)) <= 1 and self.isBalanced(root.left) and self.isBalanced(root.right)
def maxDepth(self, root):
if not root: return 0
return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right))
方法二:不用每次都计算节点的高度,一遇到不满足的则返回-1标记即可。剪枝
自底向上,在回溯的过程就能判断,不满足条件直接返回-1,不必再计算高度
同样也是计算高度的过程
#方法二:
class Solution:
def isBalanced(self, root: TreeNode) -> bool:
def height(root):
if not root: return 0
leftHeight = height(root.left)
rightHeight = height(root.right)
if leftHeight == -1 or rightHeight == -1 or abs(leftHeight - rightHeight) > 1:
return -1
else:
return 1 + max(leftHeight, rightHeight) #同样也是计算高度的过程,不满足才会别记为-1
return height(root) >= 0 #如果满足平衡二叉树则最后返回的是高度,否则-1
方法一:深度优先搜索
关键是判断什么条件下该返回什么
#错解:
#例如是只有连续5个右子节点的情况,应该返回6,但这里会返回一。因为叶子节点的定义是左右子节点都为空
class Solution:
def minDepth(self, root: TreeNode) -> int:
if not root: return 0
return 1 + min(self.minDepth(root.left), self.minDepth(root.right))
class Solution:
def minDepth(self, root: TreeNode) -> int:
if not root: #空时返回零
return 0
elif not root.left and not root.right: #左右节点都为空返回 1
return 1
elif root.left and root.right: #左右节点都不为空,返回小的那个深度
return min(self.minDepth(root.left), self.minDepth(root.right)) + 1
else:
return self.minDepth(root.left) + 1 if root.left else self.minDepth(root.right) + 1 #有一个为空,需要返回非空的那个深度才满足叶子节点的条件。因为一个子节点为空不是叶子节点,两个为空才是。
方法二:广度优先搜索
方法一:DFS
#错解
class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
if not root:
return
targetSum -= root.val
if not root.left and not root.right and targetSum == 0:
return True
self.hasPathSum(root.left, targetSum)
self.hasPathSum(root.right, targetSum)
return False
class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
if not root:
return False #因为下面用or连接左右节点,如果其中一个节点是空、False,还有另一个节点继续下去。如果两个节点都空就是下面的条件了
targetSum -= root.val
if not root.left and not root.right and targetSum == 0:
return True
return self.hasPathSum(root.left, targetSum) or self.hasPathSum(root.right, targetSum)
#一开始喂入的targetsum是多少就是多少,不会因为后面有调用函数而发生改变,因为返回到现场,参数还是用的喂进去的参数。
#赋值给了参数列表,就不会跟着targetSum的变化而变化
#写法二:
class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
if not root:
return False
if not root.left and not root.right:
return targetSum == root.val
return self.hasPathSum(root.left, targetSum - root.val) or self.hasPathSum(root.right, targetSum - root.val)
方法二:回溯,遍历保存路径,遇到路径和与目标相同的就返回True
class Solution(object):
def hasPathSum(self, root, sum):
if not root: return False
return self.dfs(root, sum, [root.val])
def dfs(self, root, target, path):
if not root: return False
if sum(path) == target and not root.left and not root.right:
return True
left_flag, right_flag = False, False
if root.left:
left_flag = self.dfs(root.left, target, path + [root.left.val])
if root.right:
right_flag = self.dfs(root.right, target, path + [root.right.val])
return left_flag or right_flag
方法三:BFS
方法四:栈
方法一:递归
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
def recur(root, lower, upper):
if not root:
return True #都到空了还没有返回False的就返回Ture
if root.val <= lower or root.val >= upper:
return False #关键是找到什么时候不等
return recur(root.left, lower, root.val) and recur(root.right, root.val, upper)
if not root: return
return recur(root, float('-inf'), float('+inf'))
方法二:中序遍历,递归
中序遍历的的结果应该是递增的,即当前节点的值应该大于前一个节点,否则返回False
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
self.pre = float('-inf')
def recur(root):
if not root:
return True
l = recur(root.left) #中序遍历,会一直进入到左子树的尽头,才会进行下面的访问操作
if root.val <= self.pre:
return False
self.pre = root.val
r = recur(root.right)
return l and r
return recur(root)
方法三:中序遍历,迭代
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
stack = []
pre = float('-inf')
while stack or root:
if root:
stack.append(root)
root = root.left
else:
tmp = stack.pop()
if tmp.val <= pre:
return False
pre = tmp.val
root = tmp.right
return True
中序遍历迭代法的另一种写法
class Solution:
def isValidBST(self, root: TreeNode) -> bool:
stack, inorder = [], float('-inf')
while stack or root:
while root:
stack.append(root)
root = root.left
root = stack.pop()
# 如果中序遍历得到的节点的值小于等于前一个 inorder,说明不是二叉搜索树
if root.val <= inorder:
return False
inorder = root.val
root = root.right
return True
恢复有两个节点位置对调了的搜索二叉树
方法一:中序遍历
将中序遍历的节点保存到列表中,在遍历列表找出位置错误的节点,改变他们的值
class Solution:
def recoverTree(self, root: Optional[TreeNode]) -> None:
"""
Do not return anything, modify root in-place instead.
"""
lst = []
def dfs(root):
if not root:
return
dfs(root.left)
lst.append(root)
dfs(root.right)
dfs(root)
x = None
y = None
for i in range(len(lst)-1):
if lst[i].val > lst[i+1].val:
if not x: #第一次遇到非升序才才将i给x
x = lst[i]
y = lst[i+1]
if x and y:
x.val, y.val = y.val, x.val
方法二:中序遍历
在遍历的过程中就找到错误的位置,并记录到x和y。同时要维护一个pre来记录前一个节点
class Solution:
def recoverTree(self, root: Optional[TreeNode]) -> None:
self.x = None
self.y = None
self.pre = None
def dfs(root):
if not root:
return
dfs(root.left)
if not self.pre:
self.pre = root #用最左边那个初始化pre
else:
if self.pre.val > root.val:
if not self.x:
self.x = self.pre
self.y = root
self.pre = root
dfs(root.right)
dfs(root)
if self.x and self.y:
self.x.val, self.y.val = self.y.val, self.x.val
方法一:递归
class Solution:
def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
self.path = []
self.res = []
def dfs(root, targetSum):
if not root:
return
self.path.append(root.val)
if not root.left and not root.right:
if root.val == targetSum:
self.res.append(self.path[:]) #这里要用切片拷贝
dfs(root.left, targetSum - root.val)
dfs(root.right, targetSum - root.val)
self.path.pop()
dfs(root, targetSum)
return self.res
方法一:先序遍历然后逐个修改指针
#错解,试图用模板解决,想得太复杂了。还不如先暴力得到先序遍历的列表再修改指针
class Solution:
def flatten(self, root: TreeNode) -> None:
"""
Do not return anything, modify root in-place instead.
"""
self.pre = None
def dfs(root):
if not root:
return
if not self.pre:
self.pre = root
else:
self.pre.left = None
self.pre.right = root
self.pre = root
dfs(root.left)
dfs(root.right)
dfs(root)
#先序遍历,递归
class Solution:
def flatten(self, root: TreeNode) -> None:
stack = []
def dfs(root):
if not root:
return
stack.append(root)
dfs(root.left)
dfs(root.right)
dfs(root)
for i in range(len(stack)-1):
stack[i].left = None
stack[i].right = stack[i+1]
方法二:遍历,根据题目特点灵活地改变指针指向
空间复杂度降为O(1)
class Solution:
def flatten(self, root: TreeNode) -> None:
while root:
if not root.left: #左子树为空,直接考虑下一个节点
root = root.right
else:
pre = root.left
while pre.right:
pre = pre.right
pre.right = root.right #左子树的最右节点的下一个一定是接当前的右子树
root.right = root.left
root.left = None
root = root.right
方法一:层序遍历
维持一个pre指针指向前一个节点
#myCode
class Solution:
def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
if not root: return
stack = [root]
while stack:
pre = None
for _ in range(len(stack)):
node = stack.pop(0)
if not pre:
pre= node
else:
pre.next = node
pre = node
if node.left: stack.append(node.left)
if node.right: stack.append(node.right)
pre.next = None
return root
方法二:递归
https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/solution/dong-hua-yan-shi-san-chong-shi-xian-116-tian-chong/
class Solution(object):
def connect(self, root):
"""
:type root: Node
:rtype: Node
"""
def dfs(root):
if not root:
return
left = root.left
right = root.right
# 配合动画演示理解这段,以root为起点,将整个纵深这段串联起来
while left:
left.next = right
left = left.right
right = right.left
# 递归的调用左右节点,完成同样的纵深串联
dfs(root.left)
dfs(root.right)
dfs(root)
return root
方法一:贪心
maxPos保存这一次所有可能的起跳点能到达的最远位置
从当前到边界,是所有可能的起跳点
而边界是上一次跳得最远的位置
class Solution:
def jump(self, nums: List[int]) -> int:
res = 0
end = 0
maxPos = 0
for i in range(len(nums) - 1):
maxPos = max(i + nums[i], maxPos) # maxPos保存当前这一次跳远能到达的最远的位置
if i == end: #遍历完上一次跳跃的最远位置,就知道这一次能到达的最远位置
end = maxPos
res += 1
return res
方法一:双指针
#错解
应该增加一个dummy节点,遍历到目标的前一个才好删除
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
if not head or not head.next: return None
slow = fast = head
for _ in range(n):
fast = fast.next
while fast:
slow = slow.next
fast = fast.next
slow.next = slow.next.next
return head
#双指针,增加一个dummy结点!!!
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
slow = dummy = ListNode(0, head)
fast = head
for _ in range(n):
fast = fast.next
while fast:
fast = fast.next
slow = slow.next
slow.next = slow.next.next
return dummy.next
方法二:先遍历一遍得到长度k
再遍历k-n次
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
node = head
k = 0
while node:
node = node.next
k += 1
dummy = cur= ListNode(0, head) #还是要有dummy才好删除,特别是第一个
for _ in range(k-n):
cur = cur.next
cur.next = cur.next.next
return dummy.next