Python 实现各个基础算法模板
包括题目,思路,模板,应用
一、二分模板
题目:给你一个 m * n
的矩阵 grid
,矩阵中的元素无论是按行还是按列,都以非递增顺序排列。
请你统计并返回 grid
中 负数 的数目。
思路:找到每排的第一个负数,后面就都是负数了,最后累计负数数目
模板:
def binary_search(nums, target):
low = 0
high = len(nums) - 1
while low <= high:
mid = (low + high) // 2
if nums[mid] == target:
return mid
if nums[mid] > target:
high = mid - 1
else:
low = mid + 1
return low
a = [1, 3, 7, 9, 14, 20, 24]
print(binary_search(a, 0)) # 0
print(binary_search(a, 1)) # 0
print(binary_search(a, 2)) # 1
print(binary_search(a, 4)) # 2
print(binary_search(a, 8)) # 3
print(binary_search(a, 11)) # 4
print(binary_search(a, 16)) # 5
print(binary_search(a, 24)) # 6
print(binary_search(a, 29)) # 7
应用:
from typing import List
class Solution:
def countNegatives(self, grid: List[List[int]]) -> int:
res = 0
m = grid.__len__()
n = grid[0].__len__()
for i in range(m):
# 二分找到0的位置
low, high = 0, n - 1
while low < high:
mid = (low + high) // 2
if grid[i][mid] >= 0:
low = mid + 1
else:
high = mid
if grid[i][low] < 0:
res += n - low
return res
s = Solution()
print(s.countNegatives([[4, 3, 2, -1], [3, 2, 1, -1], [1, 1, -1, -2], [-1, -1, -2, -3]]))
# 8
二、递归模板
题目:
在经典汉诺塔问题中,有 3 根柱子及 N 个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子。一开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。移动圆盘时受到以下限制:
(1) 每次只能移动一个盘子;
(2) 盘子只能从柱子顶端滑出移到下一根柱子;
(3) 盘子只能叠在比它大的盘子上。
请编写程序,用栈将所有盘子从第一根柱子移到最后一根柱子。
你需要原地修改栈。
思路:
将N个盘子分为N,N-1两部分,将N-1个盘子看成一个整体,分三步完成移动即可完成
1. N-1部分由A->B
2. N部分由A->C
3. N-1部分由B->C
再把上述N-1部分分为N-1和N-2两部分,继续重复的分三步完成移动
模板:搞清楚递归退出条件是什么,问题怎么分解递归解决的
def fib(n):
# 递归退出条件
if n < 2:
return n
else:
# 分解递归
return fib(n-1) + fib(n-2)
print(fib(10))
应用:
class Solution:
def hanota(self, A: List[int], B: List[int], C: List[int]) -> None:
def hanoi(n,a,b,c):
# 递归退出条件
if n == 1:
c.append(a.pop())
else:
# 分解递归
hanoi(n-1,a,c,b)
c.append(a.pop())
hanoi(n-1,b,a,c)
hanoi(len(A),A,B,C)
三、分治模板
四、贪心模板
五、回溯剪枝模板
题目:
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:1. 所有数字都是正整数。2. 解集不能包含重复的组合。
思路:
主体步骤为先想办法递归遍历所有可能,再通过剪枝加速,筛选并记录正确的解
模板及应用:
class Solution:
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
def back(candidates, cur, target, length):
# 剪枝判断
if len(cur) > length or target < 0:
return
# 筛选目标解判断
if len(cur) == length and target == 0:
# 保存目标解
res.append(cur.copy())
return
# 循环遍历所有可能分支
for i in range(len(candidates)):
# 这里也可剪枝
# 若出现逆序,则剪枝,防止出现重复的情况例如 [1,2] [2,1]是一种
if len(cur) > 0 and candidates[i] < cur[-1]:
continue
# 为进入分支做准备
cur.append(candidates[i])
# 进入分支
back(candidates[:i] + candidates[i + 1:], cur, target - candidates[i], length)
# 恢复进入分支所做的准备, 使每次循环遍历的分支所处的条件一致
cur.pop()
# 算法准备及入口
res = []
nums = [i for i in range(1, 10)]
back(nums, [], n, k)
return res
s = Solution()
print(s.combinationSum3(3, 9))
六、二叉树遍历模板
# 前序优先遍历
def qianxu(p: TreeNode):
res = []
stack = []
while (stack.__len__() != 0 or p):
if p:
res.append(p.val)
stack.append(p)
p = p.left
else:
p = stack.pop(-1)
p = p.right
return res
# 中序优先遍历
def zhongxu(p: TreeNode):
res = []
stack = []
while (stack.__len__() != 0 or p):
if p:
stack.append(p)
p = p.left
else:
p = stack.pop(-1)
res.append(p.val)
p = p.right
return res
# 后序优先遍历
def houxu(p: TreeNode):
res = []
stack = []
d = dict()
while (p or stack.__len__() != 0):
if p:
stack.append(p)
d.update({p.val: 1})
p = p.left
else:
p = stack[-1]
if d[p.val] == 2:
stack.pop(-1)
res.append(p.val)
p = None
else:
d[p.val] = 2
p = p.right
return res
def houxu(self, root: Optional[TreeNode]) -> List[int]:
res = []
stack = []
p = root
# 保存前一个访问的节点
prev = None
while p or stack:
# 找到最左边
while p:
stack.append(p)
p = p.left
node = stack.pop()
if not node.right or node.right == prev:
res.append(node.val)
prev = node
p = None
else:
stack.append(node)
p = node.right
return res
# 层次遍历,用队列
def cengci(p: TreeNode):
res = []
queue = [p]
while (queue.__len__() != 0):
p = queue.pop(0)
res.append(p.val)
if p.left:
queue.append(p.left)
if p.right:
queue.append(p.right)
return res
# 递归
# 先序
def preorder(root):
if not root:
return
print(root.val)
preorder(root.left)
preorder(root.right)
# 中序
def inorder(root):
if not root:
return
inorder(root.left)
print(root.val)
inorder(root.right)
# 后序
def postorder(root):
if not root:
return
postorder(root.left)
postorder(root.right)
print(root.val)
平衡二叉树的二分搜索
def exists(self, idx: int, d: int, node: TreeNode) -> bool:
"""
Last level nodes are enumerated from 0 to 2**d - 1 (left -> right).
Return True if last level node idx exists.
Binary search with O(d) complexity.
"""
left, right = 0, 2**d - 1
for _ in range(d):
pivot = (left + right) // 2
if idx <= pivot:
node = node.left
right = pivot
else:
node = node.right
left = pivot + 1
return node is not None