1. 力扣141:环形链表
1.1 题目:
给你一个链表的头节点 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)
(即,常量)内存解决此问题吗?
1.2 思考
采用快慢指针法,快指针一次走2步,慢指针一次走一步,如果快慢指针能够相遇,就说明链表环,否则,在快指针遍历完了,二者是不可能相遇的。
拿scala刷题感觉有点唐。
1.3:题解
/**
* Definition for singly-linked list.
* class ListNode(var _x: Int = 0) {
* var next: ListNode = null
* var x: Int = _x
* }
*/
object Solution {
def hasCycle(head: ListNode): Boolean = {
if(head == null){
false
} else{
var (fast, slow) = (head, head)
while(fast.next != null && fast.next.next != null){
fast = fast.next.next
slow = slow.next
if(slow == fast){
return true
}
}
false
}
}
}
2. 力扣142:环形链表2
2.1 题目:
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0 输出:返回索引为 0 的链表节点 解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1 输出:返回 null 解释:链表中没有环。
提示:
- 链表中节点的数目范围在范围
[0, 104]
内 -105 <= Node.val <= 105
pos
的值为-1
或者链表中的一个有效索引
进阶:你是否可以使用 O(1)
空间解决此题?
2.2 思路:
传说中的龟兔赛跑法。
2.3 题解:
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head == null){
return null;
}
ListNode fast = head;
ListNode slow = head;
boolean flag = false;
while(fast.next != null && fast.next.next != null){
fast = fast.next.next;
slow = slow.next;
// 如果快慢指针相遇,说明是有环的,更新flag
if(fast == slow){
flag = true;
break;
}
}
// 如果有环的情况,进入到下面的if语句
if(flag){
slow = head;
// 慢指针回到头节点
// 再次相遇时,指向的节点就是入环的第一个节点
while(slow != fast){
slow = slow.next;
fast = fast.next;
}
return slow;
}
return null;
}
}
3. 力扣143:重排链表
3.1 题目:
给定一个单链表 L
的头节点 head
,单链表 L
表示为:
L0 → L1 → … → Ln - 1 → Ln
请将其重新排列后变为:
L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 1:
输入:head = [1,2,3,4] 输出:[1,4,2,3]
示例 2:
输入:head = [1,2,3,4,5] 输出:[1,5,2,4,3]
提示:
- 链表的长度范围为
[1, 5 * 104]
1 <= node.val <= 1000
3.2 思路:
看注释。
3.3 题解:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public void reorderList(ListNode head) {
// 快慢指针,慢指针定位到中间节点,然后将一个链表以分为二
// 记录一下第二个链表的头节点,然后遍历到最后一个节点,然后开始将最后一个节点插入到第一个链表中
ListNode fast = head;
ListNode slow = head;
// 因为链表的长度至少是1,所以不用额外判断head节点为空的情况
while(fast.next != null && fast.next.next != null){
fast = fast.next.next;
slow = slow.next;
}
// 将一个链表一分为2,并将slow的下一个节点作为第二个链表的首元节点
// 因为第二个链表的头节点也有可能被移除
// 所以需要哨兵节点
ListNode next = slow.next;
// 将两个链表断开
slow.next = null;
ListNode dummy = new ListNode(10086, next);
// 规律:在p指针的next处插入第二个链表的尾节点
// 直到dummy.next==null
ListNode p = head;
while(dummy.next != null){
// 循环条件限制了node不可能为第二个链表的哨兵节点
ListNode node = tailNode(dummy);
// 从p的后面插入
node.next = p.next;
p.next = node;
// 这里为什么可以直接大胆的将后挪一个位置呢?不怕下一轮循环p为null,p.next报错吗
// 因为第一个链表的长度大于等于第二个链表的长度
// 所以如果dummy.next != null,说明第二个链表还没被遍历完
// 所以p肯定还有next节点的,然后又向p节点后插入了一个节点,所以只要
//dummy.next != null,则p后面肯定有不少于两个节点
p = p.next.next;
}
}
private ListNode tailNode(ListNode dummy) {
ListNode p = dummy;
ListNode parent = null;
while(p.next != null){
parent = p;
p = p.next;
}
// 删掉尾巴节点
if(parent != null){
parent.next = null;
}
// 不可能返回哨兵节点,因为返回链表的最后一个节点的时候,压根不会进入循环
return p;
}
}