总结
- 有时候加个冗余头节点会大大降低实现难度,让代码变得更简洁。
- 做与链表相关的题目一定要在写代码前就想清楚算法的各个步骤,想好要保存哪些指针。否则很容易写乱。
- 要注意delete掉删除掉的节点,避免内存泄露。
Leetcode 2 Add Two Numbers
题意
用链表存储十进制数字的各位,现在给这样的两个链表,求和。
分析
由于链表头存的是最低位,所以我们可以同时扫描两个链表,模拟一下加法就行了。
假如用C++写的话,可以先开一个冗余头指针,方便实现。注意申请或释放内存时,应该要用new和delete而不要用malloc和free,养成良好的编程习惯。
假设两个链表的长度分别为n和m,那么时间复杂度和空间复杂度都是$O(n + m)$,显然空间复杂度可以优化到$O(1)$。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class Solution { public: ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { ListNode *dummy = new ListNode(0); ListNode *p = dummy; int carry = 0; while (l1 || l2) { int x = l1 ? l1->val : 0; int y = l2 ? l2->val : 0; int sum = x + y + carry; carry = sum / 10; p->next = new ListNode(sum % 10); p = p->next; if (l1) l1 = l1->next; if (l2) l2 = l2->next; } if (carry) p->next = new ListNode(1); p = dummy->next; delete dummy; return p; } };
|
Leetcode 19 Remove Nth Node From End of List
题意
给一个链表,要求删除倒数第n个节点。
分析
这道题的边界情况是删除链表首个节点。
我们用两个指针指向表头,然后让其中一个指针先走n步,假如走完n步后指向了空指针,说明删除的是表头。假如走完不是空指针,则让另外一个指针开始和它一步一步走,直到前面的指针指到了最后一个节点时,后面的指针刚好指向待删除节点的前一个节点。
这样做的时间复杂度是$O(n)$。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class Solution { public: ListNode* removeNthFromEnd(ListNode* head, int k) { if (head == nullptr || k <= 0) return nullptr; ListNode *behind = head, *ahead = head; for (int i = 0; i < k; i++) { ahead = ahead->next; } if (ahead == nullptr) { ListNode *temp = head; head = head->next; delete(temp); return head; } while (ahead->next != nullptr) { ahead = ahead->next; behind = behind->next; } ListNode *temp = behind->next; behind->next = behind->next->next; delete(temp); return head; } };
|
Leetcode 21 Merge Two Sorted Lists
题意
合并两个有序链表。
分析
直接写就行了,时间复杂度为$O(n+m)$,其中n和m为两个链表的长度。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Solution { public: ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { ListNode *dummy = new ListNode(0); ListNode *p = dummy; while(l1 && l2) { if (l1->val < l2->val) { p->next = l1; l1 = l1->next; } else { p->next = l2; l2 = l2->next; } p = p->next; }
p->next = l1 ? l1 : l2;
p = dummy->next; delete(dummy); return p; } };
|
Leetcode 23 Merge k Sorted Lists
题意
合并k个有序链表。
分析
我们需要一种数据结构来较快地插入一个数,取出最小的数,删除最小的数。不难想到优先队列可以满足这些要求。
假设k个链表的节点个数和为n,则时间复杂度为$O(nlogk)$。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| class Solution { private: struct Node { ListNode *p; int idx; Node(ListNode *p, int idx) { this->p = p; this->idx = idx; } bool operator < (const Node &rhs) const { return p->val > rhs.p->val; } }; public: ListNode* mergeKLists(vector<ListNode*>& lists) { priority_queue<Node> que; for (int i = 0; i < lists.size(); i++) { if (lists[i]) { que.push(Node(lists[i], i)); lists[i] = lists[i]->next; } } ListNode *dummy = new ListNode(0); ListNode *p = dummy; while(!que.empty()) { Node node = que.top(); que.pop(); if (lists[node.idx] != nullptr) { int idx = node.idx; que.push(Node(lists[idx], idx)); lists[idx] = lists[idx]->next; } p->next = node.p; p = p->next; } p = dummy->next; delete(dummy); return p; } };
|
Leetcode 24 Swap Nodes in Pairs
题意
两两交换链表的相邻节点。
分析
递归来写,分三种情况,当前节点是空节点,当前节点没有后继,当前节点有后继。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Solution { public: ListNode* swapPairs(ListNode* head) { if (head == nullptr) { return nullptr; } if (head->next == nullptr) { return head; } ListNode *newHead = head->next; ListNode *nex = head->next->next; newHead->next = head; head->next = swapPairs(nex); return newHead; } };
|
Leetcode 25 Reverse Nodes in k-Group
题意
是Leetcode 24的升级版,需要将链表的每k个节点翻转。
分析
用迭代来写,先加个冗余头节点,方便实现。需要记录k节点组的前一个节点p,第一个节点head,最后一个节点tail。然后,我们不断地将p的后一个节点扔到tail的后面即可。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| class Solution { public: ListNode* reverseKGroup(ListNode* head, int k) { if (head == nullptr || head->next == nullptr || k < 2) return head; ListNode *dummy = new ListNode(0); dummy->next = head; ListNode *p = dummy, *tail = dummy, *temp = nullptr; while (true) { int count = 0; while (count != k && tail != nullptr) { tail = tail->next; count++; } if (tail == nullptr) { break; } head = p->next; while (p->next != tail) { temp = p->next; p->next = temp->next; temp->next = tail->next; tail->next = temp; } p = tail = head; } p = dummy->next; delete(dummy); return p; } };
|
Leetcode 61 Rotate List
题意
回转链表,注意不是反转链表。
分析
分三步,首先求出链表长度以及最后一个节点,然后找到回转后的首个节点的前继,最后修改一下它们的指针即可。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class Solution { public: ListNode* rotateRight(ListNode* head, int k) { if (head == nullptr) return nullptr; ListNode *p = head, *lastNode; int len = 1; while (p->next) { len++; p = p->next; lastNode = p; } k %= len; if (k == 0) return head; k = len - k - 1; ListNode *pre = head; while (k--) { pre = pre->next; } lastNode->next = head; head = pre->next; pre->next = nullptr; return head; } };
|
Leetcode 82 Remove Duplicates from Sorted List II
题意
删除有序链表中的重复元素。
分析
申请个冗余节点会比较好实现点。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class Solution { public: ListNode* deleteDuplicates(ListNode* head) { ListNode *dummy = new ListNode(0); ListNode *p = dummy; while (head) { ListNode *p2 = head; while (p2->next && p2->next->val == head->val) { p2 = p2->next; } if (head != p2) { while (head != p2) { ListNode *temp = head->next; delete(head); head = temp; } head= p2->next; delete(p2); } else { p->next = head; p = p->next; head = head->next; p->next = nullptr; } } ListNode *temp = dummy->next; delete(dummy); return temp; } };
|
Leetcode 83 Remove Duplicates from Sorted List
题意
删除有序链表中的多余的重复元素,即要保证每个元素最多出现一次。
分析
直接扫一遍就行了,注意delete掉删掉的节点。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Solution { public: ListNode* deleteDuplicates(ListNode* head) { ListNode *cur = head; while (cur && cur->next) { if (cur->next->val == cur->val) { ListNode *temp = cur->next; cur->next = cur->next->next; delete(temp); } else { cur = cur->next; } } return head; } };
|
Leetcode 86 Partition List
留坑待填。