关于单链表反转的一点整理

单链表的反转困扰了我好几天了。今天终于一通百通了,特地记录一下,免得以后又忘记了。脑子笨,只能靠这种办法了。

之前网上的一种做法是这样的:

 1 public void reversList(){
 2             Node pre = null;
 3             Node next = null;
 4             while (head != null) {
 5                 next = head.next;
 6                 head.next = pre;
 7                 pre = head;
 8                 head = next;
 9             }
10                 head = pre;
11 }

 核心的代码就是这一段:

但实际上做种做法是错误的。我们先看看反转之后的情况。

一遍一遍遍历的过程是这样的。每一行就是一次遍历。

1 Node [data=4, next=Node [data=3, next=Node [data=2, next=Node [data=1, next=Node [data=null, next=null]]]]]
2 Node [data=3, next=Node [data=2, next=Node [data=1, next=Node [data=null, next=null]]]]
3 Node [data=2, next=Node [data=1, next=Node [data=null, next=null]]]
4 Node [data=1, next=Node [data=null, next=null]]
5 Node [data=null, next=null]

这个实际上我没办法遍历,我是用最笨的办法一点一点还原出来的,如下:

1 System.out.println(list.getHead());
2 System.out.println(list.getHead().next);
3 System.out.println(list.getHead().next.next);
4 System.out.println(list.getHead().next.next.next);
5 System.out.println(list.getHead().next.next.next.next);

我们再看看链表在反转之前是什么样子的,同样适用一样的笨办法:

1 Node [data=null, next=Node [data=1, next=Node [data=2, next=Node [data=3, next=Node [data=4, next=null]]]]]
2 Node [data=1, next=Node [data=2, next=Node [data=3, next=Node [data=4, next=null]]]]
3 Node [data=2, next=Node [data=3, next=Node [data=4, next=null]]]
4 Node [data=3, next=Node [data=4, next=null]]
5 Node [data=4, next=null]

现在看来,反转前后,链表中有效的值只是大概相等而已,实际上并不一致。其在首尾节点上也是不一样的。

这种思路一开始是从head开始遍历开始反转的,但是其实head是一个指针节点,真正有效的节点是head.next,应该从这个开始遍历替换,所以真正应该遍历的当前节点其实是 head.next。而应该先保存的当前节点的下一个节点其实是 head.next.next。这是第一个错误。

第二个错误是:用来保存前一个节点的变量,不应该是Node  pre = null;而应该是Node pre = new Node(null)。这两种写法,完全是两个不同的东西。Node  pre = null 完全就是只有一个栈空间里的引用而已,根本没有指向堆里的空间,事实上这就不是一个对象。而Node pre = new Node(null)这种写法,是一种实打实的对象,是真正的引用指向了堆里面的空间了。所以当第二步  head.next = pre;(正确的是:head.next.next = pre.next)的时候,得到的结果就完全不一样了。如果pre=null;那就是把前一个有效节点都置为null了,这个对象就死了,真正要做的是断开前一个节点与后一个节点之间的指针连接,所以要置为null的是 pre.next,pre的指针域是null,而不是pre本身是null。这是两码事。这里我第一次看到网上的答案时,还不明白,为什么不可以直接直接Node  pre = null,这下才算是明白。

第一步第二步都错了之后,后面的步骤肯定也是错的了。

那为什么最后还可以得出一个对的假象呢?即:

Node [data=4, next=Node [data=3, next=Node [data=2, next=Node [data=1, next=Node [data=null, next=null]]]]]

最后得到的链表是这个,

这就是因为第二次循环之后,pre的next域,被恰好填充了对象,但是这个对象却是一个错误的对象如:

Node [data=1, next=Node [data=null, next=null]]

具体怎么错,以后慢慢研究吧。

正确的思路应该是,从head的next域指向的第一个有效节点开始遍历。可以直接在原来的链表上进行遍历替换。最后用反转之后的链表的第一个有效节点再接上head.next,这样head的指针域就指向了反转之后的链表。如下:

 1  public void reversList(){
 2          Node pre = new Node(null);
 3         //Node pre = null;错误写法
 4             Node next = null;
 5
 6             while (head.next != null) {//从第一个有效节点开始遍历
 7                 next = head.next.next;//先记录当前节点的下一个节点
 8                 head.next.next = pre.next;//将当前节点的指针域置空
 9                 pre.next = head.next;//将下一节点的指针域,指向当前节点,完成节点交换
10                 head.next = next;//往后移动
11             }
12
13                 head.next = pre.next;//最后把反转后的节点与头结点联系上。最终得到完整的反转之后的链表。
14 }

或者也。可以新创建一个新的链表,然后遍历老链表后,把节点添加到新链表的第一个有效节点位置。最后返回新的链表,或者用新的链表覆盖head。也许这样比较容易理解:

public void reversList(){
             Node temp = head.next;//设置一个临时变量保存第一个有效节点
             Node newHead = new Node(null);//设置一个新的头结点
             Node next = null;//用于保存下一个节点

             while(temp != null){
                 next = temp.next;//保存当前节点的下一个节点
                 temp.next = newHead.next;//将当前节点的指针域置空
                 newHead.next = temp;//始终把当前节点插入到新链表的第一个有效节点的位置
                 temp = next;//向后移动一个节点
             }

             head = newHead;//最后把新的链表的头节点赋值给原节点的头结点,完成两个链表的合并
              //   head.next = newHead.next; 这种写法也是一个一样的效果,但是行为是不一样的行为。
        }        

如果还有不对,欢迎指正。

原文地址:https://www.cnblogs.com/lukely/p/11349363.html

时间: 2024-10-10 23:10:40

关于单链表反转的一点整理的相关文章

单链表反转问题

单链表反转问题 基本问题 如何将单链表反转? 算法实现 /** * * Description: 单链表反转. * * @param head * @return ListNode */ public static ListNode reverseList(ListNode head) { if (head == null) { return head; } ListNode prev = null; ListNode current = head; ListNode next = null;

单链表反转的2种方法

1 public class ReverseDemo { 2 3 /** 4 * 单链表反转的两种方法 5 */ 6 public static void main(String[] args) { 7 Node head =new Node("a"); 8 Node node2=new Node("b"); 9 Node node3=new Node("c"); 10 head.setNext(node2); 11 node2.setNext(

链表 单链表反转

思路1:O(n^2). “狸猫换太子”,不进行改动链表结构,只首尾交换len/2次.但是在本函数中用到了定位函数,定位函数实际上是遍历了一遍整个链表,所以综合效率很低,达到O(n^2). //单链表反转(O(n^2)) void reverseList(Node* Head) { int count = numOfNodes(Head); //首尾交换 for(int i=1; i<=count/2; i++) { Node* p1 = locateNodeI(Head, i); Node* p

单链表反转C语言实现

单链表的反转可以使用循环,也可以使用递归的方式 1.循环反转单链表 循环的方法中,使用pre指向前一个结点,cur指向当前结点,每次把cur->next指向pre即可. 代码: # include <iostream> # include <cstdlib> using namespace std; struct linkNode { int val; linkNode *next; linkNode(int x):val(x),next(NULL){} }; linkNod

单链表反转python实现

单链表的反转可以使用循环,也可以使用递归的方式 1.循环反转单链表 循环的方法中,使用pre指向前一个结点,cur指向当前结点,每次把cur->next指向pre即可. 代码: class ListNode: def __init__(self,x): self.val=x; self.next=None; def nonrecurse(head): #循环的方法反转链表 if head is None or head.next is None: return head; pre=None; c

数据结构 单链表反转 回顾练习

之前写了一个单链表反转,但是使用的新的空间. 这次的反转是不修改原来的结构,直接将节点内的元素进行修改 1 #!/usr/bin/env python3 2 3 class LNode(object): 4 def __init__(self, elem, next_=None): 5 self.elem = elem 6 self.next = next_ 7 8 class ListError(Exception): 9 pass 10 11 class LList(object): 12

单链表反转(Singly Linked Lists in Java)

单链表反转(Singly Linked Lists in Java) 博客分类: 数据结构及算法 Java代码   package dsa.linkedlist; public class Node<E>{ E data; Node<E> next; } Java代码   package dsa.linkedlist; public class ReverseLinkedListRecursively { public static void main(String args[])

单链表反转java代码

据说单链表反转问题面试中经常问,而链表这个东西相对于数组的确稍微难想象,因此今天纪录一下单链表反转的代码. 1,先定义一个节点类. 1 public class Node { 2 int index; 3 Node next; 4 5 public Node(int index, Node next) { 6 this.index = index; 7 this.next = next; 8 } 9 } 2,我一共写了三种方法 (1)迭代法.先将下一节点纪录下来,然后让当前节点指向上一节点,再将

时间复杂度为O(n)的非递归单链表反转【算法导论课后题】

单链表反转:1->2->3->4... 思路:先将1指向3,2指向1,结果为2->1->3->4,然后循环将3插入到2之前 <span style="font-size:18px;">void reverseLinkedList(List head) { List tmp,p; if(head==null) { return ; } tmp=head->next; while(tmp->next !=null){ p=tmp-