• LeetCode力扣刷题——指针三剑客之一:链表


    链表


    一、数据结构介绍

            (单)链表是由节点和指针构成的数据结构,每个节点存有一个值,和一个指向下一个节点 的指针,因此很多链表问题可以用递归来处理。不同于数组,链表并不能直接获取任意节点的值, 必须要通过指针找到该节点后才能获取其值。同理,在未遍历到链表结尾时,我们也无法知道链 表的长度,除非依赖其他数据结构储存长度。LeetCode 默认的链表表示方法如下。        

    1. struct ListNode {
    2. int val;
    3. ListNode *next;
    4. ListNode(int x) : val(x), next(nullptr) {}
    5. };

            由于在进行链表操作时,尤其是删除节点时,经常会因为对当前节点进行操作而导致内存或 指针出现问题。有两个小技巧可以解决这个问题:一是尽量处理当前节点的下一个节点而非当前 节点本身,二是建立一个虚拟节点 (dummy node),使其指向当前链表的头节点,这样即使原链表 所有节点全被删除,也会有一个 dummy 存在,返回 dummy->next 即可。

            一般来说,算法题不需要删除内存。在刷 LeetCode 的时候,如果想要删除一个节点,可以 直接进行指针操作而无需回收内存。实际做软件工程时,对于无用的内存,笔者建议尽量显式回 收,或利用智能指针。


    二、经典问题

    1. 链表的基本操作

    206. 反转链表

    206. Reverse Linked List

            给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

            链表翻转是非常基础也一定要掌握的技能。我们提供了两种写法——递归和非递归,且我们建议你同时掌握这两种写法。

    递归的写法为:

    1. class Solution {
    2. public:
    3. ListNode* reverseList(ListNode* head, ListNode* prev = nullptr) {
    4. if(!head){
    5. return prev;
    6. }
    7. ListNode* next = head->next;
    8. head->next = prev;
    9. return reverseList(next, head);
    10. }
    11. };

    非递归的写法为:

    1. class Solution {
    2. public:
    3. ListNode* reverseList(ListNode* head) {
    4. ListNode* prev = nullptr, *next;
    5. while(head){
    6. next = head->next;
    7. head->next = prev;
    8. prev = head;
    9. head = next;
    10. }
    11. return prev;
    12. }
    13. };

    21. 合并两个有序链表

    21. Merge Two Sorted Lists

            将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

            我们提供了递归和非递归,共两种写法。

    递归的写法为:

    1. /**
    2. * Definition for singly-linked list.
    3. * struct ListNode {
    4. * int val;
    5. * ListNode *next;
    6. * ListNode() : val(0), next(nullptr) {}
    7. * ListNode(int x) : val(x), next(nullptr) {}
    8. * ListNode(int x, ListNode *next) : val(x), next(next) {}
    9. * };
    10. */
    11. class Solution {
    12. public:
    13. ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
    14. if(!list1){
    15. return list2;
    16. }
    17. if(!list2){
    18. return list1;
    19. }
    20. if(list1->val > list2->val){
    21. list2->next = mergeTwoLists(list1, list2->next);
    22. return list2;
    23. }else{
    24. list1->next = mergeTwoLists(list1->next, list2);
    25. return list1;
    26. }
    27. }
    28. };

    非递归的写法为:

    1. /**
    2. * Definition for singly-linked list.
    3. * struct ListNode {
    4. * int val;
    5. * ListNode *next;
    6. * ListNode() : val(0), next(nullptr) {}
    7. * ListNode(int x) : val(x), next(nullptr) {}
    8. * ListNode(int x, ListNode *next) : val(x), next(next) {}
    9. * };
    10. */
    11. class Solution {
    12. public:
    13. ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
    14. ListNode* dummy = new ListNode(0), *node = dummy;
    15. while(list1 && list2){
    16. if(list1->val > list2->val){
    17. node->next = list2;
    18. list2 = list2->next;
    19. }else{
    20. node->next = list1;
    21. list1 = list1->next;
    22. }
    23. node = node->next;
    24. }
    25. node->next = list1? list1: list2;
    26. return dummy->next;
    27. }
    28. };

    24. 两两交换链表中的节点

    24. Swap Nodes in Pairs

            给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

            利用指针进行交换操作,没有太大难度,但一定要细心。

    1. /**
    2. * Definition for singly-linked list.
    3. * struct ListNode {
    4. * int val;
    5. * ListNode *next;
    6. * ListNode() : val(0), next(nullptr) {}
    7. * ListNode(int x) : val(x), next(nullptr) {}
    8. * ListNode(int x, ListNode *next) : val(x), next(next) {}
    9. * };
    10. */
    11. class Solution {
    12. public:
    13. ListNode* swapPairs(ListNode* head) {
    14. ListNode* p = head, *s;
    15. if(p && p->next){
    16. s = p->next;
    17. p->next = s->next;
    18. s->next = p;
    19. head = s;
    20. while(p->next && p->next->next){
    21. s = p->next->next;
    22. p->next->next = s->next;
    23. s->next = p->next;
    24. p->next = s;
    25. p = s->next;
    26. }
    27. }
    28. return head;
    29. }
    30. };

    2. 其他链表技巧

    160. 相交链表

    160. Intersection of Two Linked Lists

            给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

            假设链表 A 的头节点到相交点的距离是 a,链表 B 的头节点到相交点的距离是 b,相交点 到链表终点的距离为 c。我们使用两个指针,分别指向两个链表的头节点,并以相同的速度前进, 若到达链表结尾,则移动到另一条链表的头节点继续前进。按照这种前进方法,两个指针会在 a + b + c 次前进后同时到达相交节点。

    1. /**
    2. * Definition for singly-linked list.
    3. * struct ListNode {
    4. * int val;
    5. * ListNode *next;
    6. * ListNode(int x) : val(x), next(NULL) {}
    7. * };
    8. */
    9. class Solution {
    10. public:
    11. ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
    12. ListNode* l1 = headA, *l2 = headB;
    13. while(l1 != l2){
    14. l1 = l1? l1->next: headB;
    15. l2 = l2? l2->next: headA;
    16. }
    17. return l1;
    18. }
    19. };

    234. 回文链表

    234. Palindrome Linked List

            给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

            以 O(1) 的空间复杂度,判断链表是否回文。

            先使用快慢指针找到链表中点,再把链表切成两半;然后把后半段翻转;最后比较两半是否相等。

    1. /**
    2. * Definition for singly-linked list.
    3. * struct ListNode {
    4. * int val;
    5. * ListNode *next;
    6. * ListNode() : val(0), next(nullptr) {}
    7. * ListNode(int x) : val(x), next(nullptr) {}
    8. * ListNode(int x, ListNode *next) : val(x), next(next) {}
    9. * };
    10. */
    11. class Solution {
    12. public:
    13. // 主函数
    14. bool isPalindrome(ListNode* head) {
    15. if(!head || !head->next){
    16. return true;
    17. }
    18. ListNode* slow = head, *fast = head;
    19. while(fast->next && fast->next->next){
    20. slow = slow->next;
    21. fast = fast->next->next;
    22. }
    23. slow->next = reverseList(slow->next);
    24. slow = slow->next;
    25. while(slow){
    26. if(head->val != slow->val){
    27. return false;
    28. }
    29. head = head->next;
    30. slow = slow->next;
    31. }
    32. return true;
    33. }
    34. // 辅函数 - 链表翻转
    35. ListNode* reverseList(ListNode* head){
    36. ListNode* prev = nullptr, *next;
    37. while(head){
    38. next = head->next;
    39. head->next = prev;
    40. prev = head;
    41. head = next;
    42. }
    43. return prev;
    44. }
    45. };

    三、巩固练习

    83. 删除排序链表中的重复元素

    83. Remove Duplicates from Sorted List

    328. 奇偶链表

    328. Odd Even Linked List

    19. 删除链表的倒数第 N 个结点

    19. Remove Nth Node From End of List

    148. 排序链表

    148. Sort List


    欢迎大家共同学习和纠正指教

  • 相关阅读:
    基于新版Opencv5.x(C++)+yolov6快速目标检测
    qt软件崩溃的分析方法-定位源文件和行号
    CSS 3 弹跳的小球动画的制作
    使用HuTool的Http工具发送post传递中文参数,请求会乱码的解决方法
    Excel 拆分单元格数据(公式拆分、智能填充、分列)
    电脑msvcp100.dll丢失的解决办法,靠谱的五个解决方法分享
    【云原生】gitlab+jenkins自动化部署springboot项目实战01
    cmake简洁教程 - 第二篇
    阻塞队列--线程安全问题
    Power BI 实现日历图,在一张图中展示天、周、月数据变化规律
  • 原文地址:https://blog.csdn.net/Hello_world_n/article/details/127831099