LeetCode-链表专题

反转链表

206. 反转链表

剑指 Offer 24. 反转链表

反转一个单链表。

示例:

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

进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

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

这是一个基础题了,应该能在纸上写出来!!!

class Solution {
    public ListNode reverseList(ListNode head) {
        // [prev]			[curr]1->2->3->4->5
        // NULL<-[prev]1	   [curr]2->3->4->5
        ListNode curr = head;
        ListNode prev = null;

        while (curr != null) {
            // 保存当前节点的下一个节点
            ListNode tmp = curr.next;
            // 头插法
            curr.next = prev;
            prev = curr;
            // 链表向后移动一节
            curr = tmp;
        }

        return prev;
    }
}

92. 反转链表 II

反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。

说明:
1 ≤ m ≤ n ≤ 链表长度。

示例:

输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL

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

这个题和反转链表 I 实际上是一样的,只是增加了指定位置约束。

class Solution {
    public ListNode reverseBetween(ListNode head, int m, int n) {
        // 哨兵节点
        // [1] [sentinel]->1[head]->2->3->4->5->NULL, m = 2, n = 4
        ListNode sentinel = new ListNode(0);
        sentinel.next = head;

        // 先将prev节点移动到第m个节点的*头节点*,示例中`m=2,n=4`也就是 `1` 的位置,作为`m-n`区间反转后的头节点
        // [2] [sentinel|prev]->1[head]->2->3->4->5->NULL, m = 2, n = 4
        ListNode prev = sentinel;
        for (int i = 1; i < m; i++) {
            prev = prev.next;
        }
        // [3] [sentinel]->1[head|prev]->2->3->4->5->NULL, m = 2, n = 4

        // 反转第 m-n 位置的节点,即2->3->4这一段
        // [4] [sentinel]->1[head|prev]->2[curr]->3->4->5->NULL, m = 2, n = 4
        // {
        //		loop
        // 		[5] [sentinel]->1[head|prev]->2[curr]->3[next]->4->5->NULL
        // 		[6] [sentinel]->1[head|prev]->2[curr]->4->5->NULL
        // 									  3[next]->4->5->NULL
        // 		[7] [sentinel]->1[head|prev]->2[curr]->4->5->NULL
        // 							 3[next]->2[curr]->4->5->NULL
        // 		[8] [sentinel]->1[head|prev]->3[next]->2[curr]->4->5->NULL
        // }
        ListNode curr = prev.next;
        for (int i = m; i < n; i++) {
            ListNode next = curr.next;
            curr.next = next.next;
            next.next = prev.next;
            prev.next = next;
        }
        return sentinel.next;
    }
}

剑指 Offer 06. 从尾到头打印链表

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

示例 1:

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

限制:

0 <= 链表长度 <= 10000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/cong-wei-dao-tou-da-yin-lian-biao-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

class Solution {
    public int[] reversePrint(ListNode head) {
        // 1. 求链表长度
        int length = 0;
        ListNode cursor = head;
        while (cursor != null) {
            cursor = cursor.next;
            length++;
        }
        
        // 2. 预分配 length 长度的数组作为返回值,利用数组随机访问特性输出结果
        int[] array = new int[length];
        for (int idx = length -1; idx >= 0; idx++) {
            array[idx] = head.val;
            head = head.next;
        }

        return array;
    }
}

61. 旋转链表

给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。

示例 1:

输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL
示例 2:

输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: 0->1->2->NULL
向右旋转 4 步: 2->0->1->NULL

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

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        if (head == null || k == 0) return head;

        // 1. 先遍历一遍链表,求链表长度,将链表转成环形链表
        int length = 1;
        ListNode curr = head;
        while (curr.next != null) {
            curr = curr.next;
            length++;
        }
        // 1.1. 现在 curr 指向了链表的尾节点, 将尾节点连接到头节点, 形成循环链表
        curr.next = head;

        // 2. 链表向右旋 k 个位置, 也就是向左移动 length - (k % length) 个位置
        int shift = length - (k % length);

        for (int i = 0; i < shift; i++) {
            curr = curr.next;
        }

        // 2.1 此时 curr 指向了旋转链表的尾节点
        head = curr.next;
        curr.next = null;
        return head;
    }
}

剑指 Offer 22. 链表中倒数第k个节点

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。

示例:

给定一个链表: 1->2->3->4->5, 和 k = 2.

返回链表 4->5.

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode fast = head;
        ListNode slow = head;
        
        for (int i = 0; i < k; i++) {
            fast = fast.next;
        }
        
        while (fast != null) {
            slow = slow.next;
            fast = fast.next;
        }
        
        return slow;
    }
}

剑指 Offer 35. 复杂链表的复制

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

示例 1:

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
示例 2:

输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]
示例 3:

输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]
示例 4:

输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。

提示:

-10000 <= Node.val <= 10000
Node.random 为空(null)或指向链表中的节点。
节点数目不超过 1000 。

注意:本题与主站 138 题相同:https://leetcode-cn.com/problems/copy-list-with-random-pointer/

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/fu-za-lian-biao-de-fu-zhi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/
class Solution {
    public Node copyRandomList(Node head) {
        // 1->2->3->4->5->6->NULL
        // 1. 复制链表节点
        Node curr = head;
        while (curr != null) {
            // 复制 curr 节点
            Node copy = new Node(curr.val);

            // 将 copy 节点插入到 curr 后面
            copy.next = curr.next;
            curr.next = copy;

            // curr 指向下一个节点
            curr = copy.next;
        }

        // 此时链表结构如下所示
        // 1->1'->2->2'->3->3'->4->4'->5->5'->6->6'->NULL

        // 2. 复制 random 节点
        curr = head;
        while (curr != null) {
            Node copy = curr.next;
            // copy 节点的 random 指向原节点的 random 节点的 copy 节点, 完成复制
            if (curr.random != null) {
                copy.random = curr.random.next;
            }

            curr = copy.next;
        }

        // 3. 将 copy 链表从原链表上断开
        Node sentinel = new Node(0);
        curr = sentinel;
        while (head != null) {
            Node copy = head.next;
            // 恢复原链表
            head.next = copy.next;
            // 构建copy链表
            curr.next = copy;
            // 游标后移
            curr = curr.next;
            head = head.next;
        }

        return sentinel.next;
    }
}
posted @ 2020-09-04 09:30  HiroSyu  阅读(259)  评论(0编辑  收藏  举报