算法专题 | 链表(再也不用担心面试的链表题了)

阅读更多文章,请看学习笔记汇总

做题方法

链表题不难,只要你在纸上画个图就会很简单。把思路模拟一遍,主要代码先写在纸上,不要急着敲代码。你会发现在纸上写一下,链表题会特别简单

  struct ListNode {
      int val;
      ListNode *next;
      ListNode(int x) : val(x), next(nullptr) {} // 初始化列表法
  };

常用代码

  • 虚拟结点
    对于可能删除头结点或者返回值不是你期望的head时,我们通常会增加一个虚拟结点
ListNode* dummy = new ListNode(-1);
或
ListNode* dummy = new ListNode({-1,nullptr});
  • 求链表长度
int n = 0;
for (auto p = head; p; p = p->next) n ++;
  • 结点为空或1
if(!head || !head->next) return head;
  • 删除后一个结点
p->next = p->next->next;
  • 结构体指针后移一位
p = p->next; (类似i++
  • 找到最后一个结点
while(p->next) {
	p = p->next;
}
  • 判断是否成环+计算环状链表的结点数
    LeetCode141改进版代码 传送门

计算结点数的方法:让一个指针再走一圈,回到相遇的位置

class Solution {
public:
    bool hasCycle(ListNode *head) {
        int cnt = 1;
        auto fast = head, slow = head;
        while(fast){
            fast = fast->next;
            slow = slow->next;
            if (fast) fast = fast->next;
            else return false;
            
            // Counting junction points
            if (fast == slow) {
                auto tmp = fast;
                fast = fast->next;
                while(fast != tmp) {
                    fast = fast->next;
                    cnt ++;
                }
                cout << cnt << endl;
                
                return true;   
            }
        }
        
        return false;
    }
};

  • 判断环状链表成环的结点位置
    LeetCode142.环状链表II

@单链表的快速创建、反向输出、翻转链表、删除

LeetCode上面的链表都是它事先创建好的,那如果我想自己创建一个链表,然后再去试验各种链表操作函数,则可以用如下代码模版

#include <iostream>
#include <initializer_list>

using namespace std;

// 定义链表结点
typedef struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(nullptr) {}
} ListNode;

// 创建单链表(返回头结点)
ListNode *create_linkedlist(initializer_list<int> lst) {
    auto iter = lst.begin();
    ListNode *head = lst.size() ? new ListNode(*iter++) : nullptr;
    for (ListNode *cur = head; iter != lst.end(); cur = cur->next)
        cur->next = new ListNode(*iter++);
    return head;
}

// 单链表正向输出(递归)
void printList(ListNode *head) {
    if (head == nullptr) return;
    cout << head->val << " ";
    printList(head->next);
}

// 单链表反向输出(递归)
void printReverse(ListNode *head) {
    if (head == nullptr) return;
    printReverse(head->next);
    cout << head->val << " ";
}

// 删除单链表
void clear(ListNode *head) {
    while (head) {
        ListNode *del = head;
        head = head->next;
        delete del;
    }
}

// 翻转链表
ListNode *reverseList(ListNode *head) {
    auto a = head, b = head;
    if (!a || !a->next) return a;
    b = a->next;
    while(b) {
        auto c = b->next;
        b->next = a;
        a = b;
        b = c;
    }

    head->next = NULL;
    head = a;
    return head;
}


int main() {
    ListNode *head = create_linkedlist({1, 4, 5, 3, 2, 6, 7});
    printList(head);
    cout << endl;
    printList(reverseList(head));
    clear(head);
}

LeetCode链表题

2.两数相加(虚拟结点)

在这里插入图片描述
技巧:用一个变量t去控制进位的1

/**
 *    @Author: Wilson79
 *    @Datetime: 2019年12月28日 星期六 14:40:26
 *    @Filename: 2.两数相加.cpp
 */

/**
 * 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) {
        int t = 0;
        auto res = new ListNode(-1); // 创建一个虚拟结点
        auto q = res;

        while(l1 || l2 || t) { // 加个t是解决5 + 5的情况
            if (l1) {
                t += l1->val;
                l1 = l1->next;
            }
            if (l2) {
                t += l2->val;
                l2 = l2->next;
            }
            auto p = new ListNode(t % 10);
            t /= 10;
            q -> next = p;
            q = p;
        }
        
        q->next = NULL;

        return res->next;
    }
};

143.重排链表

在这里插入图片描述
分析:一开始图没画清楚,害我焦虑地调试了1个小时才做出来

// 2019年12月25日 星期三 21:51:19
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void reorderList(ListNode* head) {
        if (!head) return;
        auto fast = head, slow = head;
        // 找中点
        // 1 2 [3] 4
        // 1 2 [3] 4 5
        while(fast && fast->next) {
            fast = fast->next->next;
            slow = slow->next;
        }
        
        // slow 中点
        
        // 翻转链表
        auto a = slow;
        auto b = slow->next;
        while(b) {
            auto c = b->next;
            b->next = a;
            a = b;
            b = c;
        }
        
        // 1->2->3<-4
        // 1->2->3<-4<-5
        auto p = head, q = a;
        while(p != q && p->next != q) {
            auto t = q->next;
            q->next = p->next;
            p->next = q;
            p = p->next->next;
            q = t;
        }
        slow->next = NULL; // 最终链表尾结点
        
    }
};

876.链表的中点

画图很清楚
在这里插入图片描述

/**
 * 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) {
        auto fast = head, slow = head;
        
        // odd-even 快慢指针法找链表的中点
        while(fast && fast->next) {
            fast = fast->next->next;
            slow = slow->next;
        }
        
        return slow;
    }
};

21.合并两个有序链表(递归)

/**
 * 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) {
        // Recursion
        // 1 2 5 NULL
        //   3 4 7 NULL    
        if (l1 == nullptr) return l2;
        if (l2 == nullptr) return l1;

        // 可以理解为递归函数已经给你做好了
        if (l1->val < l2->val) {
            l1->next = mergeTwoLists(l1->next, l2);
            return l1;
        }
        else {
            l2->next = mergeTwoLists(l1, l2->next);
            return l2;
        }
        
        return nullptr;
    }
};

234.回文链表

原题链接
在这里插入图片描述
时空复杂度要求较高,先用快慢指针找出中点,然后逆序后半段,再依次比较即可

/**
 * 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) {
        // 1 2 3 3 2 1 NULL
        if (!head || !head->next) return true;
        auto fast = head, slow = head, p = head;
        while(fast) {
            slow = slow->next;
            fast = fast->next;
            if(!fast) break; 
            fast = fast->next;
        } 
        // Now slow is the head of half Link
        auto a = slow, b = slow;
        b = a->next;
        while(b) {
            auto c = b->next;
            b->next = a;
            a = b;
            b = c;
        }
    
        slow->next = NULL;
        
        auto q = a;
        while(q) {
            if(p->val != q->val) return false;
            p = p->next;
            q = q->next;
        }

        return true;
    }
};

142.环形链表II*(快慢指针)

此题思路很巧妙——快慢指针

  1. fast速度2,slow速度1,直到第一次相遇
  2. slow回到起点,fast不动
  3. fast和slow速度1运动,再次相遇时的位置一定是成环的位置
    在这里插入图片描述
/**
 * 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) {
        // use fast and slow points
        auto fast = head, slow = head;
        while(fast) {
            fast = fast->next;
            slow = slow->next;
            if (fast != NULL) fast = fast->next;
            else break;

            if (fast == slow) {
                slow = head;
                while(fast != slow) {
                    fast = fast->next;
                    slow = slow->next;
                }
                return fast; // second time meet position
            }
        }

        return NULL;
    }
};

206.ReverseLinkedList I(翻转链表)

/**
 * 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 head;
        auto a = head, b = head->next, c = head;
        
        // 1 2 3 4 5 NULL
        // 5 4 3 2 1 NULL
        while(b) {  // 画个图很清楚
           c = b->next;
           b->next = a;
           a = b;
           b = c;
        }

        head->next = NULL;

        return a;
    }
};

92.ReverseLinkedList II*

在这里插入图片描述
在I的基础上要求链表翻转从m到n的部分
在纸上画个图就很容易了
在这里插入图片描述
易错点:while的判断条件是b,不是b->next
原题链接
参考图例

/**
 * 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) {
        if (m == n) return head;
        auto dummy = new ListNode(-1); // Reduced boundary discussion
        dummy->next = head; 
        auto ft = dummy, sd = dummy;
        for (int i = 0; i < m - 1; i ++) ft = ft->next;
        for (int i = 0; i < n; i ++) sd = sd->next;
        auto a = ft->next; // similar to I:head
        auto d = sd->next; // similar to I:NULL
        
        // Reverse Linked List template
        auto b = a->next;
        while(b != d) { // b->next is wrong, cause b = c after while;
            auto c = b->next;
            b->next = a;
            a = b;
            b = c;
        }
        
        // Connect the fore and aft
        ft->next->next = b;
        ft->next = sd;
       
        return dummy->next;
    }
};

24.SwapNodeinPairs

在这里插入图片描述

/**
 * 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) {
        // 在纸上画图模拟后,特别简单
        ListNode *dummy = new ListNode(-1);
        dummy->next = head;
        auto p = dummy;
        while(p->next && p->next->next) {
            auto a = p->next, b = p->next->next;
            p->next = b;
            a->next = b->next;
            b->next = a;
            p = a;
        }

        return dummy->next;
    }
};

19.RemoveNthNodefromEndofList

/**
 * 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) {
        // create a visual Node whose next is head;
        if(!head -> next) return NULL;
        auto dummy = new ListNode(-1);
        
        dummy -> next = head;
        auto first = dummy, second = dummy;
        
        while(n --) first = first -> next;
    
        // let two points move together
        while(first -> next) {
            first = first -> next;
            second = second -> next;
        }

        second -> next = second -> next -> next;
        
        return dummy -> next; // 真正的头结点
        // 为什么return head;不行,详见数据结构笔记               
    }
};

237.DeleteNodeinLinkedList

/**
 * 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; // 5 change to 1
        node -> next = node -> next -> next; // delete origin 1
    }
};

83.RemoveDuplicatesfromSortedList

/**
 * 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;
        if (!cur || !cur -> next) return head;
        while(cur -> next) { // cur 和 cur -> next 有区别
            if( cur -> val == cur -> next -> val) {
                cur -> next = cur -> next -> next;
            } else {
                cur  = cur -> next;
            }
        }

        return head;
    }
};

61.RotateList

在这里插入图片描述

// https://leetcode-cn.com/problems/rotate-list/
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        if (!head) return head;

        int n = 0;
        for (ListNode *p = head; p; p = p->next) n ++;

        k %= n;
    
        // two points move together
        auto first = head, second = head;
        while(k --) first = first->next;
        while(first -> next) {
            first = first->next;
            second = second->next;
        }
    
        // Draw a clear picture 
        first->next = head;
        head = second->next;
        second->next = NULL;
        
        return head;
    }
};


203.移除链表元素

/**
 * 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) {
        auto dummy = new ListNode(-1);
        dummy->next = head;
        auto a = dummy, b = dummy;
        
        // draft in the paper first
        while(b) {
            b = a->next;
            while(b && b->val == val) {
                b = b ->next;
            }
    
            // delete val
            a->next = b;
            a = b;
        }

        return dummy->next;
        
    }
};

160.两个链表的交点(a+b+c)

算法:
1.Two pointers do not intersect
p and q both move a+b step, then turn into NULL.
2.Two pointers intersect
p adn q both move a+b+c step, then they will meet.

/**
 * 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) {
        // p and q both move a + b + c
        auto p = headA, q = headB;
        while(p != q) {
            if (p) p = p->next;
            else p = headB;
            if (q) q = q->next;
            else q = headA;
        }

        return p;
    }
};

86.分割两个链表

/**
 * 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) {
        // Nice, AC this problem only one attempt
        // 1 4 3 2 5 2  x = 3
        auto dummy1 = new ListNode(-1);        
        auto dummy2 = new ListNode(-1);        
        auto less = dummy1, greater = dummy2, p = head;
        
        // less:dummy1->1->2->2 greater:dummy2->4->3->5
        while(p) {
            if (p->val < x) {
                less->next = p;
                less = less->next;
            } else {
                greater->next = p;
                greater = greater->next;
            }
            p = p->next;
        }
    
        // Merge two lists
        less->next = dummy2->next;
        greater->next = NULL;

        return dummy1->next;
    }
};

发布了182 篇原创文章 · 获赞 71 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qq_43827595/article/details/103234179