【小白笔记】Python 矩阵操作与控制流
·
💻 Python 矩阵操作与控制流(初学者角度)
1. 矩阵(嵌套列表)的读取与维度确定
在 Python 中,面试题中的 M×NM \times NM×N 矩阵通常用嵌套列表(List of Lists)来表示。
| 概念 | Python 实现 | 解释与算法思想 |
|---|---|---|
| 矩阵的数据结构 | matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] |
这是一个列表,列表的每个元素又是一个列表(代表一行)。 |
| 确定行数 MMM | M = len(matrix) |
算法思想: 行数 MMM 就是最外层列表的长度。这里使用了 Python 的内置函数 len()。 |
| 确定列数 NNN | N = len(matrix[0]) |
算法思想: 列数 NNN 就是任一行(通常取第一行 matrix[0])的长度。这里使用了索引操作 matrix[0] 访问第一行,再用 len() 求其长度。 |
| 读取坐标 (i,j)(i, j)(i,j) | value = matrix[i][j] |
算法思想: 这是一个连续的索引操作。第一个索引 [i] 找到第 iii 行的列表,第二个索引 [j] 找到该行列表的第 jjj 个元素。 |
写法分析:
m = len(matrix):这是正确的,得到了行数 MMM。matrix[:, 0]:这个写法是 NumPy 库或 Pandas 库中的切片操作,在纯 Python 的嵌套列表中是不生效的。在纯 Python 中,您必须用len(matrix[0])来求列数 NNN。
2. 核心算法逻辑:绝对值列差的计算
您提到不知道如何表示**“减去那个 abs\text{abs}abs 列叉”。这涉及到简单的数学运算和内置函数**。
| 概念 | Python 实现 | 解释与算法思想 |
|---|---|---|
| 计算绝对值 | abs(x) |
内置函数 abs(): 用于求任何数字(整数或浮点数)的绝对值。这是最常用的数学函数之一。 |
| 计算列差惩罚 | penalty = abs(j - k) |
控制流/算法思想: 在动态规划的迭代中,假设: * j 是当前行选取的列坐标。 * k 是上一行选取的列坐标。 |
| 完整减法 | new_score = old_dp_score - abs(j - k) |
数学运算: old_dp_score 是从上一行 kkk 位置转移过来的最大累积得分。我们将惩罚项直接从这个累积得分中减去。 |
🧠 栈求和
如果一道算法题确实需要使用栈(Stack)来求和,那么它通常不会是动态规划(DP)求最优值的路径问题。
需要用到栈求和的典型场景是涉及深度优先搜索 (DFS)、回溯 (Backtracking)、或者表达式求值、数据结构自身特性的题目。
我将以一个经典的栈应用场景——求路径和为例,来讲解正确的栈求和思路和 Python 实现。
🧭 正确的栈求和场景:深度优先搜索 (DFS)
1. 经典应用场景:二叉树中的路径总和
假设题目是:
给定一棵二叉树和一个目标和 SSS。判断是否存在一条从根节点到叶节点的路径,路径上所有节点值的总和等于 SSS。
在这个场景下,栈(在 Python 中通常用列表模拟)可以用于存储当前正在探索的路径上的节点值,并进行求和。
2. Python 实现与算法思想
在这个 DFS 场景中,栈的作用是记录状态,而回溯(即出栈)是关键的控制流。
| 概念 | Python 实现 | 解释与算法思想 |
|---|---|---|
| 数据结构(栈) | path_stack = [] |
用 Python 列表模拟栈,append() 是入栈,pop() 是出栈。 |
| 算法思想(DFS) | 递归或显式栈迭代 | 栈用于存储(节点, 当前累积和)或(节点)并在函数外部维护和。 |
| 栈求和(回溯) | 入栈 (Push): 访问新节点时,将节点值加入栈中。 求和: 在判断是否是目标路径时,对栈求 sum(path_stack)。 出栈 (Pop): 离开该节点(回溯)时,将节点值从栈中移除,确保栈只包含当前路径。 |
|
| 控制流 | 递归调用或 while 循环 |
递归是隐式的栈;while 循环配合 stack.pop() 是显式的栈操作。 |
3. 代码示例 (DFS 结合栈求和)
这个例子展示了如何在 DFS 过程中维护和利用栈来计算路径和:
# 假设 Node 有 .val, .left, .right 属性
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def has_path_sum_dfs(root: TreeNode, target_sum: int) -> bool:
if not root:
return False
# 使用列表模拟栈,存储当前路径上的节点值
path_stack = []
def dfs_helper(node):
# 算法思想:入栈(Stack Push)
path_stack.append(node.val)
# 1. 检查是否为叶子节点
if not node.left and not node.right:
# 算法思想:栈求和
# 这是一个关键的条件分支:只有在叶节点才对当前路径(栈)求和
current_sum = sum(path_stack)
if current_sum == target_sum:
# 找到目标路径,返回 True
return True
# 2. 递归访问子节点(控制流:条件分支和递归调用)
found = False
if node.left:
if dfs_helper(node.left):
found = True
if not found and node.right:
if dfs_helper(node.right):
found = True
# 算法思想:回溯 / 出栈 (Stack Pop)
# 离开当前节点前,将其从路径中移除,这是栈求和正确的关键
path_stack.pop()
return found
return dfs_helper(root)
# 注意:在实际的树问题中,为了避免重复求和,更高效的做法是在栈中存储(节点, 累计和)
# 避免在每次到叶节点时都对整个栈求和 O(N^2)
总结:栈 vs. DP 的使用区别
| 特性 | 栈求和 (DFS/回溯) | 动态规划 (DP) |
|---|---|---|
| 目标 | 找到一条满足条件的路径/状态(是或否),或记录特定路径。 | 找到所有路径中的最优值(最大或最小)。 |
| 存储内容 | 存储路径上的元素/操作。 | 存储到当前状态的最优累积值。 |
| 求和方式 | 每次探索完一条路径后,对栈(路径)内容进行一次性求和。 | 在状态转移时(递推),不断累加历史最优值。 |
| 适用场景 | 树/图的遍历、括号匹配、表达式求值、回溯搜索。 | 最优子结构和重叠子问题的阶段性决策问题。 |
更多推荐
所有评论(0)