返回

剖析剑指Offer 16:直击链表反转的精髓

前端

剑指Offer 16:揭开链表反转的神秘面纱

算法世界中,剑指Offer系列以其严谨的题设和巧妙的解法而闻名。其中,第16题——链表反转,更是算法爱好者们津津乐道的话题。

所谓链表反转,是指将一个单链表中结点的顺序逆置,使链表尾结点变为头结点,原头结点变为尾结点。这种操作在数据结构和算法领域有着广泛的应用,如字符串反转、栈和队列的实现等等。

面对这道颇具挑战性的题目,我们该如何着手解决呢?不妨从链表反转的原理入手,循序渐进地攻克难题。

链表反转的原理:化繁为简,妙笔生花

链表反转的本质在于改变链表中结点的指向。原本相邻的两个结点,通过改变它们的指针,可以变成以相反顺序相连。

假设我们有一个单链表,结构如下:

头结点 -> 结点1 -> 结点2 -> 结点3 -> 尾结点

为了将其反转,我们需要将链表中结点的指向一一改变。具体步骤如下:

  1. 将头结点的指针指向尾结点。
  2. 将结点1的指针指向头结点。
  3. 将结点2的指针指向结点1。
  4. 以此类推,直到将链表中的所有结点都反转完毕。

经过这番操作,原本的单链表就变成了:

尾结点 <- 结点3 <- 结点2 <- 结点1 <- 头结点

是不是很简单?看似复杂的链表反转,其实只需要通过改变结点的指向就能实现。

代码示例:从原理到实践,一气呵成

掌握了链表反转的原理,我们就可以着手编写代码了。下面是C++语言中的一个链表反转示例:

struct ListNode {
  int val;
  ListNode *next;
  ListNode(int x) : val(x), next(NULL) {}
};

ListNode* reverseList(ListNode* head) {
  ListNode* prev = NULL;
  ListNode* curr = head;
  while (curr) {
    ListNode* nextNode = curr->next;
    curr->next = prev;
    prev = curr;
    curr = nextNode;
  }
  return prev;
}

在这个代码中,我们首先定义了链表结点的结构体ListNode,然后是链表反转函数reverseList。该函数接收一个单链表的头结点作为参数,返回反转后的链表头结点。

在函数内部,我们使用三个指针prevcurrnextNode来进行链表的反转操作。prev指向当前结点的前一个结点,curr指向当前结点,nextNode指向当前结点的下一个结点。

我们从头结点开始,将prev指针置为NULL,然后依次遍历链表中的所有结点。在每个结点,我们将curr结点的next指针指向prev结点,并将prev指针和curr指针分别指向当前结点的上一个结点和下一个结点。

就这样,我们遍历完整个链表,并将所有结点的指针一一反转。最后,我们将prev指针返回作为反转后的链表头结点。

结语:登峰造极,精益求精

链表反转,看似简单,实则蕴藏着不少奥妙。通过剖析其原理和编写代码示例,我们对链表反转有了更深入的理解。在算法的世界里,每一个看似简单的题目都值得我们细细品味,从中汲取知识和灵感。