代码随想录Day 4、5 | 链表Part 2

文章介绍了链表相关问题的解决方案,包括两两交换节点、删除倒数第N个节点、寻找相交链表节点以及环形链表的处理。针对每个问题,提供了两种不同的解法,一种是暴力解法,另一种是优化后的高效方法,如快慢指针和递归。同时,还提到了C++的一些语法和特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


Day4 | 链表Part 2

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

暴力思路:分别对节点数=0/1,2/3和>=4时分情况讨论:

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        //如果为空或只有一个节点,则返回节点
        if (head == NULL || head->next == NULL)
            return head;
        ListNode* node1 = head;
        ListNode* node2 = node1->next;
        ListNode* newHead = node2;
        ListNode* tmp = node2->next;
        //如果有两个或三个节点,则手动将前两个节点进行调换。
        if (tmp == NULL || tmp->next == NULL){
            node2->next = node1;
            node1->next = tmp;
            return newHead;
        }
        // 如果节点数大于等于四个,两两对换,如果接下来的节点数目不足两个,则停止;
        while (tmp && tmp->next){
            node2->next = node1;
            node1->next = tmp->next;
            node1 = tmp;
            node2 = tmp->next;
            tmp = node2->next;        
        }
        // 当tmp或tmp->next为空时,最后两个节点还未对换,需要手动操作
        node2->next = node1;
        node1->next = tmp;
        return newHead;
    }
};

标准答案里使用哑头,做得很漂亮。这里我拿到题第一反应是将2节点取出来,让其指向1节点,再让1节点指向4节点。但是标准答案的做法相当于增加了一步,,让1节点先指向3节点,再在下一次循环的第一步让其指向4节点。为什么这样做就能将基本情况都涵盖在内?本题和反转链表的标准解答中,共同点是以某个节点为当前节点,而不是以两个节点为当前节点。具体为何还需要进一步思考。
因为交换后奇节点还需要考虑连接到后面交换好的偶节点头上,当时做题的时候也在想能不能从后向前进行操作,但觉得从后向前没有办法记住前一个节点。能记住的办法就是递归,本题递归的解法看起来思路要更加清晰一些,只不过空间复杂度就变为了O(n)。

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        if (head == nullptr || head->next == nullptr) {
            return head;
        }
        ListNode* newHead = head->next;
        head->next = swapPairs(newHead->next);
        newHead->next = head;
        return newHead;
    }
};

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

暴力解法,时间复杂度为O(2n)。

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head;
        int count = 0;
        ListNode* cur = dummyHead;
        while (cur->next){
            cur = cur->next;
            count++;
        }
        cur = dummyHead;
        while (count - n > 0)
        {
            cur = cur->next;
            count--;
        }
        cur->next = cur->next->next;
        return dummyHead->next;
    }
};

复杂度为O(n)的解法:快慢指针,先让快指针走n+1步。

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head;
        ListNode* fast = dummyHead;
        ListNode* slow = dummyHead;
        while (n && fast->next != NULL)
        {
            fast = fast->next;
            n--;
        }
        fast = fast->next;
        while (fast != NULL)
        {
            fast = fast->next;
            slow = slow->next;
        }
        slow->next = slow->next->next;
        return dummyHead->next;
    }
};

160. 相交链表

暴力解法:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if (headA == NULL || headB == NULL)
            return 0;
        //分别获取两表的最后一个节点,并记录长度。如果不是同一地址,则不相交
        ListNode* curA = headA;
        ListNode* curB = headB;
        int lengthA = 1;
        int lengthB = 1;
        while (curA->next){
            curA = curA->next;
            lengthA++;
        }
        while (curB->next){
            curB = curB->next;
            lengthB++;
        }
        if (curA != curB)
            return 0;
        //根据长度之差同步两表当前节点
        curA = headA;
        curB = headB;
        int n = abs(lengthA - lengthB);
        if (lengthA > lengthB){
            while (n--)
                curA = curA->next;
        }
        else{
            while(n--)
                curB = curB->next;
        }
        while(curA != curB){
            curA = curA->next;
            curB = curB->next;
        }
        return curA;
    }
};

双指针做法:很巧妙,还需要日后结合下一道环形链表再品一品。

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if (headA == nullptr || headB == nullptr) {
            return nullptr;
        }
        ListNode *pA = headA, *pB = headB;
        while (pA != pB) {
            pA = pA == nullptr ? headB : pA->next;
            pB = pB == nullptr ? headA : pB->next;
        }
        return pA;
    }
};

142. 环形链表II

之前做过,后续还要更深入地思考这类题目的本质。


C++语法及特性/快捷键

  1. 将光标所在行上移:Alt + ↑

总结

  1. 对于链表的题目的暴力解法更加熟悉了;
  2. 从做题的思考中能稍微获得一点乐趣了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值