(单)链表是由节点和指针构成的数据结构,每个节点存有一个值,和一个指向下一个节点 的指针,因此很多链表问题可以用递归来处理。不同于数组,链表并不能直接获取任意节点的值, 必须要通过指针找到该节点后才能获取其值。同理,在未遍历到链表结尾时,我们也无法知道链 表的长度,除非依赖其他数据结构储存长度。LeetCode 默认的链表表示方法如下。
- struct ListNode {
- int val;
- ListNode *next;
- ListNode(int x) : val(x), next(nullptr) {}
- };
由于在进行链表操作时,尤其是删除节点时,经常会因为对当前节点进行操作而导致内存或 指针出现问题。有两个小技巧可以解决这个问题:一是尽量处理当前节点的下一个节点而非当前 节点本身,二是建立一个虚拟节点 (dummy node),使其指向当前链表的头节点,这样即使原链表 所有节点全被删除,也会有一个 dummy 存在,返回 dummy->next 即可。
一般来说,算法题不需要删除内存。在刷 LeetCode 的时候,如果想要删除一个节点,可以 直接进行指针操作而无需回收内存。实际做软件工程时,对于无用的内存,笔者建议尽量显式回 收,或利用智能指针。
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
链表翻转是非常基础也一定要掌握的技能。我们提供了两种写法——递归和非递归,且我们建议你同时掌握这两种写法。
递归的写法为:
- class Solution {
- public:
- ListNode* reverseList(ListNode* head, ListNode* prev = nullptr) {
- if(!head){
- return prev;
- }
- ListNode* next = head->next;
- head->next = prev;
- return reverseList(next, head);
- }
- };
非递归的写法为:
- class Solution {
- public:
- ListNode* reverseList(ListNode* head) {
- ListNode* prev = nullptr, *next;
- while(head){
- next = head->next;
- head->next = prev;
- prev = head;
- head = next;
- }
- return prev;
- }
- };
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
我们提供了递归和非递归,共两种写法。
递归的写法为:
- /**
- * 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* list1, ListNode* list2) {
- if(!list1){
- return list2;
- }
- if(!list2){
- return list1;
- }
- if(list1->val > list2->val){
- list2->next = mergeTwoLists(list1, list2->next);
- return list2;
- }else{
- list1->next = mergeTwoLists(list1->next, list2);
- return list1;
- }
- }
- };
非递归的写法为:
- /**
- * 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* list1, ListNode* list2) {
- ListNode* dummy = new ListNode(0), *node = dummy;
- while(list1 && list2){
- if(list1->val > list2->val){
- node->next = list2;
- list2 = list2->next;
- }else{
- node->next = list1;
- list1 = list1->next;
- }
- node = node->next;
- }
- node->next = list1? list1: list2;
- return dummy->next;
- }
- };
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
利用指针进行交换操作,没有太大难度,但一定要细心。
- /**
- * 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* swapPairs(ListNode* head) {
- ListNode* p = head, *s;
- if(p && p->next){
- s = p->next;
- p->next = s->next;
- s->next = p;
- head = s;
- while(p->next && p->next->next){
- s = p->next->next;
- p->next->next = s->next;
- s->next = p->next;
- p->next = s;
- p = s->next;
- }
- }
- return head;
- }
- };
160. Intersection of Two Linked Lists
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
。
假设链表 A 的头节点到相交点的距离是 a,链表 B 的头节点到相交点的距离是 b,相交点 到链表终点的距离为 c。我们使用两个指针,分别指向两个链表的头节点,并以相同的速度前进, 若到达链表结尾,则移动到另一条链表的头节点继续前进。按照这种前进方法,两个指针会在 a + b + c 次前进后同时到达相交节点。
- /**
- * 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) {
- ListNode* l1 = headA, *l2 = headB;
- while(l1 != l2){
- l1 = l1? l1->next: headB;
- l2 = l2? l2->next: headA;
- }
- return l1;
- }
- };
给你一个单链表的头节点 head
,请你判断该链表是否为回文链表。如果是,返回 true
;否则,返回 false
。
以 O(1) 的空间复杂度,判断链表是否回文。
先使用快慢指针找到链表中点,再把链表切成两半;然后把后半段翻转;最后比较两半是否相等。
- /**
- * 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:
- // 主函数
- bool isPalindrome(ListNode* head) {
- if(!head || !head->next){
- return true;
- }
- ListNode* slow = head, *fast = head;
- while(fast->next && fast->next->next){
- slow = slow->next;
- fast = fast->next->next;
- }
- slow->next = reverseList(slow->next);
- slow = slow->next;
- while(slow){
- if(head->val != slow->val){
- return false;
- }
- head = head->next;
- slow = slow->next;
- }
- return true;
- }
- // 辅函数 - 链表翻转
- ListNode* reverseList(ListNode* head){
- ListNode* prev = nullptr, *next;
- while(head){
- next = head->next;
- head->next = prev;
- prev = head;
- head = next;
- }
- return prev;
- }
- };
83. Remove Duplicates from Sorted List
19. Remove Nth Node From End of List
欢迎大家共同学习和纠正指教