一、二叉树
前序遍历、中序遍历、后序遍历:
1.前序遍历:
def preorderTraversal(self, root: TreeNode) -> List[int]:
if root == None:
return []
# 分别记录遍历结果,存储栈
result, istack = [], []
while root != None or len(istack) != 0:
# 嵌套循环
while root != None:
# 前序遍历,压入栈前先输出
result.append(root.val)
istack.append(root)
root=root.left
# 栈弹出
root = istack[-1]
istack = istack[:-1]
root = root.right
return result
2.中序遍历:
def inorderTraversal(self, root: TreeNode) -> List[int]:
if root == None:
return []
# 分别记录遍历结果,存储栈
result, istack = [], []
while root != None or len(istack) != 0:
while root != None:
istack.append(root)
root = root.left
# 中序遍历,等左子树压入栈完毕,再从栈输出
cur = istack[-1]
result.append(cur.val)
istack = istack[:-1]
root = cur.right
return result
3.后序遍历:
def postorderTraversal(self, root: TreeNode) -> List[int]:
if root == None:
return []
# 分别记录遍历结果,存储栈,后序遍历必要的前一个输出节点记录
result, istack, lastnode = [], [], None
while root != None or len(istack) != 0:
while root != None:
istack.append(root)
root = root.left
# 左子树压入栈后,判断当前节点是否可输出
cur = istack[-1]
# 如果当前节点无右子树,或者右子树已经输出,则该点可输出
if cur.right == None or cur.right == lastnode:
istack = istack[:-1]
result.append(cur.val)
lastnode = cur
# 否则继续将该点右子树入栈,再继续循环
else:
root = cur.right
return result
深度搜索DFS,层次搜索BFS:
1.层次搜索/遍历
def levelOrder(self, root: TreeNode) -> List[List[int]]:
if root == None:
return []
# 层序遍历,需要借助队列记录
result, iqueue = [], []
# 队列需要先入队
iqueue.append(root)
while len(iqueue) != 0:
# 遍历当前层之前,先记录该层的个数,以便队列边记录边遍历
l, temp = len(iqueue), []
for i in range(0, l):
# 遍历时,依次取出和输出队中的点,并将子树入队
cur = iqueue[0]
iqueue = iqueue[1:]
temp.append(cur.val)
if cur.left != None:
iqueue.append(cur.left)
if cur.right != None:
iqueue.append(cur.right)
result.append(temp)
return result
分治法:递归返回条件+分段处理+合并结果(快速排序、归并排序、二叉树)
1.简单分治法:用于计算二叉树的最大深度
def maxDepth(self, root: TreeNode) -> int:
if root == None:
return 0
left = self.maxDepth(root.left)
right = self.maxDepth(root.right)
return left + 1 if left > right else right + 1
2.判断是否为平衡二叉树:
def isBalanced(self, root: TreeNode) -> bool:
if self.judge(root) == -1:
return False
else:
return True
def judge(self, root: TreeNode) -> int:
if root == None:
return 0
left = self.judge(root.left)
right = self.judge(root.right)
# 借助层数来记录是否平衡,如果为-1,则直接返回不平衡
if left == -1 or right == -1 or left - right > 1 or right - left > 1:
return -1
else:
return left + 1 if left > right else right + 1
3.找出二叉树中最大路径和【hard】
def maxPathSum(self, root: TreeNode) -> int:
# 最大和:左/右子树最大和,左右子树加根节点的最大和
summax = self.find(root)
return summax[1]
def find(self, root: TreeNode) -> List[int]:
if root == None:
return [0, float('-inf')]
# 分治
left = self.find(root.left)
right = self.find(root.right)
result = []
# 合并规整,先求单边值,再求最大和
if left[0] > right[0]:
result.append(max(left[0] + root.val, 0))
else:
result.append(max(right[0] + root.val, 0))
result.append(max(max(left[1], right[1]), (left[0] + right[0] + root.val)))
return result
4.找出二叉树的公共祖先
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if root == None:
return root
if root == p or root == q:
return root
# 分治
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
# 归并整合,返回为空则未找到,返回不为空则找到
if left != None and right != None:
return root
if left != None:
return left
if right != None:
return right
return None
BFS层次遍历:
1.锯齿形层次遍历:
def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
if root == None:
return []
# 层次遍历需要借助队列顺序,锯齿形需要逆转标记
result, iqueue, iturn = [], [], True
iqueue.append(root)
while len(iqueue) > 0:
temp, l = [], len(iqueue)
for i in range(0, l):
cur = iqueue[0]
iqueue = iqueue[1:]
if iturn:
temp.append(cur.val)
else:
temp.insert(0, cur.val)
if cur.left != None:
iqueue.append(cur.left)
if cur.right != None:
iqueue.append(cur.right)
if iturn:
iturn = False
else:
iturn = True
result.append(temp)
return result
2.判定是否为有效二叉搜索树【medium】
def isValidBST(self, root: TreeNode) -> bool:
res = self.helper(root)
if res[0] == 0:
return False
else:
return True
def helper(self, root: TreeNode) -> List[int]:
# 结果记录,分别为0是否有效,1树的最大值,2树的最小值
if root == None:
return [1, float('-inf'), float('-inf')]
# 分治
left = self.helper(root.left)
right = self.helper(root.right)
# 整合:先判定无效情况
if left[0] == 0 or right[0] == 0:
return [0, float('-inf'), float('-inf')]
if left[1] != float('-inf') and left[1] >= root.val:
return [0, float('-inf'), float('-inf')]
if right[2] != float('-inf') and right[2] <= root.val:
return [0, float('-inf'), float('-inf')]
# 节点为有效时,节点最大值为右子树的最大值,最小值为左子树的最小值,否则最大最小值为本身
result = [1, root.val, root.val]
if right[1] != float('-inf'):
result[1] = right[1]
if left[2] != float('-inf'):
result[2] = left[2]
return result
3.二叉搜索树中插值:
def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:
if root == None:
root = TreeNode()
root.val = val
return root
if root.val == val:
return root
elif val < root.val:
root.left = self.insertIntoBST(root.left, val)
else:
root.right = self.insertIntoBST(root.right, val)
return root
二、链表
链表基本操作:
1.删除全部的重复元素
def deleteDuplicates(self, head: ListNode) -> ListNode:
pre = ListNode()
pre.next = head
head, last = pre, float('inf')
# 删除重复的额外点,只需要一个指针滑动,判断当前点和下一点(如有);
# 而删除全部重复点,则需要空指针领头,进行领头后续两个节点的判断(无两个节点则无需判断)。
while head.next != None and head.next.next != None:
if head.next.val == head.next.next.val:
last = head.next.val
while head.next != None and head.next.val == last:
head.next = head.next.next
else:
head = head.next
return pre.next
2.反转链表
def reverseList(self, head: ListNode) -> ListNode:
pre = ListNode()
pre.next = head
while head.next != None:
# 记录下一个节点
last = head.next
# 将当前节点next指向下一个节点的next
head.next = last.next
# 下一个节点next指向空指针next
last.next = pre.next
# 将空指针移动至最左
pre.next = last
return pre.next
3.将指定段落的链表反转【medium】
def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
if head == None or m == n:
return head
# 先进行小节点检索
point = ListNode()
point.next = head
head = point
count = 1
while count < m:
head = head.next
count += 1
pre, mid = head, head.next
head = head.next
last = head
# 用head作为原始遍历顺序指针,last记录移动的前一个结点
while count <= n and head != None:
# 将head指针的下一个记录,将当前节点的指针指向上一个节点
temp = head.next
head.next = last
# 移动上一个节点,并将head指针放在原来的下一个
last = head
head = temp
count += 1
pre.next = last
mid.next = head
return point.next
4.拼接两个有序链表
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
# 将合并后的表新起一个空指针作为起点(表头)
result = ListNode()
point = result
while l1 != None and l2 != None:
if l1.val <= l2.val:
point.next = l1
l1 = l1.next
else:
point.next = l2
l2 = l2.next
point = point.next
if l1 != None:
point.next = l1
if l2 != None:
point.next = l2
return result.next
链表分隔与排序:
1.将链表根据值分隔为左小右大
def partition(self, head: ListNode, x: int) -> ListNode:
# 需要用空指针作为原始起点的记录,用额外的表记录比x大的节点,再将结果并至指针末尾
result, pre = ListNode(), ListNode()
pre.next = head
bigger, head = result, pre
while head.next != None:
if head.next.val < x:
head = head.next
else:
# 将大值节点记录,移动指针,同时将大值节点链入额外记录链表中
node = head.next
head.next = node.next
bigger.next = node
bigger = bigger.next
bigger.next = None
head.next = result.next
return pre.next
2.归并排序【medium】
def sortList(self, head: ListNode) -> ListNode:
return self.mergeSort(head)
def findMiddle(self, head: ListNode) -> ListNode:
# 用快慢指针来寻找中间节点
slow = head
fast = head.next
while fast != None and fast.next != None:
fast = fast.next.next
slow = slow.next
return slow
# 拼接两个有序链表
def mergeTwo(self, l1: ListNode, l2: ListNode) -> ListNode:
pre = ListNode()
point = pre
while l1 != None and l2 != None:
if l1.val < l2.val:
point.next = l1
l1 = l1.next
else:
point.next = l2
l2 = l2.next
point = point.next
if l1 != None:
point.next = l1
if l2 != None:
point.next = l2
return pre.next
def mergeSort(self, head: ListNode) -> ListNode:
# 分治法要点1:递归返回确定,这里对至少2个节点排序,故而要有两个判断条件
if head == None or head.next == None:
return head
mid = self.findMiddle(head)
last = mid.next
mid.next = None
# 分治法要点2:分两部分递归,利用快慢指针找到中间节点
left = self.mergeSort(head)
right = self.mergeSort(last)
# 分治法要点3:归并整合两部分,用合并有序链表的方法
return self.mergeTwo(left, right)
3.将链表按照1, n, 2, n-1, 3, n-2, ..., 的顺序排列
def reorderList(self, head: ListNode) -> None:
# 思路:从中间破开链表,反转后链,再合并
if head == None or head.next == None:
return head
mid = self.findMiddle(head)
last = self.reverseList(mid.next)
mid.next = None
head = self.mergeTwo(head, last)
def findMiddle(self, head: ListNode) -> ListNode:
slow, fast = head, head.next
while fast != None and fast.next != None:
fast = fast.next.next
slow = slow.next
return slow
def reverseList(self, head: ListNode) -> ListNode:
pre = ListNode()
pre.next = head
while head.next != None:
last = head.next
head.next = last.next
last.next = pre.next
pre.next = last
return pre.next
def mergeTwo(self, left: ListNode, right: ListNode) -> ListNode:
pre, isleft = ListNode(), True
point = pre
while left != None and right != None:
if isleft:
point.next = left
left = left.next
isleft = False
else:
point.next = right
right = right.next
isleft = True
point = point.next
if left != None:
point.next = left
if right != None:
point.next = right
return pre.next
环状链表:
1.判断链表是否有环
def hasCycle(self, head: ListNode) -> bool:
if head == None:
return False
# 快指针用head表示,相交时当前点即为交汇点;
# 如用head.next表示,则相交点为下一个点
slow, fast = head, head.next
# 仅需判断快指针
while fast != None and fast.next != None:
if slow == fast:
return True
slow = slow.next
fast = fast.next.next
return False
2.找出带环链表的入环点
def detectCycle(self, head: ListNode) -> ListNode:
if head == None:
return head
# 相交点时,快慢指针分别走过a+b,a+n(b+c),c表示环剩下的长度;
# 得2(a+b)=a+n(b+c),即a=c+(n-1)(b+c),表示a的长度为n-1圈的环+c;
# 而慢指针继续走c即可回到入环点,再经过n-1圈仍然在入环点;
slow, fast = head, head.next
while fast != None and fast.next != None:
if fast == slow:
fast = head
# 因为一开始fast=head.next,此时slow.next才是真当前点
slow = slow.next
while fast != slow:
fast = fast.next
slow = slow.next
return slow
fast = fast.next.next
slow = slow.next
return None
回文链表:
1.判断回文链表
def isPalindrome(self, head: ListNode) -> bool:
if head == None or head.next == None:
return True
# 思路:与前面置换插入排序类似,采用中点截断、反转后链、再比较的做法。
slow, fast = head, head.next
while fast != None and fast.next != None:
slow = slow.next
fast = fast.next.next
last = self.reverseList(slow.next)
slow.next = None
while last != None and head != None:
if last.val != head.val:
return False
last, head = last.next, head.next
return True
def reverseList(self, head: ListNode) -> ListNode:
if head == None:
return head
pre = ListNode()
# 切记,在反转链表时,空指针不需要head辅助
pre.next = head
while head.next != None:
node = head.next
head.next = node.next
node.next = pre.next
pre.next = node
return pre.next
2.深拷贝链表【medium】
def copyRandomList(self, head: 'Node') -> 'Node':
if head == None:
return head
point = head
while point != None:
node = Node(point.val, point.next)
point.next = node
point = node.next
point = head
# 很精妙,采用判断前一个节点为非空,来操作空节点
while point != None:
if point.random != None:
point.next.random = point.random.next
point = point.next.next
# 原做法
# node = point.random
# if node != None:
# point.random = node.next
# if point.next != None:
# point = point.next.next
# else:
# point = point.next
point = head.next
while head != None and head.next != None:
# 很精妙,采用指针穿线的方法
node = head.next
head.next = head.next.next
head = node
return point
三、栈和队列
栈
1.最小栈
class MinStack:
def __init__(self,):
self.istack = []
self.minstack = []
# 采用辅助栈做法,记录每个位置的最小值
def push(self, x: int) -> None:
self.istack.append(x)
if len(self.minstack) != 0 and self.minstack[-1] < x:
temp = self.minstack[-1]
self.minstack.append(temp)
else:
self.minstack.append(x)
def pop(self) -> None:
if len(self.istack) == 0:
return
self.istack = self.istack[:-1]
self.minstack = self.minstack[:-1]
def top(self) -> int:
if len(self.istack) == 0:
return 0
return self.istack[-1]
def getMin(self) -> int:
if len(self.istack) == 0:
return float('inf')
return self.minstack[-1]
2.利用栈进行逆波兰表达式求解
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
if len(tokens) == 0:
return 0
istack = []
for i in range(0, len(tokens)):
# 关键:会有二位数,因此先判断是否符号,并且是符号还要判断是否有已有两个值在栈中
if tokens[i] not in ["+", "-", "*", "/"]:
istack.append(int(tokens[i]))
elif len(istack) < 2:
break
else:
a, b = istack[-2], istack[-1]
istack = istack[:-2]
if tokens[i] == "+":
istack.append(a + b)
elif tokens[i] == "-":
istack.append(a - b)
elif tokens[i] == "*":
istack.append(a * b)
elif tokens[i] == "/":
istack.append(int(a / b))
else:
pass
if len(istack) == 1:
return istack[0]
else:
return 0
3.字符串解码
def decodeString(self, s: str) -> str:
istack = []
for i in range(0, len(s)):
if s[i] == ']':
# 找字符串
temp = []
while len(istack) > 0 and istack[-1] != '[':
temp.insert(0, istack[-1])
istack = istack[:-1]
istack = istack[:-1]
# 找数字
number = 1
ilen = len(istack)
while ilen >= number and istack[ilen - number] in ['0','1','2','3','4','5','6','7','8','9']:
number += 1
# 数字范围:[len - i + 1, len - 1]
res = int(''.join(istack[ilen - number + 1:]))
istack = istack[:ilen - number + 1]
# 复制
for i in range(0, res):
istack.extend(temp)
else:
istack.append(s[i])
return ''.join(istack)
4.克隆图
def cloneGraph(self, node: 'Node') -> 'Node':
# 关键点:用visited记录新建的节点,以连接
visited = {}
return self.cloneg(node, visited)
def cloneg(self, node: 'Node', visited: dict) -> 'Node':
if node == None:
return node
# visited中能找到新建节点,则直接返回节点
if visited.get(node) != None:
return visited[node]
newnode = Node(node.val, [])
visited[node] = newnode
for n in range(0, len(node.neighbors)):
newnode.neighbors.append(self.cloneg(node.neighbors[n], visited))
return newnode
5.小岛数量【medium】
def numIslands(self, grid: List[List[str]]) -> int:
count = 0
# 按数组顺序扫描
for i in range(0, len(grid)):
for j in range(0, len(grid[0])):
if grid[i][j] == "1" and self.dfs(grid, i, j) >= 1:
count += 1
return count
def dfs(self, grid: List[List[str]], i: int, j: int) -> int:
if i < 0 or j < 0 or i >= len(grid) or j >= len(grid[0]):
return 0
if grid[i][j] == "1":
grid[i][j] = "0"
# 将grid列表传入函数与(对象一样)属于引用传递,访问后改值相当于列表自带访问记录功能
return self.dfs(grid, i-1, j) + self.dfs(grid, i, j-1) +\
self.dfs(grid, i+1, j) + self.dfs(grid, i, j+1) + 1
return 0
6.求条状图中的最大矩形面积【单调栈hard】
def largestRectangleArea(self, heights: List[int]) -> int:
# 1.采用宽度法,选取左右点固定区间,直接取高度,O(n2)
# 2.采用高度法,对于每根柱子求取最大宽度
# 固定高度,求宽度,即找左右侧:
# 左侧比当前高度低的第一根柱子;右侧比当前高度低的第一根柱子
res, istack = 0, []
# 在矩形中加入左右哨兵以求宽度
heights = [0] + heights + [0]
for i in range(len(heights)):
# 每次入栈,必须保证矩形高度为递增的
while istack and heights[istack[-1]] > heights[i]:
last= istack.pop()
res = max(res, heights[last]*(i - 1 - istack[-1]))
istack.append(i)
return res
队列
1.用栈实现队列
class MyQueue:
def __init__(self):
self.prestack = []
self.poststack = []
def push(self, x: int) -> None:
while len(self.poststack) != 0:
self.prestack.append(self.poststack.pop())
self.prestack.append(x)
def pop(self) -> int:
while self.prestack:
self.poststack.append(self.prestack.pop())
if self.poststack:
return self.poststack.pop()
return -1
def peek(self) -> int:
while self.prestack:
self.poststack.append(self.prestack.pop())
if self.poststack:
return self.poststack[-1]
return -1
def empty(self) -> bool:
if not self.prestack and not self.poststack:
return True
return False
2.二叉树层次遍历
3.0-1矩阵,找非0位置到0的距离【medium】
def updateMatrix(self, matrix: List[List[int]]) -> List[List[int]]:
# 根据广度优先搜索,从外部向内包围
if not matrix:
return matrix
row, col = len(matrix), len(matrix[0])
iqueue = []
for i in range(row):
for j in range(col):
# 从外包围,先将外围入队
if matrix[i][j] == 0:
iqueue.append([i,j])
# 直接用值(-1)区分需要修改的点
else:
matrix[i][j] = -1
while len(iqueue) != 0:
point_i, point_j = iqueue[0][0], iqueue[0][1]
iqueue = iqueue[1:]
newpoint = [[point_i-1, point_j], [point_i+1, point_j], [point_i, point_j-1], [point_i, point_j+1]]
for n in newpoint:
ii, jj = n[0], n[1]
# 修改过的点为下一层入队点
if ii >= 0 and ii < row and jj >= 0 and jj < col and matrix[ii][jj] == -1:
matrix[ii][jj] = matrix[point_i][point_j] + 1
iqueue.append([ii, jj])
return matrix
四、二进制(位运算)【https://blog.csdn.net/holmofy/article/details/79360859】
6个位运算符【& | ^ ~ << >>】
-
&(0清零,1保留):取指定位;判断奇偶 (a&1)==0或a&(-a)==0;
-
|(有1则1,0保留):改值(用1)
-
^(同0异1,满足结合律、自反性):反转指定位;
-
~
从数组中找出只出现1次的数字:
1.2次中找1次:【异或】
def singleNumber(self, nums: List[int]) -> int:
res = 0
for n in nums:
res = res ^ n
return res
2.3次中找1次:【异或、与、非的组合】
def singleNumber(self, nums: List[int]) -> int:
once, twice = 0, 0
# n,once,twice
# 0 0 0
# 0 1 0
# 0 0 1
# 1 0 0
# 1 1 0
# 1 0 1
# 对于n=1而言,3个1最终仍使once=0,实现3个数的位运算保证
for n in nums:
once = once ^ n & ~twice
twice = twice ^ n & ~once
return once
3.2次中找2个1次:
def singleNumber(self, nums: List[int]) -> List[int]:
# 1.求x和y的异或值。
# 2.求这个异或值最右位的1
# 3.找出x和y中是谁造成了这个最右位的1,以分离x和y
res = 0
for n in nums:
res ^= n
# res = x ^ y
# 取最右一位的1:res&(~res + 1)、res&(-res)、res&(res-1)^res
lastbit = (res&(res-1))^res
x = 0
for n in nums:
if n & lastbit != 0:
x ^= n
return [x, res^x]
计算二进制:
1.计算二进制中1的个数:
def hammingWeight(self, n: int) -> int:
count = 0
while n != 0:
# 获取最后一位判断
if n & (-n) == 1:
count += 1
n = n >> 1
return count
2.反转32位无符号整数二进制
def reverseBits(self, n: int) -> int:
res = 0
for i in range(32):
res = res << 1
# 获取最后一位判断
if n & 1 == 1:
res += 1
n = n >> 1
return res
3.求区间数二进制的与值
def rangeBitwiseAnd(self, m: int, n: int) -> int:
# 因为相邻的两个数必有1~2位不相同的位置,可以找规律:
# 100100
# 100101
# 100110
# 100111
# 101000
# 因为m与n的相同前缀后分别为0,1(SSS0XXX,SSS1XXX)
# 通过m与n的不断舍取得到相同前缀即可
count = 0
while m != n:
m = m >> 1
n = n >> 1
count += 1
# 得到相同前缀,将末尾0补足
return m << count
这篇博客详细探讨了LeetCode中的数据结构问题,涵盖了链表、栈和队列的算法实现,以及二进制位运算的应用。在链表部分,讲解了反转链表、合并链表等操作;栈和队列中讨论了如何利用它们解决逆波兰表达式和层次遍历等问题;此外,还深入介绍了二进制位运算的技巧,如取指定位、反转和计算二进制中1的个数等。

4986

被折叠的 条评论
为什么被折叠?



