编程表达式
算法 + 数据结构 = 程序 | Algorithms + Data Structures = Programs | 1976年,瑞士计算机科学家,Algol W、Modula、Oberon 和 Niklaus Emil Wirth提出 |
算法 = 逻辑 + 控制 | Algorithm = Logic + Control | 1979年,英国逻辑学家和计算机科学家 Robert Kowalski 提出 |
程序 = 逻辑 + 控制 + 数据结构 | Program = Logic + Control + Data Structrue |
编程的本质是由逻辑、控制和数据结构构成的,其中逻辑是关键。逻辑是业务逻辑和逻辑过程的抽象,再加上一个由术语表示的数据结构的定义。
graph TD; A[Algorithm] --> B[Logic]; A --> C[Control]; B --> 逻辑过程抽象; B --> 由术语表示的数据结构的定义; C --> 程序流转的方式自顶向下或自底向上; C --> 执行时的策略并行或串行; C --> 调度不同的执行路径或模块; C --> 数据间的存储关系;
混乱不堪的代码,通常是由于 Logic 和 Control 纠缠在一起导致的。不仅难以维护,而且 Bug 很多。
代码的复杂度取决于业务逻辑的复杂度和控制逻辑的复杂度,如果业务逻辑和控制逻辑耦合,那么程序就会很复杂,需花费大量的时间去维护和调试代码。
例子
我:
举例说明:程序=逻辑+控制+数据结构,比如树的层级遍历的递归和非递归实现。
ChatGPT:
树的层级遍历是一个经典的问题,能够很好地展示“程序 = 逻辑 + 控制 + 数据结构”的概念。接下来我将通过树的层级遍历的递归和非递归实现,来详细解释这个公式。
在树的层级遍历中,我们需要逐层访问树的节点,首先访问根节点,然后访问根节点的子节点,再访问子节点的子节点,依此类推。
1. 递归实现树的层级遍历
递归方法本质上通过分而治之的思想,将树的层级遍历问题分解为多个子问题。
# 假设有一个TreeNode类定义如下:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def level_order_recursive(root):
levels = []
if not root:
return levels
def traverse(node, level):
if len(levels) == level:
levels.append([])
levels[level].append(node.val)
if node.left:
traverse(node.left, level + 1)
if node.right:
traverse(node.right, level + 1)
traverse(root, 0)
return levels
程序 = 逻辑 + 控制 + 数据结构
- 逻辑:
- 递归的方法遵循了树的层级遍历的本质:逐层访问节点,依次从左到右处理每一层的节点。
- 控制:
- 递归控制树的遍历,
traverse
函数不断调用自身来处理每一层的节点,且递归深度等于树的高度。 - 控制流程通过
if
判断决定如何深入到子节点并在合适时刻停止递归。
- 递归控制树的遍历,
- 数据结构:
levels
是一个二维列表,每一层的数据存储在不同的子列表中。- 树的每一个节点通过
TreeNode
对象表示,包含val
,left
,right
属性。 - 函数调用栈也隐式地扮演了数据结构的角色,递归调用的层次决定了节点的层级关系。
2. 非递归实现树的层级遍历
非递归方法通常使用队列来实现层级遍历。这种方法依赖于显式的数据结构(队列)来保存当前层级的节点。
from collections import deque
def level_order_iterative(root):
levels = []
if not root:
return levels
queue = deque([root])
while queue:
level_size = len(queue)
level = []
for _ in range(level_size):
node = queue.popleft()
level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
levels.append(level)
return levels
程序 = 逻辑 + 控制 + 数据结构
- 逻辑:
- 和递归一样,非递归方法的逻辑也是依照层级遍历的定义:逐层访问树的节点。
- 每次从队列中取出一层的节点,然后处理它们的左右子节点,将它们加入下一层的队列中。
- 控制:
- 控制结构通过
while
循环来遍历树的每一层,并通过for
循环处理每层的节点。 - 控制的核心是维护一个队列,队列保证了节点按层级顺序处理。
queue.popleft()
提取当前节点,queue.append()
将子节点按顺序加入队列。
- 控制结构通过
- 数据结构:
- 使用了一个队列 (
deque
),来保存当前层级的节点,并逐层处理。 levels
列表存储了每一层的结果,队列保证了层级的顺序。- 树的节点仍然是
TreeNode
对象,保存节点的值以及它的左右子节点。
- 使用了一个队列 (
对比总结
- 逻辑:
- 递归和非递归方法的逻辑都是基于层级遍历的定义,依次访问每一层的节点。
- 控制:
- 递归方法通过函数调用栈隐式地控制遍历,调用栈深度等于树的高度。
- 非递归方法使用
while
和for
循环,配合队列显式控制遍历的顺序。
- 数据结构:
- 递归方法隐式依赖于函数调用栈,以及
levels
列表来保存结果。 - 非递归方法使用
deque
队列来保存当前层的节点,并按层次进行遍历。
- 递归方法隐式依赖于函数调用栈,以及
这两种实现都展示了程序中逻辑、控制、数据结构的不同组合,以及如何根据具体问题选择适合的实现方式。
文档信息
- 本文作者:Wang Jiang
- 本文链接:https://wjrye.github.io/fragment/Programs/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)