您的位置:首页 > 其它

每天三道冲刺工作--用一种算法来颠倒一个链接表的顺序。现在在不用递归式的情况下做一遍。

2015-05-06 10:42 495 查看
定义一个方法(函数),实现输入一个链表的头结点,然后可以反转这个链表的方向,并输出反转之后的链表的头结点。

typedefstruct Node{
int data;

Node *next;

} Node, *List;
链表类的问题,涉及到了很多指针的操作,需要严谨的分析,全面的分析问题之后,在开始写代码,磨刀不误砍柴工!反转链表,直接的想法,就是把链表中指针的方向反转就可以了,如图所示:



假设 i 结点之前,我们把所有的结点的指针都已经反转了,那么自然 i 和以后的结点链接发生了断裂!如下图;



这样的话,无法继续遍历 i 以后的结点了,那么自然想到,在断链之前,提前保存之前的状态。那么自然想到定义三个指针,分别指向当前结点 i,i 的后继 j,i 的前驱 h 结点。保存断链之前的三个结点的连接状态。然后,假设没问题了,那么继续反转完毕,最后链表的尾结点就是反正链表的头结点了,也就是 next 为 null 的结点,是原始链表的尾结点。

#include <iostream>
using namespace std;
typedef struct Node{
int data;

Node *next;

} Node, *List;

Node * reverseList(List head){
//定义三个指针,保存原来的连接的状态
//当前结点指针

Node *pnow = head;
//当前结点的前驱指针,初始化是 NULL

Node *pre = NULL;
//当前结点的后继指针,初始化也是 null

Node *pnext = NULL;
//定义尾指针

Node *tail = NULL;
//开始遍历链表
while(pnow != NULL){
//如果当前结点不是 null,那么初始化 pnext 指针指向当前结点的下一个结点

pnext = pnow->next;
//如果找到了尾结点,初始化 tail 指针
if(NULL == pnext){

tail = pnow;

}
//进行链表的反转,当前结点的 next 指针指向前一个结点,实现链表方向的反转,此时发生了断链

pnow->next = pre;
//勿忘断链的情形,需要使用 pre 指针保存状态,pre 等价于是后移一个结点

pre = pnow;
//pnow 后移一个结点

pnow = pnext;

}

return tail;

}

定义的这个三个指针,目的就是防止断链之后无法继续遍历链表以后的结点,实现全部的反转。当 pnow 的 next 指向 pnow 的前驱pre(初始化是 null)的时候,已经实现了 pnow 和前驱pre的方向反转,但是 pnow 此时就和后继pnext断链了,那么使用 pre 后移的方式,指向 pnow,同时 pnow 也后移,指向 pnext,而 pnext 继续指向更新之后的 pnow 的 next 结点即可。从而实现了状态的保存,继续遍历全部结点,实现链表反转。

注意关于链表问题的常见注意点的思考:

1、如果输入的头结点是 NULL,或者整个链表只有一个结点的时候

2、链表断裂的考虑

下面看看递归的实现方式

递归的方法其实是非常巧的,它利用递归走到链表的末端,然后再更新每一个node的next 值 ,实现链表的反转。而newhead 的值没有发生改变,为该链表的最后一个结点,所以,反转后,我们可以得到新链表的head。

//递归方式

Node * reverseList(List head)

{
//如果链表为空或者链表中只有一个元素
if(head == NULL || head->next == NULL)

{
return head;

}
else

{
//先反转后面的链表,走到链表的末端结点

Node *newhead = reverseList(head->next);
//再将当前节点设置为后面节点的后续节点

head->next->next = head;

head->next = NULL;
return newhead;

}

}



程序刚开始执行,if 语句失效,进入 else 语句,然后执行Node *newhead = reverseList(head->next);第二个结点的指针参数传入递归函数,一直到,最后一个结点的指针参数传入递归函数,if 语句有效head->next == NULL,返回当前的head 给 newhead 指针指向,如图:



其实在递归函数栈内,按照后进先出的顺序,执行一级级的递归函数,返回末位结点给 newhead 之后,执行递归栈里的第二个递归函数,发生如图



返回 newhead,也就是新的反转之后的链表(临时的),然后进入到递归工作栈里的第一个递归函数,如图:



返回 newhead,也就是反转之后的链表,此时递归工作栈的函数全部执行,返回的结点就是反转之后的链表的头结点(之前的尾结点)

 

本文链接:全面分析再动手的习惯:链表的反转问题(递归和非递归方式),转载请注明。

java版

问题:

给一个单向链表,把它从头到尾反转过来。比如: a -> b -> c ->d 反过来就是 d -> c -> b -> a 。

分析:

假设每一个node的结构是:

[java]
view plaincopy

class Node {  
    char value;  
    Node next;  
}  

因为在对链表进行反转的时候,需要更新每一个node的“next”值,但是,在更新 next 的值前,我们需要保存 next 的值,否则我们无法继续。所以,我们需要两个指针分别指向前一个节点和后一个节点,每次做完当前节点“next”值更新后,把两个节点往下移,直到到达最后节点。

第一种方法就是把每个Node按照顺序存入到一个stack里面,这样,最后面一个就在最上面了。然后,把每一个再取出来,这样顺序就换过来了。

[java]
view plaincopy

public static Node reverse(Node head) {  
    Stack<Node> stack = new Stack<Node>();  
      
    // put all the nodes into the stack  
    while (head != null) {  
        stack.add(head);  
        head = head.next();  
    }  
      
    //reverse the linked list  
    Node current = stack.pop();  
    head = current;  
    while (stack.empty() != true) {  
        Node next = stack.pop();  
        //set the pointer to null, so the last node will not point to the first node.  
        next.setNext(null);  
        current.setNext(next);  
        current = next;  
    }  
      
    return head;      
}  

第二种方法就是利用两个指针,分别指向前一个节点和当前节点,每次做完当前节点和下一个节点的反转后,把两个节点往下移,直到到达最后节点。

[java]
view plaincopy

public static Node reverse(Node head) {  
    Node previous = null;  
  
    while (head != null) {  
        Node nextNode = head.next();  
        head.setNext(previous);  
        previous = head;  
        head = nextNode;  
    }  
          
    return previous;      
}  

上面代码使用的是非递归方式,这个问题也可以通过递归的方式解决。代码如下:

[java]
view plaincopy

public Node reverse(Node current)  
 {  
     if (current == null || current.next == null) return current;  
     Node nextNode = current.next;  
     current.next = null;  
     Node reverseRest = reverse(nextNode);  
     nextNode.next = current;  
     return reverseRest;  
 }  

递归的方法其实是非常巧的,它利用递归走到链表的末端,然后再更新每一个node的next 值 (代码倒数第二句)。 在上面的代码中, reverseRest 的值没有改变,为该链表的最后一个node,所以,反转后,我们可以得到新链表的head。

参考:
http://stackoverflow.com/questions/354875/reversing-a-linked-list-in-java-recursively

http://blog.csdn.net/beiyeqingteng/article/details/7030020

转载请注明出处:http://blog.csdn.net/beiyetengqing
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐