引言

前几天在刷华为OD的螺旋矩阵题时,我和我的“AI老师”发生了一场有趣的讨论。这道题本身并不复杂——给定一个m×n的矩阵,按顺时针螺旋顺序返回所有元素。但在实现过程中,我们发现同一个问题可以用两种截然不同的思维模型来解决。

更令人惊讶的是,这两种模型不仅在刷题中有意义,它们竟然对应着真实世界中两种完全不同的机器人控制哲学。

问题本身:螺旋矩阵遍历

题目很简单:给定一个3×3矩阵,按顺时针螺旋顺序输出所有元素。

输入:[[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]

这个问题有两种经典的解法,我们分别称之为“机器人自走式”和“边界收缩式”。

解法一:机器人自走式

这种解法的思维模型是:我是一个机器人,站在矩阵的左上角,沿着墙壁行走。我关心的是“我现在在哪”和“我下一步该往哪走”。

def spiral_order_robot(matrix):
    if not matrix or not matrix[0]:
        return []
    
    m, n = len(matrix), len(matrix[0])
    top, bottom = 0, m - 1
    left, right = 0, n - 1
    result = []
    row, col = 0, 0  # 机器人的当前位置
    
    while top <= bottom and left <= right:
        # 向右走
        for col in range(left, right + 1):
            result.append(matrix[top][col])
        top += 1  # 走过上墙,上边界下移
        
        # 向下走
        for row in range(top, bottom + 1):
            result.append(matrix[row][right])
        right -= 1  # 走过右墙,右边界左移
        
        if top > bottom or left > right:
            break
        
        # 向左走
        for col in range(right, left - 1, -1):
            result.append(matrix[bottom][col])
        bottom -= 1  # 走过下墙,下边界上移
        
        # 向上走
        for row in range(bottom, top - 1, -1):
            result.append(matrix[row][left])
        left += 1  # 走过左墙,左边界右移
    
    return result

核心特征:代码中定义了 rowcol 变量,虽然它们在 for 循环中被临时变量覆盖,但思维上我们始终在跟踪“机器人当前的位置”。每一步的起点依赖于上一步的终点。

解法二:边界收缩式

这种解法的思维模型是:我是一个监工,站在房间外面指挥。我不关心工人走到哪了,只关心“还有哪些墙没擦”和“每面墙的范围是多少”。

def spiral_order_boundary(matrix):
    if not matrix or not matrix[0]:
        return []
    
    m, n = len(matrix), len(matrix[0])
    top, bottom = 0, m - 1
    left, right = 0, n - 1
    result = []
    
    while top <= bottom and left <= right:
        # 遍历上边:从左到右
        for col in range(left, right + 1):
            result.append(matrix[top][col])
        top += 1
        
        # 遍历右边:从上到下
        for row in range(top, bottom + 1):
            result.append(matrix[row][right])
        right -= 1
        
        if top > bottom or left > right:
            break
        
        # 遍历下边:从右到左
        for col in range(right, left - 1, -1):
            result.append(matrix[bottom][col])
        bottom -= 1
        
        # 遍历左边:从下到上
        for row in range(bottom, top - 1, -1):
            result.append(matrix[row][left])
        left += 1
    
    return result

核心特征:没有全局位置变量。每次遍历使用 for 循环的临时变量,遍历完就丢弃。只关心边界变量(top, bottom, left, right)的变化。

两种写法的本质区别

从代码上看,两种写法几乎一模一样,唯一的区别是机器人自走式多定义了一对 row, col 变量(虽然在 for 循环中并未真正使用)。但背后的思维模型截然不同:

维度

机器人自走式

边界收缩式

核心隐喻

清洁工沿着墙边走

监工指挥擦墙

关注点

“我走到哪了?”

“范围还剩多少?”

核心变量

位置变量 + 边界变量

只有边界变量

遍历方式

位置驱动,边界辅助

边界驱动,临时变量辅助

变量关系

位置依赖边界,边界也依赖位置

边界独立,临时变量用完即弃

从代码到现实:两种机器人控制哲学

有趣的是,这两种思维模型在真实世界的机器人控制中有着各自的应用场景。

机器人自走式:适用于实体机器人的底层控制

如果你要编写代码驱动一台真实的物理机器人(比如扫地机器人、仓库AGV),机器人自走式是更自然的选择。

原因有三

  1. 状态必须持续跟踪:物理机器人的位置是连续的,你不能每走一步就“忘记”它在哪里。你需要全局变量实时记录它的坐标,用于避障、路径规划和电量管理。
  2. 运动依赖于传感器反馈:机器人走完一条边后,需要通过传感器确认是否真的到达了边界(比如撞到墙,或激光雷达检测到边缘)。这个“确认”动作会更新它的位置状态,然后才能决定下一步。
  3. 异常处理需要上下文:如果机器人在运动中被卡住或偏离路线,你需要知道它“本来应该在哪儿”,才能进行纠偏。

实际案例:扫地机器人的沿墙清扫模式。它从墙角出发,沿着墙走,每走一段就检查是否到了拐角。到了拐角就转弯,继续沿着下一面墙走。这个过程中,“当前坐标”和“朝向”是持续更新的核心状态。

边界收缩式:适用于多机器人调度与任务分配

如果你是在调度多个机器人执行任务,或者是在虚拟空间中遍历数据,边界收缩式更合适。

原因有三

  1. 无需跟踪个体状态:你不关心“谁”在遍历,只关心“哪些区域还没被遍历”。边界变量直接描述了剩余任务的范围。
  2. 易于并行化:你可以把边界收缩后的子矩形分配给不同的执行单元,各自独立遍历。每个单元只需要知道自己的任务范围,不需要知道其他单元的位置。
  3. 逻辑简单,不易出错:没有全局位置变量,就不会出现“位置变量被意外修改”导致的bug。每一步都是独立的,便于调试和验证。

实际案例:仓库里有多个机器人需要盘点货架。管理员把仓库划分为若干矩形区域,每个机器人分配一个区域。机器人只知道自己的区域范围,在这个范围内来回遍历。管理员只关心“还有哪些区域没被盘点”,不关心某个机器人此刻具体在哪。

混合模式:大型系统中的分层设计

在实际的大型系统中,往往是两种模式的混合:

  • 高层规划使用边界收缩式:系统将整个任务区域划分为若干子区域,分配给不同的机器人。
  • 底层控制使用机器人自走式:每个机器人接收到自己的子区域后,使用自走式模型在其中执行具体的遍历运动。

这种分层设计结合了两者的优势:高层的任务管理简单清晰,底层的运动控制灵活可靠。

总结

回到最初的螺旋矩阵题,两种写法都能正确解决问题。但理解它们背后的思维模型,能帮助我们在面对更复杂的问题时做出更好的设计选择。

场景特征

推荐模式

原因

单一执行体,状态连续

机器人自走式

需要跟踪当前位置,便于处理连续运动和异常

多个执行体,任务可分割

边界收缩式

任务范围清晰,易于分配和并行

物理世界,有传感器反馈

机器人自走式

位置状态需要与传感器数据融合

虚拟世界,数据遍历

边界收缩式

逻辑简单,不易出错

需要实时避障

机器人自走式

当前位置是避障决策的必要输入

任务完成后汇总结果

边界收缩式

只关心最终结果,不关心中间路径

两种模式没有优劣之分,它们是适用于不同场景的两种思维工具。理解它们的本质区别,就是理解“过程式思维”与“声明式思维”在工程实践中的具体体现。

下一次当你面对一个需要“遍历”的问题时,不妨先问问自己:我是该扮演一个行走的机器人,还是一个指挥的监工?​ 答案会指引你选择正确的模型。


本文首发于CSDN博客,欢迎交流讨论。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。