【leetcode】链表相关题目思路总结(更新中)
简单题
206. 反转链表
剑指 Offer 24. 反转链表
https://leetcode-cn.com/problems/reverse-linked-list/
https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof/submissions/
题目描述:
反转链表。
解题思路:
借助几个临时指针。一个prev,一个curr,一个temp,然后不断做赋值互换即可。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* prev = nullptr;
ListNode* curr = head;
ListNode* temp;
while(curr) {
temp = curr->next;
curr->next = prev;
prev = curr;
curr = temp;
}
return prev;
}
};
21. 合并两个有序链表
https://leetcode-cn.com/problems/merge-two-sorted-lists/
题目描述:
合并两个有序链表。
解题思路:
比较两个链表的头指针的val大小,然后不断串联起来。
代码:
/**
* 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* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode rst(0);
ListNode* temp = &rst;
while(l1 && l2) {
if (l1->val < l2->val) {
temp->next = l1;
l1 = l1->next;
} else {
temp->next = l2;
l2 = l2->next;
}
temp = temp->next;
}
if (l1) {
temp->next = l1;
}
if (l2) {
temp->next = l2;
}
return rst.next;
}
};
面试题 02.07. 链表相交
剑指 Offer 52. 两个链表的第一个公共节点
160. 相交链表
https://leetcode-cn.com/problems/intersection-of-two-linked-lists-lcci/
https://leetcode-cn.com/problems/liang-ge-lian-biao-de-di-yi-ge-gong-gong-jie-dian-lcof/
https://leetcode-cn.com/problems/intersection-of-two-linked-lists/
题目描述:
两个长度不同的链表,从某个节点开始相交,后面的都是一样的。
解题思路:
先将两个链表长度对齐,然后遍历比较,找到的第一个一样的就是所求。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int len_list1 = 0;
int len_list2 = 0;
ListNode* tempA;
ListNode* tempB;
tempA = headA;
while(tempA) {
tempA = tempA->next;
len_list1 += 1;
}
tempB = headB;
while(tempB) {
tempB = tempB->next;
len_list2 += 1;
}
tempA = headA;
tempB = headB;
while (len_list2 < len_list1) {
tempA = tempA->next;
len_list1 -= 1;
}
while(len_list1 < len_list2) {
tempB = tempB->next;
len_list2 -= 1;
}
for (int i = 0; i < len_list1; i++) {
if (tempA == tempB) {
return tempA;
}
tempA = tempA->next;
tempB = tempB->next;
}
return nullptr;
}
};
面试题 02.01. 移除重复节点
https://leetcode-cn.com/problems/remove-duplicate-node-lcci/
题目描述:
将链表中的重复元素删除,并不改变链接顺序。
解题思路:
方法一,使用哈希表,时间空间复杂度均为O(n)。
方法二,循环遍历,时间复杂度O(n^2),空间O(1)。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* removeDuplicateNodes(ListNode* head) {
set<int> data;
ListNode* temp = head;
ListNode* temp2 = new ListNode(0);
ListNode* rst = temp2;
while(temp) {
if (data.find(temp->val) == data.end()) {
data.insert(temp->val);
temp2->next = temp;
temp2 = temp2->next;
}
temp = temp->next;
}
temp2->next = nullptr;
return rst->next;
}
};
剑指 Offer 22. 链表中倒数第k个节点
面试题 02.02. 返回倒数第k个节点
https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/
https://leetcode-cn.com/problems/kth-node-from-end-of-list-lcci/
题目描述:
找出链表的倒数第k个节点。
解题思路:
用双指针,一个比另外一个快k步。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* getKthFromEnd(ListNode* head, int k) {
ListNode* fast;
ListNode* slow;
int i = 0;
fast = head;
slow = head;
while(fast && i < k) {
fast = fast->next;
i++;
}
if (i < k) {
return nullptr;
}
while(fast) {
fast = fast->next;
slow = slow->next;
}
return slow;
}
};
234. 回文链表
面试题 02.06. 回文链表
https://leetcode-cn.com/problems/palindrome-linked-list/
https://leetcode-cn.com/problems/palindrome-linked-list-lcci/
题目描述:
判断链表是否对称。
解题思路:
方法一,将数据复制到数组中,然后双指针。
方法二,将后半部分链表反转,然后再遍历判断,之后再将后半部分恢复。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool isPalindrome(ListNode* head) {
if (!head) {
return true;
}
ListNode* fast;
ListNode* slow;
ListNode* prev;
fast = head;
slow = head;
while(fast) {
fast = fast->next;
if (fast) {
fast = fast->next;
}
prev = slow;
slow = slow->next;
}
ListNode* save_ptr = reverseList(slow);
ListNode* rev_ptr = save_ptr;
ListNode* ptr = head;
while(rev_ptr && ptr) {
if (rev_ptr->val != ptr->val) {
return false;
}
rev_ptr = rev_ptr->next;
ptr = ptr->next;
}
ListNode* now_ptr = reverseList(save_ptr);
prev->next = now_ptr;
return true;
}
ListNode* reverseList(ListNode* head) {
ListNode* prev = nullptr;
ListNode* curr = head;
while(curr) {
ListNode* temp = curr->next;
curr->next = prev;
prev = curr;
curr = temp;
}
return prev;
}
};
面试题 02.03. 删除中间节点
237. 删除链表中的节点
https://leetcode-cn.com/problems/delete-middle-node-lcci/
https://leetcode-cn.com/problems/delete-node-in-a-linked-list/
题目描述:
删除链表中的某个节点。
解题思路:
假设链表为A->B->C->D,删除B,将B变为C即可,改变B的next,val。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
void deleteNode(ListNode* node) {
node->val = node->next->val;
node->next = node->next->next;
}
};
83. 删除排序链表中的重复元素
https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/
题目描述:
删除链表中的重复元素。
解题思路:
判断当前节点和下一个节点的值是否相等,相等则将当前节点的next域直接指向下下个节点(就是丢掉下个节点)。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
ListNode* rst = head;
while(head && head->next) {
if (head->val == head->next->val) {
head->next = head->next->next;
} else {
head = head->next;
}
}
return rst;
}
};
剑指 Offer 06. 从尾到头打印链表
https://leetcode-cn.com/problems/cong-wei-dao-tou-da-yin-lian-biao-lcof/
题目描述:
逆序打印链表值。
解题思路:
借助栈搞定。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
stack<int> data;
vector<int> rst;
while(head) {
data.push(head->val);
head = head->next;
}
while(!data.empty()) {
rst.push_back(data.top());
data.pop();
}
return rst;
}
};
203. 移除链表元素
剑指 Offer 18. 删除链表的节点
https://leetcode-cn.com/problems/remove-linked-list-elements/
https://leetcode-cn.com/problems/shan-chu-lian-biao-de-jie-dian-lcof/
题目描述:
删除链表中和给定值相同的元素。
解题思路:
因为头结点也可能会被删除,所以应当设置一个哨兵指针。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* pre = new ListNode(0);
ListNode* save = pre;
pre->next = head;
while(pre->next) {
if (pre->next->val == val) {
pre->next = pre->next->next;
} else {
pre = pre->next;
}
}
return save->next;
}
};
1474. 删除链表 M 个节点之后的 N 个节点
https://leetcode-cn.com/problems/delete-n-nodes-after-m-nodes-of-a-linked-list/
题目描述:
先保留M个,再删掉N个,再保留M个,再删掉N个,依次类推。
解题思路:
用哨兵节点,然后不断循环处理,直到链表末尾。
代码:
/**
* 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* deleteNodes(ListNode* head, int m, int n) {
ListNode* pre = new ListNode(0);
pre->next = head;
ListNode* save = pre;
while(pre->next) {
int temp_m = m;
int temp_n = n;
while(pre->next && temp_m > 0) {
pre = pre->next;
temp_m -= 1;
}
while(pre->next && temp_n > 0) {
pre->next = pre->next->next;
temp_n -= 1;
}
}
return save->next;
}
};
876. 链表的中间结点
https://leetcode-cn.com/problems/middle-of-the-linked-list/
题目描述:
求链表最中间的节点,如果有两个,则选择后面一个。
解题思路:
采用快慢指针法最好。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* middleNode(ListNode* head) {
ListNode* fast = new ListNode(0);
ListNode* slow = new ListNode(0);
fast->next = slow->next = head;
while(fast->next) {
fast = fast->next;
if (fast->next) {
fast = fast->next;
slow = slow->next;
}
}
return slow->next;
}
};
1290. 二进制链表转整数
https://leetcode-cn.com/problems/convert-binary-number-in-a-linked-list-to-integer/
题目描述:
用链表表示二进制的每位数字,要求转成10进制。
解题思路:
模拟题,遍历一遍,一边遍历一边转即可。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
int getDecimalValue(ListNode* head) {
int sum = 0;
while(head) {
sum = sum*2 + head->val;
head = head->next;
}
return sum;
}
};
141. 环形链表
https://leetcode-cn.com/problems/linked-list-cycle/
题目描述:
判断一个链表是否有环。
解题思路:
快慢指针法,如果快慢指针跑着跑着跑到同一个节点了,那么必然是有环。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
ListNode* fast = new ListNode(0);
ListNode* slow = new ListNode(0);
fast->next = head;
slow->next = head;
while(fast->next) {
fast = fast->next;
if (fast == slow) {
return true;
}
if (fast->next) {
fast = fast->next;
slow = slow->next;
}
}
return false;
}
};
中等题
1669. 合并两个链表
https://leetcode-cn.com/problems/merge-in-between-linked-lists/
题目描述:
将其中一个链表删去一段,然后接上另外一个链表。
解题思路:
把握关键节点,两个连接处的左右两个节点,要加以保存,然后连接即可。
代码:
/**
* 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* mergeInBetween(ListNode* list1, int a, int b, ListNode* list2) {
ListNode* pre = new ListNode(0);
pre->next = list1;
ListNode* save = pre;
ListNode* left;
ListNode* right;
int i = 0;
while(pre->next) {
if (i == a) {
left = pre;
}
if (i == b) {
right = pre->next->next;
}
i += 1;
pre = pre->next;
}
left->next = list2;
pre = new ListNode(0);
pre->next = list2;
while(pre->next) {
pre = pre->next;
}
pre->next = right;
return save->next;
}
};
92. 反转链表 II
https://leetcode-cn.com/problems/reverse-linked-list-ii/
题目描述:
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
解题思路:
除了m和n位置的节点外,m-1和n+1位置的节点也要保存,这样的话,就退化为了简单版本的反转链表,最后接起来就可以了。
注意m-1为空的情况,即从第一个节点开始逆置。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
int change_len = n - m + 1; // 需要逆序的长度
ListNode* result = head; // 最终返回
// ... pre_head head ...
ListNode* pre_head = NULL;
while(head && --m) {
pre_head = head;
head = head->next;
}
// ... modify_list_tail head ...
ListNode* modify_list_tail = head;
ListNode* new_head = NULL;
// head - next - -
// new_head - - -
while(head && change_len) {
ListNode* next = head->next;
head->next = new_head;
new_head = head;
head = next;
change_len--;
}
modify_list_tail->next = head;
if (pre_head) {
// 并非从第一个节点开始逆序
pre_head->next = new_head;
} else {
result = new_head;
}
return result;
}
};
142. 环形链表 II
面试题 02.08. 环路检测
https://leetcode-cn.com/problems/linked-list-cycle-ii/
https://leetcode-cn.com/problems/linked-list-cycle-lcci/
题目描述:
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
解题思路:
这道题和环形链表的简单题思路大体相同,只不过最后要返回入环点。
有两种方法,一是直接用set,二是用快慢指针。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
ListNode* meet = NULL;
while(fast) {
fast = fast->next;
slow = slow->next;
if (!fast) {
return NULL;
}
fast = fast->next;
if (fast == slow) {
meet = fast;
break;
}
}
if (!meet) {
return NULL;
}
while (head && meet) {
if (head == meet) {
return meet;
}
head = head->next;
meet = meet->next;
}
return NULL;
}
};
86. 分隔链表
面试题 02.04. 分割链表
https://leetcode-cn.com/problems/partition-list/
https://leetcode-cn.com/problems/partition-list-lcci/
题目描述:
给你一个链表和一个特定值 x ,请你对链表进行分隔,使得所有小于 x 的节点都出现在大于或等于 x 的节点之前。
你应当保留两个分区中每个节点的初始相对位置。
解题思路:
模拟法,使用两个临时链表,分别保存比x大的,和比x小的,最后接起来就行。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
ListNode less_head(0);
ListNode more_head(0);
ListNode* less_ptr = &less_head;
ListNode* more_ptr = &more_head;
while(head) {
if (head->val < x) {
less_ptr->next = head;
less_ptr = less_ptr->next;
} else {
more_ptr->next = head;
more_ptr = more_ptr->next;
}
head = head->next;
}
less_ptr->next = more_head.next;
more_ptr->next = NULL;
return less_head.next;
}
};
138. 复制带随机指针的链表
剑指 Offer 35. 复杂链表的复制
https://leetcode-cn.com/problems/copy-list-with-random-pointer/
https://leetcode-cn.com/problems/fu-za-lian-biao-de-fu-zhi-lcof/
题目描述:
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
解题思路:
保存节点地址-序号的映射(map),序号-节点地址的映射(vector),遍历两遍,就完成了链表复制。注意其中random指针为空的情形加判断条件。
代码:
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
map<Node*, int> node_map;
vector<Node*> node_vec;
Node* ptr = head;
int i = 0;
while(ptr) {
node_vec.push_back(new Node(ptr->val));
node_map[ptr] = i;
i++;
ptr = ptr->next;
}
node_vec.push_back(0);
ptr = head;
i = 0;
while(ptr) {
node_vec[i]->next = node_vec[i+1];
if (ptr->random) {
node_vec[i]->random = node_vec[node_map[ptr->random]];
}
ptr = ptr->next;
i++;
}
return node_vec[0];
}
};
困难题
23. 合并K个升序链表
https://leetcode-cn.com/problems/merge-k-sorted-lists/
题目描述:
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
解题思路:
方法一:暴力解法,依次合并链表,(n+n)+(n+2n)+...+[n+(k-1)n],时间复杂度为O(nk^2),空间复杂度O(1)。
方法二:先放到vector里,排序,然后再构造链表,knlog(kn)+kn,时间复杂度O(knlogkn),空间复杂度O(kn)。
方法三:两两合并,分治法。2nk/2+4nk/4+8nk/8+...,时间复杂度O(knlogk),空间复杂度O(logk),递归用到的栈空间。
方法四:使用优先级队列,始终维护k个数,每次取出最小值后,将其后的节点插入。时间复杂度O(kn*logk),空间复杂度O(k)。
代码:
/**
* 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* mergeKLists(vector<ListNode*>& lists) {
if (lists.size() == 0) {
return NULL;
}
if (lists.size() == 1) {
return lists[0];
}
if (lists.size() == 2) {
return mergeTwoLists(lists[0], lists[1]);
}
int mid = lists.size() / 2;
vector<ListNode*> lists_vec1;
vector<ListNode*> lists_vec2;
for (int i = 0; i < mid; i++) {
lists_vec1.push_back(lists[i]);
}
for (int i = mid; i < lists.size(); i++) {
lists_vec2.push_back(lists[i]);
}
ListNode* lists1 = mergeKLists(lists_vec1);
ListNode* lists2 = mergeKLists(lists_vec2);
return mergeTwoLists(lists1, lists2);
}
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode temp_head(0);
ListNode* pre = &temp_head;
while(list1 && list2) {
if (list1->val < list2->val) {
pre->next = list1;
list1 = list1->next;
} else {
pre->next = list2;
list2 = list2->next;
}
pre = pre->next;
}
if (list1) {
pre->next = list1;
}
if (list2) {
pre->next = list2;
}
return temp_head.next;
}
};