代码随想录训练营day04打卡

文章讲述了如何通过双指针技术解决链表问题,包括两两交换链表节点、删除链表的倒数第N个节点、判断链表相交以及检测环形链表,强调了快慢指针在这些问题中的关键作用。
摘要由CSDN通过智能技术生成

两两交换链表中的结点

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

初始状态

本题需要注意的是交换结点时,应该用一个临时指针变量temp保存当前需要更改指针的next结点,防止断链。对应代码:

            temp1 = pre->next;
            temp2 = pre->next->next;
            pre->next = temp2;
            temp1->next = temp2->next;
            temp2->next = temp1;
           

最后变成,然后将pre指针指向temp1即可开始新的一轮交换。

注意交换停止条件是:pre->next&&pre->next->next   当有偶数个结点时,pre->next不为NULL,当有奇数个结点时,pre->next->next不为NULL。

 完整代码:


class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummyhead =new ListNode(0,head);
        ListNode* pre = dummyhead;
        ListNode* temp1 = NULL;
        ListNode* temp2 = NULL;
        while (pre->next&&pre->next->next!=NULL)//四种形式pre->next  pre->next!=0  pre->next!=NULL  pre->next!=nullptr
        {
            temp1 = pre->next;
            temp2 = pre->next->next;
            pre->next = temp2;
            temp1->next = temp2->next;
            temp2->next = temp1;
            pre = temp1;
        }
        head = dummyhead->next;
        delete(dummyhead);
        return head;
    }
};

删除链表倒数第N个结点

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

通过双指针实现,先将快指针指向第N+1个结点(注意不是N个结点,因为我们要删除一个结点,需要将慢指针指向删除结点的前一个结点),然后慢指针指向虚拟头结点dummyhead,然后同时开始遍历,当快指针遍历到链表末尾,即fastptr==NULL时,慢指针也停止遍历,此时慢指针指向待删除结点的前一个结点。然后删除即可。

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
               ListNode* dummyhead = new ListNode(0, head);
       ListNode* fastptr = dummyhead;
       ListNode* slowprt = dummyhead;
       ListNode* temp = NULL;
       n++;//为了方便找到倒数第N个结点的前一个结点,将N+1
       while (n--&&fastptr)
       {
           fastptr = fastptr->next;
       }
       while (fastptr)
       {
           fastptr = fastptr->next;
           slowprt = slowprt->next;
       }
       temp = slowprt->next;
       slowprt->next = temp->next;
       delete(temp);
       head = dummyhead->next;
       delete(dummyhead);
       return head;
   }
    
};

链表相交

面试题 02.07. 链表相交

当两个链表相交时,后面的所有结点位置都相同,因此我们只需要判断两个链表是否有相同的结点(且只需要判断第一个),那么我们如何进行判断呢,这道题我们是通过双指针实现的,需要同时向后遍历判断是否相同,由于两个链表不相交的部分长度不同,因此不能直接进行遍历判断。需要先将长度更长链表的遍历指针指向与短链表相同的对应位置。然后两个指针index1和inedx2同时向后遍历,当两个指针相同时即相交,如果index遍历到结束都相等条件都没有成立,说明没有相同结点,返回空指针(返回index1也行,因为遍历完后index指向NULL)。

完整代码:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
                int lenA = 0;
        int lenB = 0;
        int len = 0;
        ListNode* ptrA = headA;
        ListNode* ptrB = headB;

        while (ptrA)
        {
            ptrA = ptrA->next;
            lenA++;
        }
        while (ptrB)
        {
            ptrB = ptrB->next;
            lenB++;
        }
        ptrA = headA;
        ptrB = headB;
        if (lenA < lenB)
        {
            swap(ptrA,ptrB);
            swap(lenA, lenB);
        }
        len = lenA - lenB;
        while (len--)
        {
            ptrA = ptrA->next;
        }
        while (ptrA)
        {
            if (ptrA == ptrB) return ptrA;
            ptrA = ptrA->next;
            ptrB = ptrB->next;
        }
        return NULL;
    }
};

环形链表II

142. 环形链表 II

通过双指针实现,设置一个快指针和一个慢指针,快指针每次走两步,慢指针每次走一步(与田径跑道长跑比赛类似),当链表存在环时,快指针比慢指针多跑1圈(一圈的情况是慢指针进入环后快指针还未跑完一圈环长度)或N圈(N圈的情况是慢指针进入环之前快指针已经跑了1圈或者多圈环长度)。然后我们证明若存在环,必定存在fastptr == slowptr的情况。由于快指针每次比慢指针多走一步,那么必然不可能直接在某一次循环直接越过慢指针的位置,即必定在某一次循环相遇。

在判断存在环之后,我们还要返回环的入口位置。假设环之前的长度为x,环的入口到第一次相遇的长度为y,环的剩余长度为z,那么环的长度就是y+z,当第一次相遇,慢指针走了x+y步,快指针走了x+y+n*(z+y)步,即多走了n圈,又由于快指针移动长度为慢指针的2倍,因此

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

通过移项可得到x+y=n*(z+y),又由于快慢指针当前位置退后y步就是环的入口,将y移到右边。得到x=n*(z+y)-y,我们将慢指针指向链表开头,然后快慢指针同时移动x步,(但我们并不知道x的值)由于该等式,我们可以推出当两个指针相遇的时候即慢指针走了x步,因为快指针走n*(z+y)-y步相当于当前位置退后y步,也刚好在环的入口处。此时就拿到了环的入口位置。

完整代码:

class Solution {
public:
    ListNode* detectCycle(ListNode* head) {
        int pos = 0;
        ListNode* slowptr = head;
        ListNode* fastptr = head;
        while (fastptr && fastptr->next) {//不能在循环条件这里判断fastptr!=slowptr,因为第一次进入循环都指向第一个结点
            fastptr = fastptr->next->next;
            slowptr = slowptr->next;
            if (fastptr == slowptr)
            {
                slowptr = head;
                while (slowptr != fastptr)
                {
                    slowptr = slowptr->next;
                    fastptr = fastptr->next;
                    pos++;
                }
                cout << pos;
                return fastptr;
            }
        }
        pos = -1;
        return NULL;

    }
};

也可以直接在快指针遍历条件中判断是否相遇:fastptr && fastptr->next&&(fastptr=fastptr->next->next)!=(slowptr=slowptr->next)。当while循环退出时,如果是第一种情况,则是不存在环,如果是第二种情况,则是存在环并且相遇退出。完整代码如下:

class Solution1 {
public:
    ListNode* detectCycle(ListNode* head) {
        int pos = 0;
        ListNode* slowptr = head;
        ListNode* fastptr = head;
        while (fastptr && fastptr->next && (fastptr = fastptr->next->next) != (slowptr = slowptr->next))
        {
            //快指针走两步,慢指针走一步,可以保证有环情况下快指针比慢指针多走n圈且不直接越过慢指针(因为每次多走一步)
        }//当循环退出时,分两种情况,一种是无环,即fastptr && fastptr->next不成立,
        //一种是有环,即(fastptr = fastptr->next->next) != (slowptr = slowptr->next)不成立
        if (fastptr == NULL || fastptr->next == NULL) {
            pos = -1;
            return NULL;
        }//无环的情况,快指针遍历到NULL
        
        else
        {
            slowptr = head;
            while (slowptr != fastptr)
            {
                slowptr = slowptr->next;
                fastptr = fastptr->next;
                pos++;
            }
            cout << pos;
            return fastptr;
        }//有环的情况,令慢指针指向头结点,快指针指向相交的地方由于x=n*(y+z)-y,
         //当快指针走了n*(y+z)-y,即当前位置退步y步,慢指针走了x步,两指针恰好在环的入口处相遇
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值