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;
}
}