Day04:24.两两交换链表中的节点、19.删除链表的倒数第N个节点、07. 链表相交、142.环形链表II

前言

今天的题目包括链表基本的查找、交换、循环。
在很多细节方面我出现或多或少的问题。例如while循环的终止条件、如何避免陷入死循环。


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

题目链接

思路

首先确定好链表元素替换的方法,先用cur指向dummyhead用*tmp1保存cur->nexttmp2保存cur->next->next->next.在这里插入图片描述
再开始更改元素位置,一定要注意这里的tmp1和tmp2不可以同时作为两个指针的右值,否则会死循环!
在这里插入图片描述
最后更改成功了,cur指针就可以进行下一步cur=cur->next->next->next啦!
在这里插入图片描述

代码

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
        dummyHead->next = head; // 将虚拟头结点指向head,这样方面后面做删除操作
        ListNode* cur = dummyHead;
        while(cur->next != nullptr && cur->next->next != nullptr) {
            ListNode* tmp = cur->next; // 记录临时节点
            ListNode* tmp1 = cur->next->next->next; // 记录临时节点
            cur->next = cur->next->next;    // 步骤一
            cur->next->next = tmp;          // 步骤二
            cur->next->next->next = tmp1;   // 步骤三
            cur = cur->next->next; // cur移动两位,准备下一轮交换
        }
        return dummyHead->next;
    }
};

时间复杂度:O(n)
空间复杂度:O(1)

总结

这里的while循环的终止条件是(cur->next ==NULL||cur->next->next ==NULL),因为当链表长度为偶数时刚好元素全部进行过交换,cur->next刚好为空;而链表为奇数时还留下一个元素,也不需要交换。


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

题目链接

思路

用双指针法最适合找到“倒数”第几个结点。
用指针fast和slow来查找,首先将他们都指向dummyHead。
在这里插入图片描述
例找倒数第二个结点。则fast在slow指针前2个位置,但因为删除fast的前倒数第二个结点,为了找到其位置,slow节点应找倒数第三个结点,凭其next指针寻找倒数第二个结点地址。注意在移动fast指针时应判断fast!=NULL,因为访问NULL时会报错。
在这里插入图片描述
接下来就是移动操作,等fast==NULL时便找到了倒数第三个指针位置。
在这里插入图片描述
再进行删除操作。这个很简单,不作赘述。
在这里插入图片描述

代码

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head;
        ListNode* slow = dummyHead;
        ListNode* fast = dummyHead;
        while(n-- && fast != NULL) {
            fast = fast->next;
        }
        fast = fast->next; // fast再提前走一步,因为需要让slow指向删除节点的上一个节点
        while (fast != NULL) {
            fast = fast->next;
            slow = slow->next;
        }
        slow->next = slow->next->next; 
        // ListNode *tmp = slow->next;  C++释放内存的逻辑
        // slow->next = tmp->next;
        // delete nth;
        return dummyHead->next;
    }
}

时间复杂度: O(n)
空间复杂度: O(1)

总结

这种查找链表最好是创造一个虚拟头结点,历遍起来更方便。同时要记得while的终止条件应是fast!=NULL哦,我常常搞不懂这种问题。


07. 链表相交

题目链接

思路

在这里插入图片描述
从上图可以发现链表到最后会变成一体,那不如反推,将链表a和链表b尾部对齐在这里插入图片描述
这样就完成啦。

代码

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* curA = headA;
        ListNode* curB = headB;
        int lenA = 0, lenB = 0;
        while (curA != NULL) { // 求链表A的长度
            lenA++;
            curA = curA->next;
        }
        while (curB != NULL) { // 求链表B的长度
            lenB++;
            curB = curB->next;
        }
        curA = headA;
        curB = headB;
        // 让curA为最长链表的头,lenA为其长度
        if (lenB > lenA) {
            swap (lenA, lenB);
            swap (curA, curB);
        }
        // 求长度差
        int gap = lenA - lenB;
        // 让curA和curB在同一起点上(末尾位置对齐)
        while (gap--) {
            curA = curA->next;
        }
        // 遍历curA 和 curB,遇到相同则直接返回
        while (curA != NULL) {
            if (curA == curB) {
                return curA;
            }
            curA = curA->next;
            curB = curB->next;
        }
        return NULL;
    }
};

时间复杂度:O(n + m)
空间复杂度:O(1)

总结

一开始我没有理解题意,以为链表A、B只是短暂的相遇又分开,其实最后已经重合了。


142.环形链表II

题目链接

思路

这是一个理解起来稍微有一点难度的题目,不过卡哥太强了一讲就清晰了。
先让fast一步走两个结点slow一步走一个结点,这样他们的相对速度为1,才能保证相遇。
在这里插入图片描述
当快慢指针相遇时:
假设slow刚走了(x+y),fast走了( x+y+n(y+z) );
则 2*(x+y)=( x+y+n(y+z) );
化简得 x = (n - 1) (y + z) + z;
不管n为多少,x和z定一样长,即他们在环形入口相连,所以确定slow和fast的相遇点,当slow从head出发,fast从相遇点出发,他们一定在同一时间到达环形入口点,这样问题就迎刃而解啦。

代码

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast != NULL && fast->next != NULL) {
            slow = slow->next;
            fast = fast->next->next;
            // 快慢指针相遇,此时从head 和 相遇点,同时查找直至相遇
            if (slow == fast) {
                ListNode* index1 = fast;
                ListNode* index2 = head;
                while (index1 != index2) {
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index2; // 返回环的入口
            }
        }
        return NULL;
    }
};

时间复杂度: O(n),快慢指针相遇前,指针走的次数小于链表长度,快慢指针相遇后,两个index指针走的次数也小于链表长度,总体为走的次数小于 2n
空间复杂度: O(1)

总结

一刷:又是让人头疼的while终止条件,当然是fast打头阵啦,当fast!=NULL&&fast->next!=NULL就好啦。这是超级经典的循环链表问题,一定要记住哦。
二刷:再刷发现自己都忘的差不多了,简单复习了一下,环形链表问题还需要深入了解。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值