day3 双指针技巧例题

1、给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

解法1:快慢指针

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) 
    {
        if(head == NULL)//空链表没有环
            return false;

        ListNode *fast = head,*slow = head;//定义快慢指针指向头结点
        while(fast != NULL && fast->next != NULL)
        {
            slow = slow->next;
            fast = fast->next->next;
            if(fast == slow)
                return true;//指针相等说明有环
        }
        return false;//否则无环
    }
};

示例:

        输入:head=[3,2,0,4]

        输出:true

2、给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 

解析:

设置快指针、慢指针,若链表有环,则两个指针一定相遇,此时头指针head和快慢指针的距离x与快慢指针到入环第一个节点的距离y相等或成倍数关系。故将fast指针重新指向head,fast和slow指针一起每次只走一步,直到fast==slow时,快慢指针恰好在入环的第一个节点,返回指针即可

/**
 * 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) {
        if(head == NULL)
            return NULL;

        ListNode *fast = head,*slow = head;
        while(fast != NULL && fast->next != NULL)
        {
            slow = slow->next;
            fast = fast->next->next;
            if(slow == fast)
            {
                fast = head;//快指针返回到头结点
                while(fast != slow)
                {
                    fast = fast->next;
                    slow = slow->next;
                }
                return slow;
            }
        }
        return NULL;
    }
};

3、找出并返回两个单链表相交的起始节点

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

图示两个链表在节点 c1 开始相交:

题目数据 保证 整个链式结构中不存在环。注意,函数返回结果后,链表必须 保持其原始结构 。

解法1:固定指针p,遍历另一链表

指针p、pre分别指向headA和headB,固定指针p,pre遍历链表,若p和pre指向同一节点,返回指针p,否则pre重新指向headB,p指向下一节点,继续遍历(复杂)

/**
 * 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) {
        if(headA == NULL || headB == NULL)
            return NULL;
            
        ListNode *p = headA,*pre = headB;//分别指向headA和headB
        while(p != NULL)
        {
            pre = headB;//pre遍历完后重新指向headB
            while(pre != NULL)
            {
                if(p == pre)
                    return p;
                else
                    pre = pre->next;
            }
            p = p->next;
        }
        return NULL;
    }
};

 解法2:先统计链表长度

若长度相等,p和pre同时出发,直到到达相同节点;若长度不同,长链表的指针先出发直到长度相等,另一指针再出发

class Solution {
/*统计链表的长度的函数*/
private:int length(ListNode *node)
{
    int length = 0;
    while (node != NULL) 
    {
        node = node->next;
        length++;
    }
    return length;
}
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        int lenA = length(headA),lenB = length(headB);//统计链表A和链表B的长度
        while (lenA != lenB) 
        {
            if(lenA > lenB) //如果链表A长,那么链表A先走
            {
                headA = headA->next;
                lenA--;
            }
            else
            {
                headB = headB->next;//如果链表B长,那么链表B先走
                lenB--;
            }
        }
        while (headA != headB)//然后开始比较,如果他俩不相等就一直往下走
        {
            headA = headA->next;
            headB = headB->next;
        }
        return headA;//走到最后,最终会有两种可能,一种是headA为空,
                 //也就是说他们俩不相交。还有一种可能就是headA
                 //不为空,也就是说headA就是他们的交点
    }
};

 解法3:双指针交替

两指针同时出发,相遇于同一节点就返回节点,否则,若p走完A链表,p变轨指向B链表,继续出发,pre走完B链表则变轨指向A链表,直到相遇

 相遇时所走路程:p:S1+S3+S2

                              pre:S2+S3+S1

故一定会相遇(p和pre最多能变轨一次)

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode *p = headA,*pre = headB;
        //变轨次数:p变一次,pre变一次,cnt大于等于2则返回NULL
        int cnt=0;
        while (p != NULL && pre != NULL)
        {
            if (p == pre) 
                return p;
            p = p->next;
            pre = pre->next;
            //p变轨
            if(cnt < 2 && p == NULL)
            {
                p = headB;
                cnt++;
            }
            //p2变轨
            if(cnt < 2 && pre == NULL)
            {
                pre = headA;
                cnt++;
            }
        }
        return NULL;
    }
};

4、

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

解法:快慢指针

快指针先走n步,之后快慢指针一起走,快指针到终点时慢指针恰好到达倒数din个结点的位置

/**
 * 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 *p = head,*pre = head;//快指针p,慢指针pre
        for(int i = 0;i < n;i++)//p先移动n位
            p = p->next;
        if(p == NULL)//要删除的是头结点
            return head->next;
        
        while(p->next != NULL)//while循环结束时pre恰好指向要删除节点的前一位
        {
            p = p->next;
            pre = pre->next;
        }
        pre->next = pre->next->next;//覆盖
        return head;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值