单链表反转

1.什么是链表的反转?


当我在床上辗转反侧难以入睡的时候,我反过来翻过去的睡不着,想学习,于是想起了最近在看的反转链表问题,这个还是需要一些思考哈。
首先当我们在聊到链表反转的时候,一定说的都是单链表,双链表本身就具有前驱指针 Prev 和后续指针 next,无需进行反转哈。
单链表反转,反转后的效果如下:

看起来很简单,只需要将单链表所有结点的 next 指向,指向它的前驱节点即可。

2.使用栈进行链表的反转

引入一个栈结构(或者数组),就可以实现。只是需要额外的空间,数组的比较简单,就是遍历这个链表,将数据data用数组存起来,再反向遍历组成结点连接起来即可,这样的话时间和空间复杂度均为n.
这里以栈为例,在原本链表的数据结构之外,引入一个栈(数组也可),将单链表循环遍历,将所有结点入栈,最后再从栈中循环出栈,记住出栈的顺序,得到的就是反转后的单链表。

但是这样实现,有一个问题,它会额外消耗一个栈的内存空间,此时空间复杂度就变成了 O(n)。并且,栈会遇到的问题,使用此种方式都会遇到,例如比较常见的,栈的深度问题。

2.原地反转链表,空间复杂度降低

在排序算法中,有一个概念叫原地排序,指的是不需要引入额外的存储空间,在原数据结构的基础上进行排序。这种排序算法的空间复杂度是 O(1)。例如我们常见的冒泡排序、插入排序都是原地排序算法。

这里,我们也可以在原单链表的数据结构上,进行单链表反转。容易出错的点在于,指针丢失,造成断链。在转换结点指针的时候,所需结点和指针反转顺序,都很重要,一不小心,就会丢掉原本的后续指针 next,导致链表断裂。

在前面的学习中我们知道删除链表某个结点时,需要知道前后的两个结点,加上被删除结点,共计三个结点,单链表反转时也是这样的。
当我们要对一个结点进行指针翻转的时候,我们也需要知道三个结点。

  • 待反转的结点。
  • 待反转结点的前驱结点 prev。
  • 待反转结点的后续结点 next。
    整个代码如下,可以直接复制运行。此处以头指针为参数,反转之后返回值为反转后的单链表头指针指向的结点。
public class Test {
    static Node reverseByLoop(Node head) {
        //如果头指针为空(表示链表为空)或者头指针的next结点为空(代表仅有一个结点),这样的话直接返回即可。
        //注意这个条件仅在进入这个方法的时候判断一次,后续while循环遍历的时候不会进行head.next == null这个判断,
        //不然最后一个结点不能反转
        if (head == null || head.next == null) {
            return head;
        }

        Node preNode = null;
        Node nextNode = null;
        // 先记录待反转结点的后一个结点位置,避免断链,然后进行反转操作,将头结点的下一个结点设置为null,同时将当前结点和 
        //前一个结点向后移动一位
        while (head != null) {
            nextNode = head.next;
            head.next = preNode;
            preNode = head;
            head = nextNode;
        }
        return preNode;
    }

    public static void main(String[] args) {
        // 1、构建单链表
        Node head = new Node(0);
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);
        Node node5 = new Node(5);
        Node node6 = new Node(6);
        head.next = node1;
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;
        node5.next = node6;
        Node node = reverseByLoop(head);
        while (node.next!=null){
            System.out.println(node.data);
            node = node.next;
        }

    }
}

class Node {
    int data;
    Node next;

    Node(int data) {
        this.data = data;
    }
}

3.递归实现单链表反转

递归还是在借助函数调用栈的思想,其实本质上也是一个栈。没什么好说的,直接上代码。

static Node reverseByRecursion(Node head){
    if(head == null || head.next == null){
        return head;
    }

    Node newHead = reverseByRecursion(head.next);

    head.next.next = head;
    head.next = null;
    return newHead;
}
原文地址:https://www.cnblogs.com/lovelywcc/p/14203827.html