目录
八、 链表
57. 环形链表 ① ×
58. 两数相加 ② √
59. 合并两个有序链表 ① √-
60. 随机链表的复制 ②
61. 反转链表II ② ×
62. K个一组翻转链表 ③
63. 删除链表的倒数第N个结点 ② √-
64. 删除排序链表中的重复元素II ② √-
65. 旋转链表 ② √-
66. 分隔链表 ② √
67. LRU缓存 ②
八、 链表
57. 环形链表 ① ×
给你一个链表的头节点 head
,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos
不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true
。 否则,返回 false
。
示例 1:
输入:head = [3,2,0,-4], pos = 1 输出:true 解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0 输出:true 解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1 输出:false 解释:链表中没有环。
提示:
- 链表中节点的数目范围是
[0, 104]
-105 <= Node.val <= 105
pos
为-1
或者链表中的一个 有效索引 。
进阶:你能用 O(1)
(即,常量)内存解决此问题吗?
力扣解析:
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
方法2:(0ms)
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) return false;
int n = 10010;
while (n -- > 0) {
head = head.next;
if (head == null) return false;
}
return true;
}
58. 两数相加 ② √
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例 1:
输入:l1 = [2,4,3], l2 = [5,6,4] 输出:[7,0,8] 解释:342 + 465 = 807.
示例 2:
输入:l1 = [0], l2 = [0] 输出:[0]
示例 3:
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9] 输出:[8,9,9,9,0,0,0,1]
提示:
- 每个链表中的节点数在范围
[1, 100]
内 0 <= Node.val <= 9
- 题目数据保证列表表示的数字不含前导零
方法1:(1ms)
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode head = new ListNode();
ListNode temp = head;
int more = 0;
int res = 0;
while (!(l1 == null && l2 == null)){
res = (l1 != null? l1.val : 0) + (l2 != null? l2.val : 0) + more;
if (res > 9){
res = res - 10;
more = 1;
}else {
more = 0;
}
ListNode newNode = new ListNode(res);
temp.next = newNode;
temp = temp.next;
if (l1.next != null){
l1 = l1.next;
}
if (l2.next != null){
l2 = l2.next;
}
}
if (more == 1){
temp.next = new ListNode(more);
}
return head.next;
}
59. 合并两个有序链表 ① √-
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3,4,4]
示例 2:
输入:l1 = [], l2 = [] 输出:[]
示例 3:
输入:l1 = [], l2 = [0] 输出:[0]
提示:
- 两个链表的节点数目范围是
[0, 50]
-100 <= Node.val <= 100
l1
和l2
均按 非递减顺序 排列
方法1:(0ms)
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if (list1 == null && list2 == null) {
return null;
} else if (list1 == null && list2 != null) {
return list2;
} else if (list1 != null && list2 == null) {
return list1;
}
ListNode head = new ListNode();
ListNode left = list1;
ListNode right = list2;
ListNode temp = head;
while (left != null && right != null) {
if (left.val < right.val) {
temp.next = left;
left = left.next;
} else {
temp.next = right;
right = right.next;
}
temp = temp.next;
}
if (left == null) {
temp.next = right;
} else if (right == null) {
temp.next = left;
}
return head.next;
}
方法2:(0ms 递归)
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if (list1 == null) {
return list2;
}
else if (list2 == null) {
return list1;
}
else if (list1.val < list2.val) {
list1.next = mergeTwoLists(list1.next, list2);
return list1;
}
else {
list2.next = mergeTwoLists(list1, list2.next);
return list2;
}
}
60. 随机链表的复制 ②
61. 反转链表II ② ×
给你单链表的头指针 head
和两个整数 left
和 right
,其中 left <= right
。请你反转从位置 left
到位置 right
的链表节点,返回 反转后的链表 。
示例 1:
输入:head = [1,2,3,4,5], left = 2, right = 4 输出:[1,4,3,2,5]
示例 2:
输入:head = [5], left = 1, right = 1 输出:[5]
提示:
- 链表中节点数目为
n
1 <= n <= 500
-500 <= Node.val <= 500
1 <= left <= right <= n
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
方法2:(0ms)
public ListNode reverseBetween(ListNode head, int m, int n) {
// 定义一个dummyHead, 方便处理
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
// 初始化指针
ListNode g = dummyHead;
ListNode p = dummyHead.next;
// 将指针移到相应的位置
for(int step = 0; step < m - 1; step++) {
g = g.next; p = p.next;
}
// 头插法插入节点
for (int i = 0; i < n - m; i++) {
ListNode removed = p.next;
p.next = p.next.next;
removed.next = g.next;
g.next = removed;
}
return dummyHead.next;
}
作者:贾卷积
链接:https://leetcode.cn/problems/reverse-linked-list-ii/solutions/138910/java-shuang-zhi-zhen-tou-cha-fa-by-mu-yi-cheng-zho/
62. K个一组翻转链表 ③
63. 删除链表的倒数第N个结点 ② √-
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1 输出:[]
示例 3:
输入:head = [1,2], n = 1 输出:[1]
提示:
- 链表中结点的数目为
sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
进阶:你能尝试使用一趟扫描实现吗?
方法1:(0ms)
public ListNode removeNthFromEnd(ListNode head, int n) {
if (head.next == null && n == 1){
return null;
}
int count = 1;
ListNode temp = head;
while (temp.next != null){
count++;
temp = temp.next;
}
if (n == count){
return head.next;
}else {
int cnt = 1;
temp = head;
while (cnt < count - n) {
cnt++;
temp = temp.next;
}
temp.next = temp.next.next;
return head;
}
}
方法2:(0ms)
解题思路:
整体思路是让前面的指针先移动 n 步,之后前后指针共同移动直到前面的指针到尾部为止
首先设立预先指针 pre
设预先指针 pre 的下一个节点指向 head,设前指针为 start,后指针为 end,二者都等于 pre
start 先向前移动n步
之后 start 和 end 共同向前移动,此时二者的距离为 n,当 start 到尾部时,end 的位置恰好为倒数第 n 个节点
因为要删除该节点,所以要移动到该节点的前一个才能删除,所以循环结束条件为 start.next != null
删除后返回 pre.next,为什么不直接返回 head 呢,因为 head 有可能是被删掉的点
时间复杂度:O(n)
作者:画手大鹏
链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/solutions/7803/hua-jie-suan-fa-19-shan-chu-lian-biao-de-dao-shu-d/
//通过快慢指针来解决,类似于你要删除中间元素的题
public ListNode removeNthFromEnd(ListNode head, int n) {
//定义一个伪节点,用于返回结果
ListNode dumpy = new ListNode(0);
dumpy.next = head;
//定义一个快指针,指向伪节点,用于遍历链表
ListNode prev = dumpy;
//定一个慢指针,
ListNode tail = dumpy;
//让快指针先移动 n 步
while(n >0){
prev = prev.next;
n--;
}
//只要快指针不指向空,就继续循环
while(prev.next !=null){
//让快慢指针同时移动
tail = tail.next;
prev = prev.next;
}
//这时慢指针移动到的位置就是,要删除节点的前一个节点
//所以只要删除当前节点的下一个节点
tail.next = tail.next.next;
//返回为指针指向的头节点
return dumpy.next;
}
64. 删除排序链表中的重复元素II ② √-
给定一个已排序的链表的头 head
, 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。
示例 1:
输入:head = [1,2,3,3,4,4,5] 输出:[1,2,5]
示例 2:
输入:head = [1,1,1,2,3] 输出:[2,3]
提示:
- 链表中节点数目在范围
[0, 300]
内 -100 <= Node.val <= 100
- 题目数据保证链表已经按升序 排列
方法1:(0ms)
public static ListNode deleteDuplicates(ListNode head) {
if (head == null || head.next == null){
return head;
}
ListNode pre = new ListNode(0);
pre.next = head;
ListNode newNode = pre;
while (head != null && head.next != null){
if (head.val != head.next.val) { // 用值做比较,而不是两个节点做比较,节点一定不相等
head = head.next;
pre = pre.next;
}else {
while (head.next != null && head.next.val == head.val){ // 用值做比较
head = head.next;
}
head = head.next;
pre.next = head;
}
}
return newNode.next;
}
解题思路:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
65. 旋转链表 ② √-
给你一个链表的头节点 head
,旋转链表,将链表每个节点向右移动 k
个位置。
示例 1:
输入:head = [1,2,3,4,5], k = 2 输出:[4,5,1,2,3]
示例 2:
输入:head = [0,1,2], k = 4 输出:[2,0,1]
提示:
- 链表中节点的数目在范围
[0, 500]
内 -100 <= Node.val <= 100
0 <= k <= 2 * 109
方法1:(1842ms)
public static ListNode rotateRight(ListNode head, int k) {
if (head == null || head.next == null || k == 0){
return head;
}
ListNode temp = head;
ListNode pre = new ListNode(0);
pre.next = head;
int cnt = 0;
while (cnt < k - 1){
if (temp.next == null){
temp = head;
}else {
temp = temp.next;
}
cnt++;
}
while (temp != null){
pre = pre.next;
temp = temp.next;
}
ListNode cur = pre;
while (pre.next != null){
pre = pre.next;
}
while (head != cur){
pre.next = new ListNode(head.val);
pre = pre.next;
head = head.next;
}
return cur;
}
方法2:(0ms)
public static ListNode rotateRight(ListNode head, int k) {
if(head == null || head.next == null || k <= 0) {
return head;
}
int n = 0;
ListNode p = head;
while(p != null) {
p = p.next;
n++;
}
k = k%n;
if(k == n || k == 0) {
return head;
}
ListNode slow = head , fast = head;
for(int i=0; i<n-1; i++) {
if(i >= k) {
slow = slow.next;
}
fast = fast.next;
}
ListNode ans = head;
ans = slow.next; //slow为新链表头结点的前一个节点
slow.next = null; //在旧链表中将新链表头结点与之前的节点斩断
fast.next = head; //旧链表的尾节点连接上旧链表的头结点
return ans;
}
66. 分隔链表 ② √
给你一个链表的头节点 head
和一个特定值 x
,请你对链表进行分隔,使得所有 小于 x
的节点都出现在 大于或等于 x
的节点之前。
你应当 保留 两个分区中每个节点的初始相对位置。
示例 1:
输入:head = [1,4,3,2,5,2], x = 3 输出:[1,2,2,4,3,5]
示例 2:
输入:head = [2,1], x = 2 输出:[1,2]
提示:
- 链表中节点的数目在范围
[0, 200]
内 -100 <= Node.val <= 100
-200 <= x <= 200
方法1:(0ms)
public static ListNode partition(ListNode head, int x) {
if (head == null || head.next == null){
return head;
}
ListNode pre = new ListNode(0);
pre.next = head;
ListNode temp = head;
ListNode newList = pre;
while (temp != null && temp.val < x){
temp = temp.next;
pre = pre.next;
}
if (temp == null){
return head;
}
ListNode cur = temp;
ListNode newPre = new ListNode(0);
newPre.next = temp;
ListNode newCur = cur;
while (cur != null){
if (cur.val < x){
newCur = cur.next;
pre.next = new ListNode(cur.val);
pre = pre.next;
cur = newCur;
newPre.next = cur;
}else {
cur = cur.next;
newPre = newPre.next;
}
}
pre.next = temp;
return newList.next;
}
方法2:(0ms):力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
public ListNode partition(ListNode head, int x) {
ListNode smlDummy = new ListNode(0), bigDummy = new ListNode(0);
ListNode sml = smlDummy, big = bigDummy;
while (head != null) {
if (head.val < x) {
sml.next = head;
sml = sml.next;
} else {
big.next = head;
big = big.next;
}
head = head.next;
}
sml.next = bigDummy.next;
big.next = null;
return smlDummy.next;
}
作者:Krahets
链接:https://leetcode.cn/problems/partition-list/solutions/2362068/86-fen-ge-lian-biao-shuang-zhi-zhen-qing-hha7/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。