0%

链表之简单题合集

141. 环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false

示例 1:

img

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

img

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

img

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:

  • 链表中节点的数目范围是 [0, 104]
  • -105 <= Node.val <= 105
  • pos-1 或者链表中的一个 有效索引

C++

class Solution {
public:
bool hasCycle(ListNode *head) {
if(!head) return false;
ListNode* fast = head;
ListNode* slow = head;
// 不能只判断 slow -> next && fast -> next -> next 因为 如果 fast -> next 就为null的话,根本就没有 fast -> next -> next 这个东西
while(slow -> next && fast -> next && fast -> next -> next){
slow = slow -> next;
fast = fast -> next -> next;
if(slow == fast) return true;
}
return false;
}
};

142. 环形链表 II

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1:

img

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

img

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

img

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

提示:

  • 链表中节点的数目范围在范围 [0, 104]
  • -105 <= Node.val <= 105
  • pos 的值为 -1 或者链表中的一个有效索引

C++

class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(!head) return nullptr;
set<ListNode*> s;
ListNode* p = head;
while(p){
if(s.find(p) == s.end()) {
s.insert(p);
p = p -> next;
}
else return p;
}
return nullptr;
}
};

160. 相交链表

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构

示例 1:

img

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
— 请注意相交节点的值不为 1,因为在链表 A 和链表 B 之中值为 1 的节点 (A 中第二个节点和 B 中第三个节点) 是不同的节点。换句话说,它们在内存中指向两个不同的位置,而链表 A 和链表 B 中值为 8 的节点 (A 中第三个节点,B 中第四个节点) 在内存中指向相同的位置。

提示:

  • listA 中节点数目为 m
  • listB 中节点数目为 n
  • 1 <= m, n <= 3 * 104
  • 1 <= Node.val <= 105
  • 0 <= skipA <= m
  • 0 <= skipB <= n
  • 如果 listAlistB 没有交点,intersectVal0
  • 如果 listAlistB 有交点,intersectVal == listA[skipA] == listB[skipB]

C++

// 先统一位置,再同时往后遍历
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* p = headA;
int lenA = 0;
ListNode* q = headB;
int lenB = 0;
while(p){
lenA ++;
p = p -> next;
}
while(q){
lenB ++;
q = q -> next;
}

ListNode* temp1 = lenA >= lenB ? headA : headB;
ListNode* temp2 = lenA >= lenB ? headB : headA;
int dist = abs(lenA - lenB);
while(dist--){
if(!temp1 || !temp1 -> next) return nullptr;
temp1 = temp1 -> next;
}

while(temp1 && temp2){
if(temp1 == temp2) return temp1;
temp2 = temp2 -> next;
temp1 = temp1 -> next;
}
return nullptr;
}
};

86. 分隔链表

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你应当 保留 两个分区中每个节点的初始相对位置。

示例 1:

img

输入: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

C++

  • 维护两个链表,一个放小的,一个放大的
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
ListNode * headSmall = new ListNode(0, nullptr);
ListNode * headLarge = new ListNode(0, nullptr);
ListNode * p = headSmall;
ListNode * q = headLarge;
while(head){
if(head -> val < x){
p -> next = new ListNode(head -> val, nullptr);
p = p -> next;
}
else{
q -> next = new ListNode(head -> val, nullptr);
q = q -> next;
}
head = head -> next;
}
p -> next = headLarge -> next;
return headSmall -> next;
}
};

61. 旋转链表

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

示例 1:

img

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

示例 2:

img

输入:head = [0,1,2], k = 4
输出:[2,0,1]

提示:

  • 链表中节点的数目在范围 [0, 500]
  • -100 <= Node.val <= 100
  • 0 <= k <= 2 * 109

C++

// 整段取下接到前面
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if(!head || !head -> next) return head;
ListNode* dummy = new ListNode(0, head);
ListNode* p = dummy -> next;
int len = 0;
while(p){
len ++;
p = p -> next;
}
if(k % len == 0) return head; // 转 k 次 和转 (k % 链表长度) 次的效果一样
int cnt = len - k % len;
ListNode* pre = dummy -> next;
while(--cnt > 0) pre = pre -> next;
ListNode * tail = pre -> next;
while(tail -> next) tail = tail -> next;
tail -> next = dummy -> next;
dummy -> next = pre -> next;
pre -> next = nullptr;

return dummy -> next;
}
};
// 正确但超时
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if(!head || !head -> next) return head;
ListNode* dummy = new ListNode(0, head);
while(k-- > 0){
ListNode * pre = dummy;
while(pre -> next -> next && pre -> next){
pre = pre -> next;
}

pre -> next -> next = dummy -> next;
dummy -> next = pre -> next;
pre -> next = nullptr;
}
return dummy -> next;
}
};

19. 删除链表的倒数第 N 个结点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:

img

输入: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

C++

  • 双指针
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy = new ListNode(0, head);
ListNode* pre = dummy;
ListNode* cur = dummy;
while(n -- > 0){
cur = cur -> next;
}
while(cur -> next){
cur = cur -> next;
pre = pre -> next;
}
pre -> next = pre -> next -> next;
return dummy -> next;
}
};
  • 递归
class Solution {
public:
int cur = 0;
ListNode* removeNthFromEnd(ListNode* head, int n) {
if(!head) return nullptr;
head -> next = removeNthFromEnd(head -> next, n);
cur ++; // 不能传参数 n-- 递归,因为是倒数统计,从最后一个节点向前return
if(n == cur) return head -> next;
return head;
}
};

203. 移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点

示例 1:

img

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

示例 2:

输入:head = [], val = 1
输出:[]

C++

class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
if(!head) return nullptr;
if(head -> val == val) return removeElements(head -> next, val);
head -> next = removeElements(head -> next, val);
return head;
}
};

83. 删除排序链表中的重复元素

给定一个已排序的链表的头 head删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表

示例 1:

img

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

示例 2:

img

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

提示:

  • 链表中节点数目在范围 [0, 300]
  • -100 <= Node.val <= 100
  • 题目数据保证链表已经按升序 排列

C++

  • 非递归版
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if(!head) return nullptr;
ListNode* dummy = new ListNode(-101, head);
ListNode* pre = dummy;
ListNode* cur = head;
while(cur){
while(cur && cur -> val == pre -> val){
pre -> next = cur -> next;
cur = pre -> next;
}
if(!cur) break;
cur = cur -> next;
pre = pre -> next;
}
return dummy -> next;
}
};
  • 递归版
class Solution {
public:
int pre = -101;
ListNode* deleteDuplicates(ListNode* head) {
if(!head) return nullptr;
if(head -> val == pre) return deleteDuplicates(head -> next);
pre = head -> val;
head -> next = deleteDuplicates(head -> next);
return head;
}
};

82. 删除排序链表中的重复元素 II

给定一个已排序的链表的头 head删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表

示例 1:

img

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

示例 2:

img

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

提示:

  • 链表中节点数目在范围 [0, 300]
  • -100 <= Node.val <= 100
  • 题目数据保证链表已经按升序 排列

C++

  • 非递归
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if(!head) return nullptr;
ListNode* dummy = new ListNode(-101, head);
ListNode* cur = dummy;
ListNode* pre = dummy;
while(cur && cur -> next){
if(cur -> next ->val == cur -> val){
int val = cur -> val;
while(cur && cur -> val == val) cur = cur -> next;
pre -> next = cur;
}
else{
pre = cur;
cur = cur -> next;
}
}
return dummy -> next;
}
};
  • 递归

head 后面有值而且和 head 的值相等,那么就找到第一个不相等的节点为止,然后对它去递归

class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if(!head) return head;
if(head -> next && head -> val == head -> next -> val){
while(head -> next && head -> val == head -> next -> val){
head = head -> next;
}
return deleteDuplicates(head -> next);
}
head -> next = deleteDuplicates(head -> next);
return head;
}
};

328. 奇偶链表

给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。

第一个节点的索引被认为是 奇数第二个节点的索引为 偶数 ,以此类推。

请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。

你必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。

示例 1:

img

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

示例 2:

img

输入: head = [2,1,3,5,6,4,7]
输出: [2,3,6,7,1,5,4]

提示:

  • n == 链表中的节点数
  • 0 <= n <= 104
  • -106 <= Node.val <= 106

C++

class Solution {
public:
ListNode* oddEvenList(ListNode* head) {
if(!head || !head -> next) return head;
ListNode* odd = head;
ListNode* evenHead = head -> next;
ListNode* even = evenHead;

while(even && even -> next){ // 等价于 odd && odd -> next && even && even -> next
odd -> next = even -> next;
odd = odd -> next;
even -> next = odd -> next;
even = even -> next;
}
odd -> next = evenHead;
return head;
}
};