代码随想录算法训练营第四天 | LeeCode 24.两两交换链表中的节点,19. 删除链表的倒数第 N 个结点,面试题 02.07. 链表相交,142. 环形链表 II

本文介绍了三种与链表相关的编程问题解决方案:交换链表中每两个节点,删除链表倒数第N个节点,以及检测环形链表的入口。主要运用了快慢指针策略,展示了高效的时间复杂度和空间复杂度
摘要由CSDN通过智能技术生成

题目链接:24. 两两交换链表中的节点

class Solution
{
public:
    ListNode *swapPairs(ListNode *head)
    {
        ListNode *dummy=new ListNode(0);
        dummy->next=head;
        ListNode *cur=dummy;
        while(cur->next!=nullptr&&cur->next->next!=nullptr){
            ListNode *tmp=cur->next->next;
            ListNode *tmp1=cur->next->next->next;

            tmp->next=cur->next;//第二个结点指向第一个结点
            cur->next->next=tmp1;//第一个节点指向第三个节点
            cur->next=tmp;//变换头节点指针

            cur=cur->next->next;//cur往后移动2个节点
        }
        return dummy->next;
    }
};

简单的一道题目,考察链表结点的交换位置,只要对链表的结点的位置有清楚的认知,就可以做出来。

题目链接:19. 删除链表的倒数第 N 个结点

class Solution {
public:
   ListNode *removeNthFromEnd(ListNode *head, int n)
    {
        ListNode *dummy=new ListNode();
        dummy->next=head;
        ListNode *fast=dummy;//快指针
        ListNode *slow=dummy;//慢指针
        while(n--&&fast->next!=nullptr){
            fast=fast->next;
        }
        while(fast->next!=nullptr){
            fast=fast->next;
            slow=slow->next;//slow并不是最终要指向被删除的节点,最终应该指向被删除节点的上一个节点
        }
        ListNode *tmp=slow->next;
        slow->next=tmp->next;
        return dummy->next;
    }
};

所用的是快慢指针法,真的是一个很巧妙的方法!首先让快指针走n个结点,然后快、慢指针一起走,直到快指针到最后,这时候慢指针就已经指到了倒数第n个结点了。原理就是快指针先走了n,那么它离链表末端的距离就是size-n,然后快慢指针一起走size-n,也就是快指针指到末尾,慢指针指到倒数第n个结点。

我一开始想到的办法是先遍历一遍链表,计数一共有几个结点,再进行操作。这样子要多遍历一遍链表,时间复杂度高于快慢指针法,因为快慢指针法只要遍历一次结点。

题目链接:面试题 02.07. 链表相交

class Solution
{
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB)
    {
        int lenA=0;
        int lenB=0;
        ListNode *curA=headA;
        ListNode *curB=headB;
        while(curA!=nullptr){
            curA=curA->next;
            ++lenA;
        }
        while(curB!=nullptr){
            curB=curB->next;
            ++lenB;
        }
        curA=headA;
        curB=headB;   
        if(lenB>lenA){
            swap(lenA,lenB);
            swap(curA,curB);
        }

        int gap=lenA-lenB;
        while(gap--){
            curA=curA->next;
        }
        while(curA!=nullptr){
            if(curA==curB){
                return curA;
            }
            curA=curA->next;
            curB=curB->next;
        }
        return NULL;
    }
};

这道题的关键在于理解题目,题目的意思其实是这两个链表,在某个结点之后,就是同一个链表了。类似于双头蛇。我们判断的条件不是cur->val相等,而是cur这个指针本身相等,即指向同一个结点。

142. 环形链表 II

class Solution {
public:
    ListNode *detectCycle(ListNode *head)
    {
        unordered_set<ListNode *> visited;  
        ListNode *cur=head;
        while(cur!=nullptr){
//find返回的是一个迭代器,如果找到就返回位置的迭代器,如果找不到就返回和end一样的迭代器
            if(visited.find(cur)!=visited.end()){
                return cur;
            }
            visited.insert(cur);
            cur=cur->next;
        }
        return nullptr;
    }
};

第一时间想到的应该是用集合来做,如果这个结点再次出现,那么就说明这是一个循环链表了,返回这个结点就行。时间复杂度o(n),遍历一个链表。空间复杂度o(n),创建一个set用于储存n个结点。

下面还有一个更巧妙的方法(令人惊叹,怎么相处这种办法的):

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)

空间复杂度:o(1),只用了fast,slow,index1,index2这几个指针

参考了代码随想录的解析:代码随想录解析

创建快、慢两个指针,快指针一次性走2个结点,慢指针一次性走1个节点。

如果链表没有环,那么两个指针永不相遇。

如果相遇,代表有环。

我们设从链表头节点到环的入口结点这段距离为x,从环的入口结点顺时针到两指针相遇结点的距离为y,两指针相遇结点顺时针到环的入口结点的距离为z。

这样子,环的长度为y+z

慢指针走过的距离是x+y

快指针走过的距离是x+y+n*(y+z)//走了n圈,即n*(y+z)

因为快指针一次性走2个结点。所以有2*(x+y)=x+y+n*(y+z)

可以化为x+y=n*(y+z)

我们要求的是环的入口,即x的长度

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

n必定大于1,因为快指针至少走1圈才会出现快慢指针相遇

假设这时候n==1

{

x==z

即头节点到环的入口的距离==快慢指针相遇结点顺时针到环的入口的距离

这时候我们使用两个指针,index1和index2

index1指向头节点

index2指向快慢指针相遇的结点

两指针同时移动,因为x==z,两指针最终相遇,相遇的点就是环的入口

}

如果n大于1,其实也就是快指针多走几圈,最终停下来的还是这个位置,也就是x会始终和z相等。

所以过程同上。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值