快慢指针的应用

作者: _shen | 来源:发表于2018-02-26 19:45 被阅读58次

快慢指针的快慢主要是指在遍历链表过程中指针移动速度的快慢。比如遍历单链表,我们可以让指针每次移动一个节点,也可以让指针移动两个或两个以上的节点。

1.单链表是否有环

如果单链表中存在环,那么快慢指针在遍历过程中相遇。就好比跑步,如果是跑圈,跑的快的人总会在某个时间与跑的慢的人相遇;如果跑道是直线,那么两个人不会相遇,但是跑的快的人最先跑到终点。

因此,使用快慢指针判断单链表是否有环也是如此。如果快指针最先遍历到链表尾部,说明没有环;如果快指针与慢指针相等即相遇,那么说明有环。

首先,创建链表节点结构体:

typedef struct Node {
    struct Node *next;
} Node, *pNode;

向链表中插入节点:

/**
 * insertNode
 * @author liebert
 * @param pNode head 链表头
 * @param pNode node 待插入节点
 * @return pNode head 新的链表头
 */
pNode insertNode(pNode head, pNode node)
{
    if (head != NULL) {
        node->next = head;
    }
    head = node;
    return head;
}

设置环节点:

/**
 * setCircle
 * @author liebert
 * @param pNode head 链表头
 * @param pNode node 循环起始的位置
 */
void setCircle(pNode head, int n)
{
    pNode p1 = head, p2;
    while (n-- > 1 && p1 != NULL) {
        p1 = p1->next;
    } 

    if (p1 != NULL) {
        p2 = p1;
        while(p1->next != NULL){
            p1 = p1->next;
        }
        p1->next = p2;
    }

}

初始化链表:

/**
 * setCircle
 * @author liebert
 * @param pNode head 链表头
 * @param int num 节点数量
 */
pNode initLinkList(pNode pHead, int num)
{
    pNode pt;
    while (num --) {
        pt = (pNode) malloc(sizeof(Node));
        pt->next = NULL;
        pHead = insertNode(pHead, pt);
    }
    return pHead;
}

判断是否有环:

/**
 * hasCircle
 * @author liebert
 * @param pNode head 链表头
 * @return int -1 链表为空 0 没有环 1 有环
 */
int hasCircle(pNode head)
{
    pNode pFast,pLow;

    if (head == NULL) return -1;    
    pFast = pLow = head;
    while(pFast->next && pFast->next->next){
        pFast = pFast->next->next;
        pLow = pLow->next;
        if (pFast == pLow) {
            return 1;
        }
    }
    return 0;
}

运行程序,查看结果:

int main ()
{
    const int NUM = 10;
    pNode pHead, pt;
    // init linklist
    pHead = initLinkList(pHead, NUM);
    printf("是否有环:%d\r\n", hasCircle(pHead));
    // set circle position
    setCircle(pHead, 4);
    printf("是否有环:%d\r\n", hasCircle(pHead));
}

2.找出环的位置

单链表如果有环,需要找出环的位置。当快慢指针第一次相遇时,让快指针从头开始,并与慢指针保持同样的速度前进,当两个指针相等时,此节点就是环的入口节点。快慢指针第一次相遇时,快指针的速度为慢指针的2倍,也就说快指针走过的路程是慢指针的2倍,对于慢指针来说是第一次走到相遇点,对于快指针来说走过了与慢指针相同的路程后还继续绕着环走了一圈,因此环的长度等于从开始位置走到相遇点的路程,即从起点到环入口的距离a加上从入口到相遇点距离b就等于环的长度l,l = a + b。同时环的长度l减去从入口到相遇点距离b是相遇点到入口点距离c,c = l –b。综上所述,慢指针从相遇点走到入口点(即走完整个环)的距离c等于从起点到环入口的距离a,此时如果此时再从链表的起点发动一个指针,速度与慢指针保持一致,那么当二者再次相遇时,慢指针第一次走完整个环,而新发动的指针也刚好走到入口点,此时指针相等,此节点就是入口节点。如下图所示:

示意图
/**
 * findCircleEntry
 * @author liebert
 * @param pNode head 链表头
 * @return int 环的位置
 */
int findCircleEntry (pNode head)
{
    pNode pFast,pLow;
    int num;

    if (head == NULL) return 0; 
    pFast = pLow = head;
    while(pFast && pFast->next){
        pFast = pFast->next->next;
        pLow = pLow->next;
        if (pFast == pLow) {
            break;
        }
    }
    if (pFast != pLow) return 0;
    pFast = head;
    num = 1;
    while (pFast != pLow) {
        pFast = pFast->next;
        pLow  = pLow->next;
        num++;
    }
    return num;
}

运行程序,查看结果:

int main ()
{
    const int NUM = 10;
    pNode pHead, pt;
    // init linklist
    pHead = initLinkList(pHead, NUM);
    printf("是否有环:%d\r\n", hasCircle(pHead));
    // set circle position
    setCircle(pHead, 4);
    printf("是否有环:%d\r\n", hasCircle(pHead));
    printf("环的位置:%d\r\n", findCircleEntry(pHead));
}

3.查找中间节点

一般地,我们通过一次遍历获得链表的长度,然后才能知道中间节点的位置,进行第二次遍历,到中间节点返回节点指针,需要做两次遍历操作。如果采取快慢指针的方式,慢指针每次移动一个节点,快指针每次移动两个节点,当快指针遍历到链表结尾时,慢指针刚好指向中间节点,具体的代码如下:

/**
 * findMiddleNode
 * @author liebert
 * @param pNode head 链表头
 * @return pNode 中间节点指针
 */
pNode findMiddleNode (pNode head)
{
    pNode pFast,pLow;

    if (head == NULL) return NULL;  
    pFast = pLow = head;
    while(pFast && pFast->next){
        pFast = pFast->next->next;
        pLow = pLow->next;
    }
    return pLow;
}

相关文章

  • 快慢指针的应用

    快慢指针 快慢指针中的快慢指的是移动的步长,快慢分别指的是快指针每次移动两步,满指针每次移动一步 快慢指针的应用 ...

  • 快慢指针的应用

    快慢指针的快慢主要是指在遍历链表过程中指针移动速度的快慢。比如遍历单链表,我们可以让指针每次移动一个节点,也可以让...

  • 快慢指针的应用

    --- 最近在刷LeetCode时,发现快慢指针在链表和查找算法中应用非常广泛,对于很多即将面试或者学习算法的同学...

  • 快慢指针的应用

    什么是快慢指针:快慢指针是链表操作中的常用操作,最经典的应用是判断单链表中是否有环。 判断单链表是否存在环 两个指...

  • 快慢指针的算法应用

    快慢指针主要解决的问题: 寻找/删除第K个节点; 有关链表环问题解法:寻找/删除第K个节点。 问题一: 给定任意一...

  • 快慢指针总结

    快慢指针 所谓「快慢指针」是指设定两个指针,其中快的指针的移动速度是慢的指针的移动速度的两倍;“快慢指针”方法主要...

  • leetcode 中快慢指针的应用

    快慢指针的在leetcode的问题中有很多应用,例如通过一次遍历找到链表的中间节点。 这里要介绍的是作为哨兵,应用...

  • leetcode第142题:环形链表II [中等]

    题目描述 考点 链表 双指针(快慢指针) 解题思路 设置快慢指针slow, fast; 慢指针slow每次移动一个...

  • 快慢指针

    快慢指针即我们有两个及以上的指针,我们可以通过控制其步长去实现某种行为。 下图中自定义的名词解释如下: 目标节点:...

  • 快慢指针

    找出单向链表中倒数第 k 个节点。返回该节点的值普通解法时间复杂度2N 用快慢指针只用循环一遍N就行了

网友评论

    本文标题:快慢指针的应用

    本文链接:https://www.haomeiwen.com/subject/rmfuxftx.html