代码随想录打卡—day4—【链表】—链表操作综合题

1 链表题目(1)

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

思路和翻转链表那个像,也是搞几个指针,按着链表的顺序走一遍就行,就是【1】写核心逻辑需不需要tmp防止覆盖,【2】哪个指针指着哪个容易晕。

AC代码:

/**
 * 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) 
    {
        if(head == nullptr || head->next == nullptr)return head;  // 只有0或者1个元素就返回
        ListNode* dummynode = new ListNode(0,head);
        ListNode* last = dummynode;
        ListNode* now = dummynode->next;
        while(now != nullptr && now->next != nullptr)
        {
            cout << last->val << endl;
            cout << now->val << endl;
            // 交换
            last->next = now->next;
            ListNode* tmp = now->next;
            now->next = now->next->next;
            tmp->next = now;
            //前进
            last = now;
            now = now->next;
        }
        return dummynode->next;
    }
};

2 链表题目(2)

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

我一开始AC的做法,先遍历整个链表计算一共几个元素,然后计算要删除的是正着数第几个节点,再遍历走到要删除的节点的前一个节点,删除。【遍历两遍】

/**
 * 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* removeNthFromEnd(ListNode* head, int n) 
    {
        // 先扫描一次看一共几个节点
        ListNode* dummyhead = new ListNode(0,head);  // 加一个虚拟头节点
        int sum = 0;  // 一共k个节点
        ListNode* cur = dummyhead;
        while(cur->next != nullptr)
        {
            sum++;
            cur = cur->next;
        }
        int k = sum - n ;  //到要删除节点的前一个
        cur = dummyhead;
        while(k--)
        {
            cur = cur->next;
        }
        cur->next = cur->next->next;
        return dummyhead->next;
    }
};

2.1 链表题目(2)优化

看了讲解发现用《同向快慢双指针》可以优化到一遍扫描+上面这个又忘记delete要删除的元素,优化之后的AC代码:

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) 
    {
        ListNode* dummyhead = new ListNode(0,head);  // 加一个虚拟头节点
        ListNode* fast = dummyhead;
        ListNode* slow = dummyhead;
        n++;
        while(n--)fast = fast->next;  //快指针先走n+1步
        while(fast != nullptr)
        {
            fast = fast->next;
            slow = slow->next;
        }
        ListNode* tmp = slow->next;
        slow->next = slow->next->next;
        delete tmp;

        return dummyhead->next;
    }
};

3 链表题目(3)

面试题 02.07. 链表相交

 一开始没有想法,只想到了O(m*n)的暴力解法。后来看了提示,才想到可以从两个链表后面长度相同的部分开始往后移。

/**
 * 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* cura = headA;
        ListNode* curb = headB;
        // 计算两个链表的长度
        int numa = 0;
        int numb = 0;
        while(cura != nullptr)
        {
            cura = cura->next;
            numa++;
        }
        while(curb != nullptr)
        {
            curb = curb->next;
            numb++;
        }
        cura = headA;
        curb = headB;
        // 把两个链表当前指针同步到后面的长度一致
        if(numa < numb)  //  b比较长
        {
            int k = numb - numa;
            while(k--){curb = curb->next;}
        }
        else if(numa > numb)  //a比较长
        {
            int k = numa - numb;
            while(k--){cura = cura->next;}
        }
        while(cura != nullptr)  // 后移 找交点
        {
            if(cura == curb)return cura;
            cura = cura->next;
            curb = curb->next;
        }
        return nullptr;
    }
};

4 链表题目(4)

142. 环形链表 II

 我的AC写法超暴力写法,又占时间,又占空间的。

/**
 * 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) 
    {
        //  我的暴力想法 来一个vector 存着过去扫过的节点地址
        vector a;
        ListNode * cur = head;
        while(cur != nullptr)
        {
            auto it = find(a.begin(),a.end(),cur);
            if(it == a.end())  // 在a中没有
                a.push_back(cur);
            else return cur;
            cur = cur->next;
        }
        return nullptr;
    }
};

代码随想录打卡—day4—【链表】—链表操作综合题_第1张图片


4.1 链表题目(4)优化

学习一下我想不到的优化写法。学完感慨,妙啊!!!下面讲一下注意点:

Q1:判断有无环。==》 快慢指针,快的每次走2步,慢的每次走1步,如果存在环的话,快慢指针一定会在环里面相遇!证明:【1】首先,快的比慢的走的快,所以不可能在直线上相遇,要是相遇只可能在环里面。【2】其次,想到在一个操场跑步,当慢的进入环,早就在环里的快的 就相当于要追慢的,而快的速度比慢的快,所以一定可以追上!

Q1.1 为什么快的要2步,不能3步,4步?==》 因为快的2步时,就是相对慢的每次增加一步的节奏逼近,如果快的走3步时,则快的相对慢的以每次2步的增速逼近,由于走的是离散的,也就是存在慢的就在自己前面一步,但是快的相对慢的迈了两步,相遇超过了但错过了没发现

Q2:现在如果判断有环了,怎么找环入口?==》

代码随想录打卡—day4—【链表】—链表操作综合题_第2张图片

那么相遇时: slow指针走过的节点数为: x + y, fast指针走过的节点数:x + y + n (y + z),其中:n为fast指针在环内走了n圈才遇到slow指针。因为fast指针是一步走两个节点,slow指针一步走一个节点, 易得:

(x + y) * 2 = x + y + n (y + z)

化简得 : x + y = n (y + z)

因为要找环形的入口,那么要求的是x,因为x表示 头结点到 环形入口节点的的距离。所以要求x ,将x单独放在左面:

x = n (y + z) - y 

 消掉 -y 得:

x = (n - 1) (y + z) + z 

注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针。

这个公式说明:先拿n为1的情况来举例,意味着fast指针在环形里转了一圈之后,就遇到了 slow指针了。

假设 n为1的时候,公式就化解为

 x = z

这就意味着,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点

 Q2.1: 为什么那么相遇时: slow指针走过的节点数为: x + y? 不是x + y + k (y + z).==》因为当慢的进入环的时候,最差的情况,快的就在自己前面一格,所以慢的转小于一圈的时间内,快的一定可以追到自己。

Q2.2:n的解释:n是一个不知道多少的定值,可能是1,可能是其他正整数,但不论是哪个定值,按下面的t1 != t2 为条件循环都满足x = (n - 1) (y + z) + z  ,也不在乎n是多少了。

class Solution {
public:
    ListNode *detectCycle(ListNode *head) 
    {
        //
        ListNode * fast = head;
        ListNode * slow = head;
        while(fast != nullptr && fast->next != nullptr)
        {
            fast = fast->next->next;  //快指针走两格
            slow = slow->next;  //慢指针走一格
            if(fast == slow)  //相遇了说明有环
            {
                ListNode * t1 = head;
                ListNode * t2 = fast;
                while(t1 != t2)  // 找到环就找环入口
                {
                    t1 = t1->next;
                    t2 = t2->next;
                }
                return t1;
            }
        }
        return nullptr;  //没有环
    }
};

代码随想录打卡—day4—【链表】—链表操作综合题_第3张图片

 果然!时间空间都小了,时间快了12倍!


总结:

1. 这次学到了快慢指针的更多应用

2. 感受了一个环形链表比较难的题,正解很难想,但很有意思

3. day6才在补day4 的wuwu , 累计用时3个半小时吧

你可能感兴趣的:(SXL,链表,数据结构)