翻转链表-迭代和递归双版本

将一个链表翻转,如 1->2->3->4 变成 4->3->2->1 的链表。这是一个非常著名的面试题,看似非常的简单,但实际上非常的tricky.

实现方法可以有递归和迭代两种方法,这两个算法也都保证了in-place 和 one-pass. 所以效率还是很高的。这篇文章主要基于http://leetcode.com/2010/04/reversing-linked-list-iteratively-and.html. 他讲解的基本已经比较清楚,我就再从比较菜的角度去分析一下。

 

迭代方法:

首先来看迭代版本的基本思路,先上图:

上图就是迭代的第一步,基本就是prev, curr, next三个指针跟踪着当前点的情况,然后更新后向前移动,直到移到链表末尾

看代码后应该更清晰:

 1 void reverse(Node*& head) {
 2   if (!head) return;
 3   Node* prev = NULL;
 4   Node* curr = head;
 5   while (curr) {
 6     Node* next = curr->next;
 7     curr->next = prev;
 8     prev = curr;
 9     curr = next;
10   }
11   head = prev;
12 }

代码中要注意 Node*&, 毕竟是改变了链表的结构,所以链表的头指针应该传一个引用。

翻转链表的迭代版本很清晰,且容易实现。而下面的递归方法虽然代码很短,但是比较难理解,且实现起来细节也很多。

 

递归版本:

这个整体过程比较复杂,所以还是先上图:

然后来看着代码分析:

void reverse(Node*& p) {
  if (!p) return;
  Node* rest = p->next;
  if (!rest) return;
  reverse(rest);
  p->next->next = p;
  p->next = NULL;
  p = rest;
}

递归过程中,首先迅速由递归函数进行至第一幅图的情况(其实下面还有一步,此时rest 指向NULL,返回到第一幅的情况)

然后将当前节点的下一节点的子节点倒指向其父节点(即当前节点),然后将当前节点指向NULL,即当前我们翻转了从当前节点到末尾节点。

注意这之后的最后一个语句,将p 赋值为 rest,然后返回。 返回到上一层会发生什么呢? 这就要注意每层调用reverse函数时,传进去的指针式rest, 即修改上一层的rest指针为当前层的rest指针,换句话说,在以后递归的过程中,rest指针将一直指向最后一个节点,即新的链表根节点。

posted on 2014-06-21 22:30  soyscut  阅读(2002)  评论(4编辑  收藏  举报

导航