在Python中,链表是一种基础且灵活的数据结构。它不依赖连续的内存空间,而是通过节点之间的引用关系来组织数据。理解链表遍历,首先要搞清楚节点和链表本身是如何协作的。

节点与链表的基本关系
链表的核心组成单元是节点。一个典型的单链表节点包含两部分:存储数据的变量,以及指向下一个节点的引用。在Python里,由于一切皆对象,这个引用其实就是另一个节点对象。多个节点通过next属性首尾相接,就形成了链表。链表本身通常只保存头节点,其余节点都要靠遍历才能到达。
很多初学者会把链表对象和节点对象混淆。实际上,链表可以是一个简单的类,它内部只维护一个head属性;而节点是独立的类实例。下面给出一个最基础的节点与链表定义,帮助建立清晰的概念。
class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
def append(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
return
curr = self.head
while curr.next:
curr = curr.next
curr.next = new_node
从上面的代码可以看出,LinkedList并不直接保存所有数据,它只持有head。每个Node通过next指向下一个Node,最后一个节点的next为None。这种结构决定了我们必须从head开始,才能访问后续元素。
迭代方式遍历链表
最常见的遍历方法是使用循环,从一个临时变量指向头节点,每次循环移动到next,直到遇到None。这种方式空间复杂度为O(1),不会额外占用栈空间,适合处理很长的链表。
下面的示例展示了如何打印链表中每个节点的数据。注意我们用了curr变量而不是直接操作head,这是为了保留链表入口,避免遍历后找不到头节点。
def print_list(link):
curr = link.head
while curr:
print(curr.data)
curr = curr.next
# 使用示例
ll = LinkedList()
for v in [10, 20, 30]:
ll.append(v)
print_list(ll)
迭代遍历的优点是直观且安全。只要控制好循环条件curr不为None,就不会越界。缺点是所有逻辑写在一个循环里,如果遍历中需要做复杂操作,代码可能变长。
递归方式遍历链表
递归遍历则是利用函数调用栈,每次处理当前节点后,把curr.next传入自身。代码更简洁,也更符合数学归纳法的描述方式。不过递归深度受限于Python默认栈大小,数据量很大时会触发递归错误。
以下代码用递归打印链表。它先处理当前节点,再对后续节点递归调用。当节点为None时作为终止条件返回。
def print_recursive(node):
if not node:
return
print(node.data)
print_recursive(node.next)
# 从头部开始递归
print_recursive(ll.head)
递归写法突出了节点之间的关系:每一个节点都负责把自己交给下一个节点。但在工程实践中,如果链表长度不可控,建议优先使用迭代,或者手动将递归改为尾递归优化(Python默认不优化尾递归)。
遍历中的常见误区
一个典型错误是在遍历时直接修改head。例如有人写出while link.head: link.head = link.head.next,这样虽然也能走完,但循环结束後link.head变成了None,整个链表就丢失了。遍历应该是只读地访问,修改结构前要想清楚引用归属。
另一个误区是认为链表和列表一样可以用下标随机访问。链表必须逐个next前进,因此按索引取数也要遍历。理解节点与链表的引用模型,才能正确评估操作代价,写出高效稳定的代码。
| 遍历方式 | 空间占用 | 适用场景 |
|---|---|---|
| 迭代 | O(1) | 长链表、嵌入式环境 |
| 递归 | O(n)栈空间 | 短链表、逻辑清晰优先 |
总结来说,Python链表遍历的本质是顺着节点next引用移动。节点是数据载体,链表是引用组织者。弄清这层关系,插入、删除、反转等操作都会变得容易理解。