每天一道算法题(java数据结构与算法)——>反转链表

Posted stormzhuo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了每天一道算法题(java数据结构与算法)——>反转链表相关的知识,希望对你有一定的参考价值。

这是LeetCode上的 [024,反转链表],难度为 [简单]

题目

给定单链表的头节点 head ,请反转链表,并返回反转后的链表的头节点。

示例 1:

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:

输入:head = [1,2]
输出:[2,1]

示例 3:

输入:head = []
输出:[]

题解1(递归方法)

题解

思路分析

递归反转其实就是从原链表的第一个存数据的结点开始,依次递归调用反转每一个结点,直到把最后一个结点反转完毕,整个链表就反转完毕。下面通过带哨兵结点的例子来说明(不带哨兵结点也是类似的)

原链表

步骤

  1. 调用reverse(Node curr)方法反转每一个结点,从元素1结点开始;
  2. 如果发现curr还有下一个结点,则递归调用reverse(curr.next)对下一个结点反转;
  3. 最终递归的出口是元素4结点,因为它没有下一个元素了,当到了出口处,以下分两种方式(带哨兵结点和不带哨兵结点)
    • 带哨兵结点:让head(哨兵结点)指向元素4结点;共递归调用4次
    • 不带哨兵结点:让4结点指向3结点,共递归调用3次
  4. 递归开始返回

带哨兵结点的递归过程

代码实现

public class Solution 

    public  ListNode reverseList1(ListNode head) 
        /* 每次递归时都会做的条件判断
        head == null是为了防止第一次通过对象调用reverseList时(递归还没开始),链表为空
        head.next == null是判断每次递归是head是不是最后一个结点,若是,则返回尾结点(递归出口)*/
        if (head == null || head.next == null) 
            return head;
        
       	// 递归,反复调自己,直到有返回值时,即到尾结点时返回尾结点(递归出口),preNode始终是尾结点(会变成头结点)
        ListNode preNode = reverseList1(head.next);
        // 让当前结点的下一个结点指向当前结点(实现反转)
        head.next.next = head;
        // 让当前结点的下一个结点为null(它下一个结点在上一步已经指向它了,即链表没有断开
        head.next = null;
        // 返回原始的尾结点,递归后变成头结点
        return preNode;
    

结点类

public class ListNode<T> 

    T val;
    ListNode next;

    public ListNode() 

    

    public ListNode(T val) 
        this.val = val;
    

    public ListNode(T val, ListNode next) 
        this.next = next;
    

测试代码

public class Test 

    @org.junit.Test
    public void test1() 
        ListNode<Integer> head = new ListNode<>(1);
        ListNode<Integer> list2 = new ListNode<>(2);
        ListNode<Integer> list3 = new ListNode<>(3);
        ListNode<Integer> list4 = new ListNode<>(4);
        ListNode<Integer> list5 = new ListNode<>(5);
        head.next = list2;
        list2.next = list3;
        list3.next = list4;
        list4.next = list5;
        System.out.println("测试反转之前--------------------");
        ListNode h1 = head;
        while (h1 != null) 
            System.out.print(h1.val + " ");
            h1 = h1.next;
        
        System.out.println();
        System.out.println("测试反转之后--------------------");
        ListNode h2 = new Solution().reverseList1(head);
        while (h2!= null) 
            System.out.print(h2.val + " ");
            h2 = h2.next;
        
    

测试结果

复杂度分析
假设链表为n

时间复杂度:递归调用相当与遍历链表,所以时间复杂度为O(n)
空间复杂度:递归调用过程中需要声明n个结点,故空间复杂度为O(n);

题解2(迭代法)

思路分析

迭代法就是在遍历链表时,让每一个结点指向它的前一个结点,由于单向链表只有后缀指针,没有前缀指针,故需要在遍历时存储前一个结点的信息,第一个结点没有前一个结点,则指向null成为尾结点,让每一个结点指向它的前一个结点之前,需要先存储这个结点的下一个结点信息,防止链表断开

步骤

  1. 声明3个结点curr,pre,next,curr存储当前遍历的结点,pre存储当前节点的前一个结点,next存储当前结点的下一个结点
  2. 遍历链表
    • 遍历过程先存储当前结点的下一个结点
    • 然后再让当前结点指向它的前一个结点
    • 让当前结点作为它下一个结点的前一个结点,重置pre
    • 让当前结点的下一个结点作为当前结点,重置curr;

代码实现

public class Solution 

    public ListNode reverseList2(ListNode head) 
        // 声明一个结点,存储当前结点
        ListNode curr = head;
        // 声明一个结点,存储当前结点的前一个结点
        ListNode pre = null;
        // 遍历链表,循环终止条件为当前结点为尾结点
        while (curr != null) 
            // 存储当前结点的下一个结点
            ListNode next = curr.next;
            // 当前结点的下一个结点指向当前结点的前一个结点
            curr.next = pre;
            // 重置当前结点的前一个结点,让当前结点作为当前结点的下一个结点的前一个结点
            pre = curr;
            // 重置当前结点,让当前结点的下一个结点作为当前结点
            curr = next;
        
        return pre;
    

复杂度分析

假设当前链表长度为n

时间复杂度:需要遍历链表,故时间复杂度为O(n)
空间复杂度:声明了三个结点curr,pre,next,故空间复杂度为O(1)

以上是关于每天一道算法题(java数据结构与算法)——>反转链表的主要内容,如果未能解决你的问题,请参考以下文章

每天一道算法题(java数据结构与算法)——> 链表的中间结点

每天一道算法题(java数据结构与算法)——>链表中环的入口节点

每天一道算法题(java数据结构与算法)——> 链表中的两数相加

每天一道算法题(java数据结构与算法)——>链表中环的入口节点

7天带你全方位刷爆数据结构与算法,每天一道,高效刷题

每天一道算法题(java数据结构与算法)——>两个链表的第一个公共节点