- 1. 找出两个链表的交点
- 2. 链表反转
- 3. 归并两个有序的链表
- 4. 从有序链表中删除重复节点
- 5. 删除链表的倒数第 n 个节点
- 6. 交换链表中的相邻结点
- 7. 链表求和
- 8. 回文链表
- 9. 分隔链表
- 10. 链表元素按奇偶聚集
参考:https://github.com/CyC2018/CS-Notes
链表是空节点,或者有一个值和一个指向下一个链表的指针,因此很多链表问题可以用递归来处理。
1. 找出两个链表的交点
160. Intersection of Two Linked Lists (Easy)
例如以下示例中 A 和 B 两个链表相交于 c1:
A: a1 → a2
↘
c1 → c2 → c3
↗
B: b1 → b2 → b3
但是不会出现以下相交的情况,因为每个节点只有一个 next 指针,也就只能有一个后继节点,而以下示例中节点 c 有两个后继节点。
A: a1 → a2 d1 → d2
↘ ↗
c
↗ ↘
B: b1 → b2 → b3 e1 → e2
要求时间复杂度为 O(N),空间复杂度为 O(1)。如果不存在交点则返回 null。
设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a。
当访问 A 链表的指针访问到链表尾部时,令它从链表 B 的头部开始访问链表 B;同样地,当访问 B 链表的指针访问到链表尾部时,令它从链表 A 的头部开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。
如果不存在交点,那么 a + b = b + a,以下实现代码中 l1 和 l2 会同时为 null,从而退出循环。
/**
* 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 lenA = getLength(headA);
int lenB = getLength(headB);
if(lenA>lenB){
for(int i=0; i<lenA-lenB; i++)
headA = headA->next;
}else{
for(int j=0; j<lenB-lenA; j++)
headB = headB->next;
}
while(headA!=headB && headA && headB){
headA = headA->next;
headB = headB->next;
}
return (headA && headB) ? headA : NULL;
}
int getLength(ListNode* head){
int cur = 0;
while(head){
cur++;
head = head->next;
}
return cur;
}
// ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
// if(!headA || !headB) return NULL;
// ListNode *a=headA, *b=headB;
// while(a != b){
// a = a ? a->next : headB;
// b = b ? b->next : headA;
// }
// return a;
// }
};
如果只是判断是否存在交点,那么就是另一个问题,即 编程之美 3.6 的问题。有两种解法:
- 把第一个链表的结尾连接到第二个链表的开头,看第二个链表是否存在环;
- 或者直接比较两个链表的最后一个节点是否相同。
2. 链表反转
206. Reverse Linked List (Easy)
递归
// java版本
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode next = head.next;
ListNode newHead = reverseList(next);
next.next = head;
head.next = null;
return newHead;
}
头插法
/**
* 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) {
if(!head) return NULL;
ListNode* newHead = NULL;
while(head){
ListNode* t = head->next;
head->next = newHead;
newHead = head;
head = t;
}
return newHead;
}
};
3. 归并两个有序的链表
21. Merge Two Sorted Lists (Easy)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if(!l1) return l2;
if(!l2) return l1;
ListNode *t1=l1, *t2=l2;
ListNode* dummy = new ListNode(-1);
ListNode* res = dummy;
while(t1!=NULL && t2!=NULL){
if(t1->val <= t2->val){
res->next=t1;
t1 = t1->next;
}else{
res->next = t2;
t2 = t2->next;
}
res = res->next;
}
res->next = t1 ? t1 : t2;
res = dummy->next;
delete dummy;
return res;
}
};
4. 从有序链表中删除重复节点
83. Remove Duplicates from Sorted List (Easy)
Given 1->1->2, return 1->2.
Given 1->1->2->3->3, return 1->2->3.
/**
* 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* cur = head;
while(cur && cur->next){
if(cur->val == cur->next->val)
cur->next = cur->next->next;
else
cur = cur->next;
}
return head;
}
};
ListNode* deleteDuplicates(ListNode* head) {
// 递归解法
if(head==NULL || head->next==NULL) return head;
head->next = deleteDuplicates(head->next);
return head->val==head->next->val ? head->next : head;
}
5. 删除链表的倒数第 n 个节点
19. Remove Nth Node From End of List (Medium)
Given linked list: 1->2->3->4->5, and n = 2.
After removing the second node from the end, the linked list becomes 1->2->3->5.
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
//双指针,一次遍历
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy = new ListNode(0);
dummy->next = head;
ListNode *first = dummy;
ListNode *second = dummy;
for(int i=1; i<=n+1; i++){
first = first->next;
}
while(first){
first = first->next;
second = second->next;
}
second->next = second->next->next;
ListNode* res = dummy->next;
delete dummy;
return res;
}
};
class Solution {
public:
// 两次遍历
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy = new ListNode(0);
dummy->next = head;
int c = 0;
ListNode* cur = head;
while(cur){
++c;
cur = cur->next;
}
c -= n;
cur = dummy;
while(c--){
cur = cur->next;
}
cur->next = cur->next->next;
return dummy->next;
}
};
6. 交换链表中的相邻结点
24. Swap Nodes in Pairs (Medium)
Given 1->2->3->4, you should return the list as 2->1->4->3.
题目要求:不能修改结点的 val 值,O(1) 空间复杂度。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(head==NULL || head->next==NULL) return head;
ListNode* dummy = new ListNode(-1);
dummy->next = head;
ListNode* preNode = dummy;
while(head && head->next){
ListNode* firsyNode = head;
ListNode* secondNode = head->next;
// 交换节点
firsyNode->next = secondNode->next;
secondNode->next = firsyNode;
preNode->next = secondNode;
// 变换节点
preNode = firsyNode;
head = firsyNode->next;
}
ListNode* res = dummy->next;
delete dummy;
return res;
}
};
7. 链表求和
445. Add Two Numbers II (Medium)
Input: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 8 -> 0 -> 7
题目要求:不能修改原始链表。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
stack<int> l1s, l2s;
while(l1){
l1s.push(l1->val);
l1 = l1->next;
}
while(l2){
l2s.push(l2->val);
l2 = l2->next;
}
ListNode* dummy = new ListNode(-1);
int plus=0;
while(!l1s.empty() || !l2s.empty()||plus){
int num, sum, n1, n2;
if(!l1s.empty()) {
n1 = l1s.top();
l1s.pop();
}else{
n1 = 0;
}
if(!l2s.empty()){
n2 = l2s.top();
l2s.pop();
}else{
n2 = 0;
}
sum = n1 + n2 + plus;
num = sum%10;
cout<<sum<<endl;
plus = sum/10;
ListNode* cur = new ListNode(num);
cur->next = dummy->next;
dummy->next = cur;
// delete cur;
}
ListNode* res = dummy->next;
delete dummy;
return res;
}
};
8. 回文链表
234. Palindrome Linked List (Easy)
题目要求:以 O(1) 的空间复杂度来求解。
切成两半,把后半段反转,然后比较两半是否相等。
/**
* 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* fastNode = head;
ListNode* slowNode = head;
//寻找中间节点
while(fastNode && fastNode->next){
fastNode = fastNode->next->next;
slowNode = slowNode->next;
}
if(fastNode != NULL)
slowNode = slowNode->next;
// 截断第一个串
ListNode* cur = head;
while(cur->next!=slowNode){
cur = cur->next;
}
cur->next = NULL;
// 反转第二个串
ListNode* dummy = new ListNode(-1);
while(slowNode){
ListNode* tmp = slowNode->next;
slowNode->next = dummy->next;
dummy->next = slowNode;
slowNode = tmp;
}
// 判断是否相等
ListNode* newHead = dummy->next;
delete dummy;
while(head && newHead){
if(head->val!=newHead->val)
return false;
head = head->next;
newHead = newHead->next;
}
return true;
}
};
9. 分隔链表
725. Split Linked List in Parts(Medium)
Input:
root = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], k = 3
Output: [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
Explanation:
The input has been split into consecutive parts with size difference at most 1, and earlier parts are a larger size than the later parts.
题目描述:把链表分隔成 k 部分,每部分的长度都应该尽可能相同,排在前面的长度应该大于等于后面的。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
vector<ListNode*> splitListToParts(ListNode* root, int k) {
vector<ListNode*> res(k);
int len = 0;
ListNode* t = root;
while(t){
++len;
t = t->next;
}
int avg = len/k, ext = len%k;
for(int i=0; i<k && root; i++){
res[i] = root;
for(int j=1; j<avg+(i<ext); j++){
root = root->next;
}
t = root->next;
root->next = NULL;
root = t;
}
return res;
}
};
10. 链表元素按奇偶聚集
328. Odd Even Linked List (Medium)
给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。
请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。
Example:
Given 1->2->3->4->5->NULL,
return 1->3->5->2->4->NULL.
/**
* 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* oddEvenList(ListNode* head) {
if(!head) return head;
ListNode *odd = head, *even = head->next, *evenHead = even;
while(even && even->next){
odd->next = odd->next->next;
odd = odd->next;
even->next = even->next->next;
even = even->next;
}
odd->next = evenHead;
return head;
}
};