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

发布于:2022-11-13 ⋅ 阅读:(759) ⋅ 点赞:(0)

链表


一、数据结构介绍

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

struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(nullptr) {}
};

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

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


二、经典问题

1. 链表的基本操作

206. 反转链表

206. Reverse Linked List

        给你单链表的头节点 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;
    }   
};

21. 合并两个有序链表

21. Merge Two Sorted Lists

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

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

递归的写法为:

/**
 * 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;
    }
};

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

24. Swap Nodes in Pairs

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

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

/**
 * 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;
    }
};

2. 其他链表技巧

160. 相交链表

160. Intersection of Two Linked Lists

        给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 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) {
        ListNode* l1 = headA, *l2 = headB;
        while(l1 != l2){
            l1 = l1? l1->next: headB;
            l2 = l2? l2->next: headA;
        }
        return l1;
    }
};

234. 回文链表

234. Palindrome Linked List

        给你一个单链表的头节点 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. 删除排序链表中的重复元素

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


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


网站公告

今日签到

点亮在社区的每一天
去签到