LeetCode-算法:81-100(Python)
81. 搜索旋转排序数组 II
思路
是33. 搜索旋转排序数组的进阶
- 顺序查找最小的数为数组nums有序的分界点
- target小于最小的数返回false
- target大于等于数组第一个数,则target可能存在数组左边有序序列中,否则target可能存在数组右边有序序列中
- 在有序序列中使用二分查找target
class Solution(object):
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: bool
"""
# 顺序查找最小的数为数组nums有序的分界点
def findMin(nums):
i, n = 0, len(nums)
for j in range(1, n):
if nums[j] >= nums[i]:
i += 1
else:
return j # 如果nums[j]小于nums[i],返回j。当最小的数有数个,返回最左边的那个
return 0 # 列表有序,最小的数在第0位
# 二分查找
def binary(nums, target):
l, r = 0, len(nums) - 1
while l <= r:
mid = (l + r)//2
if nums[mid] == target:
return True
elif nums[mid] > target:
r = mid - 1
else:
l = mid + 1
return False
l, r = 0, len(nums)-1
mini = findMin(nums)
if not nums or target < nums[mini]:
return False
if mini == l: # # mini在第一位,直接使用二分查找
return binary(nums, target)
if target >= nums[l]:
return binary(nums[0:mini], target)
else:
return binary(nums[mini:], target)
思路
O(∩_∩)O哈哈~
class Solution(object):
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: bool
"""
return True if target in nums else False
82. 删除排序链表中的重复元素 II
思路
是83. 搜索旋转排序数组的进阶
- 当head为空或只有一个结点时,直接返回
- 创建左指针l指向第一个结点和右指针指r向第二个结点,r往右遍历
- 当l.val不等于r.val时:
- 如果r是否在l右边一个位置,证明l.val只有一个,则保存l.val至返回的链表中,且cur往右移动一位
- 如果r不在l右边一个位置,证明l.val有重复结点,则更新l=r
- 当l.val等于r.val时:r = r.next,r指向下一位进入下个循环
- 最后,如果l.val是最后一个结点,证明l.val只有一个数没有重复(如果l不在最后一个位置,证明最后结点的数有重复)
- 当l.val不等于r.val时:
- 返回没有重复数字结点的链表rehead.next
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def deleteDuplicates(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
if not head or head.next == None:
return head
l, r = head, head.next
rehead = ListNode(None)
cur = rehead
while r != None:
if l.val != r.val:
if l.next == r:
cur.next = ListNode(l.val)
cur = cur.next
l = r
r = r.next
if l.next == None:
cur.next = ListNode(l.val)
return rehead.next
83. 删除排序链表中的重复元素
思路
是26. 删除排序数组中的重复项的链表形式,双指针
- 当head为空或只有一个结点时,直接返回
- 创建左指针l指向第一个结点和右指针指r向第二个结点,r往右遍历
- 当l.val不等于r.val时:l指向下一个位置,并令l.val = r.val
- 当l.val等于r.val时:r = r.next,r指向下一位进入下个循环
- 最后,l.next = None删除后面多余的结点
- 返回head
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def deleteDuplicates(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
if not head or head.next == None:
return head
l, r = head, head.next
while r != None:
if l.val != r.val:
l = l.next
l.val = r.val
r = r.next
l.next = None
return head
84. 柱状图中最大的矩形
思路
- 当柱子heights[i]的高度大于前方的柱子时,将柱子下标i存在列表stack(栈)中。
- 当柱子heights[i]的高度小于等于前方柱子时,在stack栈顶的下标为i-1比i高的柱子,此时,计算i前方的柱子面积。如栈顶stack.pop()是i-1,则高度为heights[i-1],宽度为i减pop掉heights[i-1]后的栈顶再减1。(heights[stack[-1]] >= heights[i],柱子i-1比柱子i高,从i-1柱子往左直到柱子j(j<i-1)比i矮,高度为i-1到j+1递减,宽度1到i-j-1递增。为计算面积)。计算后更新max_area。当柱子heights[i]的高度小于前方柱子heights[i-1]时,添加i至stack中。
- 最后,剩下升序排列的柱子,i到了n的位置,与2同理计算面积与max_area比较后更新max_area
class Solution(object):
def largestRectangleArea(self, heights):
"""
:type heights: List[int]
:rtype: int
"""
n = len(heights)
if not n:
return 0
stack, max_area = list(), 0
stack.append(-1)
for i in range(n):
while stack[-1] != -1 and heights[stack[-1]] >= heights[i]:
max_area = max(max_area, heights[stack.pop()]*(i-stack[-1]-1))
stack.append(i)
while stack[-1] != -1:
max_area = max(max_area, heights[stack.pop()]*(n-stack[-1]-1))
return max_area
85. 最大矩形
思路
遍历每一行求84. 柱状图中最大的矩形
栗子:
matrix = [
[“1”,“0”,“1”,“0”,“0”],
[“1”,“0”,“1”,“1”,“1”],
[“1”,“1”,“1”,“1”,“1”],
[“1”,“0”,“0”,“1”,“0”]
]
第一行的柱状图heights: [1, 0, 1, 0, 0]
第二行的柱状图heights: [2, 0, 2, 1, 1]
第三行的柱状图heights: [3, 1, 3, 2, 2]
第四行的柱状图heights: [4, 0, 0, 3, 0]
class Solution(object):
def maximalRectangle(self, matrix):
"""
:type matrix: List[List[str]]
:rtype: int
"""
if not matrix:
return 0
def largestRectangleArea(heights):
stack, max_area = list(), 0
stack.append(-1)
for i in range(n):
while stack[-1] != -1 and heights[stack[-1]] >= heights[i]:
max_area = max(max_area, heights[stack.pop()]*(i-stack[-1]-1))
stack.append(i)
while stack[-1] != -1:
max_area = max(max_area, heights[stack.pop()]*(n-stack[-1]-1))
return max_area
m, n, max_area = len(matrix), len(matrix[0]), 0
heights = [0]*n
for i in range(m):
for j in range(n):
heights[j] =heights[j] + 1 if matrix[i][j]=='1' else 0
# print(heights)
max_area = max(max_area, largestRectangleArea(heights))
return max_area
86. 分隔链表
思路
栗子:
head -> 1 -> 4 -> 3 -> 2 -> 5 -> 2
x = 3
##############################
r,rehead
0 -> 1 -> 4 -> 3 -> 2 -> 5 -> 2
r,l
0 -> 1 -> 4 -> 3 -> 2 -> 5 -> 2
l r t
0 -> 1 -> 4 -> 3 -> 2 -> 5 -> 2
l t r
0 -> 1 -> 2 -> 4 -> 3 -> 5 -> 2
l r
0 -> 1 -> 2 -> 4 -> 3 -> 5 -> 2
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def partition(self, head, x):
"""
:type head: ListNode
:type x: int
:rtype: ListNode
"""
if not head or head.next == None:
return head
r = ListNode(0)
r.next = head
rehead = r
while r.next != None and r.next.val < x: # 当结点小于x时,r往右走直到r.next.val大于x,作为第一个可插入的位置
r = r.next
l = r # l 指向 r,记录插入位置的前置结点
while r.next != None:
while r.next != None and r.next.val >= x: # 当r.next.val大于等于x时,往右走寻找小于x的结点
r = r.next
if r.next !=None:
t = r.next # t保存小于x的结点
r.next = r.next.next # r指向t后面的结点,将t结点提取出来
t.next = l.next # t结点指向l的下一个结点
l.next = t # l结点连接t结点
l = l.next # 更新l
return rehead.next
87. 扰乱字符串
思路
递归:
- 当字符串长度不相等及字符串排序后不相等返回False
- 当字符串相等时返回True
- 递归条件:1.s1的前i个字符与s2的前i个字符相等及s1的后n-i个字符与s2的后n-i个字符相等;2.s1的前i个字符与s2的后i个字符相等及s1的后n-i个字符与s2的前n-i个字符相等
class Solution(object):
def isScramble(self, s1, s2):
"""
:type s1: str
:type s2: str
:rtype: bool
"""
n1, n2 = len(s1), len(s2)
if n1 != n2: # 字符串长度不相等
return False
if sorted(s1) != sorted(s2): # 字符串的字符不相等
return False
if s1 == s2: # 字符串相等
return True
for i in range(1, n1):
if (self.isScramble(s1[:i], s2[:i]) and self.isScramble(s1[i:], s2[i:])) or \
(self.isScramble(s1[:i], s2[-i:]) and self.isScramble(s1[i:], s2[:-i])):
return True
return False
88. 合并两个有序数组
思路
sort(): 对list排序会修改list本身,不会返回新list
sorted(): 返回的排序后的 list
class Solution(object):
def merge(self, nums1, m, nums2, n):
"""
:type nums1: List[int]
:type m: int
:type nums2: List[int]
:type n: int
:rtype: None Do not return anything, modify nums1 in-place instead.
"""
nums1[:] = sorted(nums1[:m] + nums2[:n])
return nums1
89. 格雷编码
思路
格雷码-百度百科
递归生成码表
这种方法基于格雷码是反射码的事实,利用递归的如下规则来构造:
- 1位格雷码有两个码字
- (n+1)位格雷码中的前2n个码字等于n位格雷码的码字,按顺序书写,加前缀0
- (n+1)位格雷码中的后2n个码字等于n位格雷码的码字,按逆序书写,加前缀1
- n+1位格雷码的集合 = n位格雷码集合(顺序)加前缀0 + n位格雷码集合(逆序)加前缀1
1位格雷码 | 2位格雷码 | 3位格雷码 |
---|---|---|
0 | 00 | 000 |
1 | 01 | 001 |
- | 11 | 011 |
- | 10 | 010 |
- | - | 110 |
- | - | 111 |
- | - | 101 |
- | - | 100 |
class Solution(object):
def grayCode(self, n):
"""
:type n: int
:rtype: List[int]
"""
def gray(n):
if n == 0:
return ["0"]
if n == 1:
return ["0", "1"]
ans = list()
for num in gray(n-1):
ans.append("0"+num)
for num in reversed(gray(n-1)):
ans.append("1"+num)
return ans
return list(map(lambda num:int(num, 2), gray(n)))
90. 子集 II
思路
是78. 子集的进阶
- 对nums进行排序
- 去除重复元素
class Solution(object):
def subsetsWithDup(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
nums.sort()
ans = [[]]
for num in nums:
ans += [pre+[num] for pre in ans if pre+[num] not in ans]
return ans
91. 解码方法
思路
- s为空或第一个字符为0,返回0
- 创建dp数组,初始化dp数组第一个数为1
- 遍历s字符串:
- 当s[i]等于0时,如果前一个字符是1或2,说明s[i-1:i+1]只有一种编码,因此dp[i]=dp[i-2]。如果前一个字符不为0或1(如00,30,40…),说明字符串没有合法编码,故返回0
- 当s[i]等于1或2时,与前一个字符在11~26范围内,说明s[i-1:i+1]有两种编码,因此dp[i]=dp[i-1]+dp[i-2]
- 当s[i]在3~9范围内,说明s[i]只有一种编码,因此dp[i] = dp[i-1]
- 返回dp[-1]
class Solution(object):
def numDecodings(self, s):
"""
:type s: str
:rtype: int
"""
if not s or s[0]=="0": # s为空或第一个字符为0,返回0
return 0
n = len(s)
dp = [None]*n # 创建dp数组
dp[0] = 1 # 初始化dp数组第一个数为1
for i in range(1, n):
if s[i] == "0":
if s[i-1] == "1" or s[i-1] == "2":
dp[i] = dp[i-2] if i > 1 else dp[i-1]
else:
return 0
elif (s[i-1] == "1" and int(s[i])<=9) or (s[i-1] == "2" and int(s[i]) <= 6):
dp[i] = dp[i-1] + dp[i-2] if i > 1 else dp[i-1]+1
else:
dp[i] = dp[i-1]
return dp[-1]
92. 反转链表 II
思路
参考25. K 个一组翻转链表
栗子:
head -> 1 -> 2 -> 3 -> 4 -> 5
m, n = 2, 4
##############################
p,pre,tail
-1 -> 1 -> 2 -> 3 -> 4 -> 5
p pre tail
-1 -> 1 -> 2 -> 3 -> 4 -> 5
p pre tail cur
-1 -> 1 -> 3 -> 4 -> 2 -> 5
p pre tail cur
-1 -> 1 -> 4 -> 3 -> 2 -> 5
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def reverseBetween(self, head, m, n):
"""
:type head: ListNode
:type m: int
:type n: int
:rtype: ListNode
"""
p = ListNode(-1)
p.next = head
pre, tail = p, p
tmp = list
while m-1 > 0: # 寻找第m-1结点作为前置结点pre
pre = pre.next
m -= 1
while n > 0: # 寻找第n个结点作为tail
tail = tail.next
n -= 1
while pre.next != tail: # 寻找第n个结点作为tail
cur = pre.next
pre.next = cur.next
cur.next = tail.next
tail.next = cur
return p.next
93. 复原IP地址
思路
回溯:
- 结束条件:创建tmp保持临时结果,当tmp长度为4,s字符串为空时,添加tmp至ans
- 添加有效字符串至ans,有效范围在0-255之间,长度在1到3为之间
class Solution(object):
def restoreIpAddresses(self, s):
"""
:type s: str
:rtype: List[str]
"""
def backtrack(s, tmp=list()):
if len(tmp) == 4: # 当tmp长度等于4,s字符串没有字符时,添加tmp至ans
if not s:
ans.append(".".join(tmp))
return
for cur in range(1, min(4, len(s)+1)): # 因为列表s[:cur]的区间是[),所以cur取值最大为len(s)
if int(s[:cur])<0 or int(s[:cur]) > 255 or (cur > 1 and s[0]=="0"):
break
backtrack(s[cur:],tmp+[s[:cur]])
ans = list()
backtrack(s)
return ans
94. 二叉树的中序遍历
思路
树的遍历顺序根据遍历根的顺序分为先序、中序、后序:
- 先序:根左右
- 中序:左根右
- 后序:左右根
递归
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def inorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
def helper(root, ans=list()):
if root == None:
return []
helper(root.left)
ans.append(root.val)
helper(root.right)
return ans
return helper(root)
def inorder(root):
return [] if root == None else inorder(root.left) + [root.val] + inorder(root.right)
思路
栈实现
class Solution(object):
def inorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
stack, ans = list(), list()
while root or stack:
while root != None:
stack.append(root)
root = root.left
root = stack.pop()
ans.append(root.val)
root = root.right
return ans
95. 不同的二叉搜索树 II
思路
递归:
从1到n的数作为根结点,left(start, i-1)递归构建左子树,(i+1, end)递归构建右子树
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def generateTrees(self, n):
"""
:type n: int
:rtype: List[TreeNode]
"""
def __generateTrees(start, end):
if start > end:
return [None]
ans = list()
for i in range(start, end+1):
left = __generateTrees(start, i-1)
right = __generateTrees(i+1, end)
for l in left:
for r in right:
root = TreeNode(i)
root.left = l
root.right = r
ans.append(root)
return ans
return __generateTrees(1, n) if n else []
96. 不同的二叉搜索树
思路
卡特兰数_百度百科
卡特兰数 C n C_n Cn满足以下递推关系:
- C n + 1 = C 0 C n + C 1 C n − 1 + ⋯ + C n C 0 C_n+1 = C_0C_n + C_1C_{n-1} + \cdots + C_nC0 Cn+1=C0Cn+C1Cn−1+⋯+CnC0
- ( n − 3 ) C n = n 2 ( C 3 C n − 1 + C 4 C n − 2 + C 5 C n − 3 + C n − 2 C 4 + C n − 1 C 4 ) (n - 3)C_n = \frac{n}{2}(C_3C_{n-1} + C_4C_{n-2} + C_5C_{n-3} + C_{n-2}C_4 + C_{n-1}C_4) (n−3)Cn=2n(C3Cn−1+C4Cn−2+C5Cn−3+Cn−2C4+Cn−1C4)
设 h ( n ) h(n) h(n)为catalan数的第 n + 1 n+1 n+1项,令 h ( 0 ) = 1 , h ( 1 ) = 1 h(0)=1,h(1)=1 h(0)=1,h(1)=1,catalan数满足递推式:
h ( n ) = h ( 0 ) × h ( n − 1 ) + h ( 1 ) × h ( n − 2 ) + . . . + h ( n − 1 ) × h ( 0 ) ( n > = 2 ) h(n)= h(0)\times h(n-1)+h(1)\times h(n-2) + ... + h(n-1)\times h(0) (n>=2) h(n)=h(0)×h(n−1)+h(1)×h(n−2)+...+h(n−1)×h(0)(n>=2)
另类递推式:
h ( n ) = h ( n − 1 ) × ( 4 × n − 2 ) / ( n + 1 ) h(n)=h(n-1)\times(4\times n-2)/(n+1) h(n)=h(n−1)×(4×n−2)/(n+1)给定节点组成二叉搜索树
给定 n n n个节点,能构成多少种不同的二叉搜索树?
能构成 h ( n ) h(n) h(n)个,这个公式的下标是从 h ( 0 ) h(0) h(0)=1开始
class Solution(object):
def numTrees(self, n):
"""
:type n: int
:rtype: int
"""
if n == 0:
return 1
if n == 1:
return 1
return self.numTrees(n-1)*(4*n-2)//(n+1)
97. 交错字符串
思路
- 创建dp
- 当s1加s2长度不等于s3长度时,返回False
- 当s1,s2为空,s3为空时,dp[0][0]=True;当s1/s2为空时,s2/s1应与s3完全相等;当s1、s2、s3非空时,求递推公式。s1或s2的交错组成s3的前缀,此时dp[i][j]的上面dp[i-1][j]=True且s1[i-1]=s3[i-1+j](s1第i个字符下标为i-1)或 左边dp[i][j-1]=True且s2[j-1]==s3[j-1+i]
class Solution(object):
def isInterleave(self, s1, s2, s3):
"""
:type s1: str
:type s2: str
:type s3: str
:rtype: bool
"""
n1, n2, n3 = len(s1), len(s2), len(s3)
if n1 + n2 != n3:
return False
dp = [[None]*(n2+1) for _ in range(n1+1)]
dp[0][0] = True
for i in range(1, n1+1):
dp[i][0] = dp[i-1][0] and s1[i-1] == s3[i-1]
for i in range(1, n2+1):
dp[0][i] = dp[0][i-1] and s2[i-1] == s3[i-1]
for i in range(1, n1+1):
for j in range(1, n2+1):
dp[i][j] = (dp[i-1][j] and s1[i-1] == s3[i-1+j]) or (dp[i][j-1] and s2[j-1] == s3[j-1+i])
return dp[-1][-1]
98. 验证二叉搜索树
思路
递归(先序):
- 当左结点大于根结点或右结点小于根结点时返回False
- 递归左子树,原结点在右边,左子树不是二叉搜索树时返回False
- 递归右子树,原结点在左边,右子树不是二叉搜索树时返回False
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def isValidBST(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
def helper(root, left, right):
if root == None:
return True
val = root.val
if val <= left or val >= right:
return False
if not helper(root.left, left, val):
return False
if not helper(root.right, val, right):
return False
return True
return helper(root, float("-inf"), float("inf"))
99. 恢复二叉搜索树
思路
一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数
节点的右子树只包含大于当前节点的数
所有左子树和右子树自身必须也是二叉搜索树
中序遍历:左根右
先遍历到的结点pre比后遍历到的结点root大,找到错误交换的结点x,y
class Solution(object):
def recoverTree(self, root):
"""
:type root: TreeNode
:rtype: None Do not return anything, modify root in-place instead.
"""
x, y, pre = None, None, None
stack = list()
while root or stack:
while root != None:
stack.append(root)
root = root.left
root = stack.pop()
if pre and pre.val > root.val: # 先遍历到的结点比后遍历到的结点大,找到错误交换的结点
y = root
if x == None:
x = pre
else:
break
pre = root
root = root.right
x.val, y.val = y.val, x.val
100. 相同的树
思路
- 遍历结束后都为空结点返回True
- 其中一个结点为空返回False
- 结构相同值不相等返回False
- 左子树与右子树均相等
class Solution(object):
def isSameTree(self, p, q):
"""
:type p: TreeNode
:type q: TreeNode
:rtype: bool
"""
if p == None and q == None:
return True
if p == None or q == None:
return False
if p.val != q.val:
return False
return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)
小milkmilk同小kk~~O(∩_∩)O哈哈~