链表反转问题

时间:2022-07-22
本文章向大家介绍链表反转问题,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

对于链表问题的求解,大体做法是画出图一步一步分析。一般都可以进行原地操作(即额外空间复杂度为O(1))。

问题一:反转链表

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

迭代求解

定义三个指针,记做pre,cur,next。其初值为

pre = head
pre.next = null
cur = head.next
next = null

迭代过程中先使用next存储当前位置的下一个结点,再让当前位置指向其前一个节点,然后更新前一个节点跟新当前结点。

图解如下:

代码如下:

class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        ListNode pre = head;
        ListNode cur = head.next;
        pre.next = null;
        ListNode next = null;
        while(cur != null){
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }
}

时间复杂度O(N),额外空间复杂度O(1)

递归求解

对于一个节点,首先将其后续结点完成反转,再将其连到反转后的后续结点之后。

递归结束条件:当前结点不存在后续结点,直接返回其本身即可。

图解如下:

上述图的当前结点为首结点的情况。

代码实现中对于后续结点我们既想知道其反转后的头结点又想知道其反转后的尾结点,因此使用一大小为2的一维数组作为递归的返回值,其中第一个元素为后溪结点反转后的头结点,第二个元素为其尾节点。

代码如下:

class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null){
            return null;
        }
        ListNode[] info = reverse(head);
        return info[0];
    }
    // info[0] 为反转后的头结点, info[1] 为反转后的尾结点
    public ListNode[] reverse(ListNode head){
        if(head.next == null){
            return new ListNode[]{head, head};
        }
        ListNode[] info = reverse(head.next);
        head.next = null;
        info[1].next = head;
        return new ListNode[]{info[0], head};
    }
}

时间复杂度O(N),额外空间复杂度O(1)

问题二:两两交换链表结点

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

示例:

给定 1->2->3->4, 你应该返回 2->1->4->3.

其中该问题就是问题一的升级版,只不过时两个两个反转,因此每次两个两个往前走。

图解如下:

为了清楚期间上述图解并未考虑不链表长度为奇数的情况,对于该情况的最后一个结点将其接到之前反转后的链表的后面即可。

代码如下:

class Solution {
    public ListNode swapPairs(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        ListNode newHead = head.next;
        ListNode pre = head;
        ListNode cur = head.next;
        head.next = null;
        ListNode next = null;
        while(cur != null){
            next = cur.next;
            cur.next = pre;
            if(next == null || next.next == null){
                pre.next = next;
                break;
            }
            pre.next = next.next;
            pre = next;
            cur = pre.next;
            pre.next = null;
        }
        return newHead;
    }
}

时间复杂度O(N),额外空间复杂度O(1)

问题三:K个一组反转链表

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。

k 是一个正整数,它的值小于或等于链表的长度。

如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

示例:

给你这个链表:1->2->3->4->5

当 k = 2 时,应当返回: 2->1->4->3->5

当 k = 3 时,应当返回: 3->2->1->4->5



说明:

你的算法只能使用常数的额外空间。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reverse-nodes-in-k-group
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

该问题是问题一,问题二的一般化。

使用递归求解 + 迭代求解。整体上进行递归,而递归内部反转K个结点使用迭代。

对于当前头结点,首先判断该链表长度是否不小于K,若小于k直接返回即可,对于不小于k的情况,先对该链表的k个结点进行反转,然后对K+1个结点进行上述操作。

class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode temp = head;
        for(int i = 0; i < k; i++){
            if(temp == null){
                return head;
            }
            temp = temp.next;
        }
        ListNode cur = head.next;
        ListNode pre = head;
        pre.next = null;
        ListNode next = null;
        // 对k个结点反转,k - 1次即可
        for(int i = 0; i < k - 1; i++){
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        head.next = reverseKGroup(cur, k);
        return pre;
    }
}

时间复杂度O(N),额外空间复杂度O(1)