zoukankan      html  css  js  c++  java
  • 链表三——快慢指针的使用

    本篇主要介绍快慢指针。

    快慢指针中的快慢指的是移动的步长,即每次向前移动速度的快慢。例如可以让快指针每次沿链表向前移动2次,慢指针每次向前移动1次。

    一、快慢指针的经典应用:判断一个链表是否存在环

    思路:

    1、使用快慢指针,慢指针每次走一步,快指针每次走两步。

    2、快指针有两种状态:

    当链表无环时,快指针走到末尾,这时退出循环;
    
    当链表有环时,快指针就一直走,直到慢指针快指针追上慢指针时,退出循环。

    注意:链表的长度分为奇数和偶数,所以快指针没走一步都要判断是否到达末尾(无环时)。

    代码如下:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <string.h>
     4 #include <time.h>
     5 
     6 typedef struct tag
     7 {
     8     int Nnum_;
     9     struct tag *Nnext_;
    10 }Node, *pNode;
    11 
    12 
    13 void link_print(pNode phead, int size)
    14 {
    15     int i= 0;
    16     while(phead != NULL)
    17     {
    18         printf("%4d", phead->Nnum_);
    19         phead = phead->Nnext_;
    20         if( ++i > size)
    21             break;
    22     }
    23     printf("
    ");
    24 }
    25 //尾插法
    26 void link_init_tail(pNode *phead, int size) //传的是地址
    27 {
    28     pNode pNew = NULL;
    29     pNode pTail = NULL;
    30 
    31     while( size > 0)
    32     {
    33         //申请内存
    34         pNew = (pNode)malloc(sizeof(Node)); //注意这里为何不用pNode而用Node,因为sizeof(pNode) = 4
    35         //赋值
    36         pNew->Nnum_ = rand()%1000;
    37         //插入链表
    38         if(*phead == NULL) //链表为空时
    39         {
    40             *phead = pNew;//连接新的结点
    41             pTail = pNew;
    42         }
    43         else//不为空
    44         {
    45             pTail->Nnext_ = pNew ; //连接新的结点
    46              pTail = pNew; //改名字
    47         }    
    48         size --;
    49     }
    50 /*
    51     //生成一个带环的链表
    52     pNode pCur = *phead;
    53     int i =0;
    54     while( i < 3) //attention
    55     {
    56         pCur = pCur->Nnext_;
    57         i++;
    58     }
    59     pTail->Nnext_ = pCur; //生成一个环
    60 */
    61 }
    62 
    63 int isloop( pNode phead)
    64 {
    65     pNode pFast = phead; //快指针
    66     pNode pSlow = phead; //慢指针
    67 
    68     while( pFast->Nnext_ != NULL)
    69     {    
    70         pSlow = pSlow->Nnext_;
    71         pFast = pFast->Nnext_;
    72         pFast = pFast->Nnext_; 
    73         if(pFast == NULL)//没有这句的话,若length为偶数,则会Segmentation fault (core dumped)
    74             return 0;
    75 
    76         if( pSlow == pFast) //有环存在
    77             return 1;
    78     }
    79 
    80     if( pFast == NULL) //无环
    81         return 0;
    82 } 
    83 
    84 int main(int argc, char const *argv[])
    85 {
    86     srand(time(NULL));
    87     pNode phead = NULL;
    88     link_init_tail(&phead, 10); //10为初始链表的长度
    89     //从init中可以看出,若真正打印出的数字的长度为15(> 链表的初始长度),则说名存在环
    90     link_print(phead,15); 
    91 
    92     printf(" ret:%d
    ", isloop(phead));
    93 
    94     return 0;
    95 }
    View Code

    二、在有序链表中寻找中位数(快慢指针)

    思路:同 一

    代码如下:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <string.h>
     4 #include <time.h>
     5 
     6 typedef struct tag
     7 {
     8     int Nnum_;
     9     struct tag *Nnext_;
    10 }Node, *pNode;
    11 
    12 
    13 void link_print(pNode phead)
    14 {
    15     while(phead != NULL)
    16     {
    17         printf("%4d", phead->Nnum_);
    18         phead = phead->Nnext_;
    19     }
    20     printf("
    ");
    21 }
    22 
    23 void link_insert_by_sort(pNode *phead, int size)
    24 {
    25     pNode pNew = NULL;
    26     pNode pCur, pPre;
    27 
    28     while( size > 0 )
    29     {
    30         pNew = (pNode)malloc(sizeof(Node));
    31         pNew->Nnum_ = rand()%1000;
    32 
    33         pPre = NULL;
    34         pCur = *phead;
    35         while( pCur != NULL)
    36         {
    37             if( pNew->Nnum_ > pCur->Nnum_) //按照从小到大排序
    38             {
    39                 pPre = pCur;
    40                 pCur = pCur->Nnext_;
    41             }
    42             else //找到位置
    43                 break;
    44         }
    45         //找到的位置在头部
    46         if( pPre == NULL)
    47         {
    48             pNew->Nnext_ = *phead;
    49             *phead = pNew;
    50         }else//位置在中间或者尾部
    51         {
    52             pPre->Nnext_ = pNew;
    53             pNew->Nnext_ = pCur;
    54         }
    55         size --;
    56     }
    57 }
    58 
    59 int find_mid_pos( pNode phead)
    60 {
    61     pNode pFast = phead;
    62     pNode pSlow = phead;
    63 
    64     while(pSlow->Nnext_ != NULL)
    65     {
    66         pSlow = pSlow->Nnext_;
    67         pFast = pFast->Nnext_;
    68         pFast = pFast->Nnext_; //考虑链表长度为奇数偶数情况
    69         if(pFast == NULL) //偶数时,返回右边值
    70             return pSlow->Nnum_ ;
    71     }
    72     return pSlow->Nnum_ ;//奇数
    73 }
    74 
    75 int main(int argc, char const *argv[])
    76 {
    77     srand(time(NULL));
    78     pNode phead = NULL;
    79     link_insert_by_sort(&phead, 15);
    80     link_print(phead);    
    81 
    82     printf("ret:%4d
    ", find_mid_pos(phead));
    83     return 0;
    84 }
    View Code

    三:输入一个链表,输出该链表倒数第K个元素值(快慢指针)

    思路:

    1、快指针先走K-1个结点,如果连K个结点都没走完,则说明原链表长度不够;

    2、在快指针到达末尾之前:慢指针、快指针每次都走一步。

    3、当快指针走到末尾时,此时,慢指针所指的结点就是倒数第K个结点。

    代码如下:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <string.h>
     4 #include <time.h>
     5 
     6 typedef struct tag
     7 {
     8     int Nnum_;
     9     struct tag *Nnext_;
    10 }Node, *pNode;
    11 
    12 
    13 void link_print(pNode phead)
    14 {
    15     while(phead != NULL)
    16     {
    17         printf("%4d", phead->Nnum_);
    18         phead = phead->Nnext_;
    19     }
    20     printf("
    ");
    21 }
    22 
    23 //头插法
    24 void link_init_head(pNode *phead, int size)
    25 {
    26     pNode pNew = NULL;
    27 
    28     while( size > 0)
    29     {
    30         //申请内存
    31         pNew = (pNode)malloc(sizeof(Node)); //注意这里为何不用pNode而用Node,因为sizeof(pNode) = 4
    32         //赋值
    33         pNew->Nnum_ = rand()%1000;
    34         //插入链表
    35         if(*phead == NULL)
    36         {
    37             *phead = pNew;//连接新的结点;    
    38         }
    39         else
    40         {
    41             pNew->Nnext_ = *phead;//将pNew的next域置为phead
    42             *phead = pNew;
    43         }
    44         size --;
    45     }
    46 }
    47 
    48 int backwards_K(pNode phead, int k)
    49 {
    50     pNode pFast = phead;
    51     pNode pSlow = phead;
    52 
    53     int i =0;
    54     for ( ; i < k-1; ++i) //倒数第2个,pFast走1 步即可
    55     {
    56         pFast = pFast->Nnext_;
    57         if (pFast == NULL)
    58         {
    59             printf("the link's length is not enough
    ");
    60             return -1;
    61         }
    62     }
    63 
    64     while( pFast->Nnext_ != NULL)
    65     {
    66         pSlow = pSlow->Nnext_;
    67         pFast = pFast->Nnext_;
    68     }
    69     return pSlow->Nnum_;
    70 }
    71 
    72 int main(int argc, char const *argv[])
    73 {
    74     srand(time(NULL));
    75     pNode phead = NULL;
    76     link_init_head(&phead, 15);
    77     link_print(phead);
    78 
    79     printf("ret:%4d
    ", backwards_K(phead, 5));
    80 
    81     printf("ret:%4d
    ", backwards_K(phead, 18));
    82     return 0;
    83 }
    View Code

    四:寻找循环链表的入口

    题目:假设一个链表存在环,那么怎么寻找环的入口呢?

    分析:设链表长度为 L, 起始点到环入口长度为 a,环长度为 r, L=a + r(最好自己画图演示一遍)

    在快指针进入环到慢指针进入环前的这段时间,若环的长度较短,也许快指针已经走了好几圈了。然后慢指针进入环,设慢指针和快指针在环内相遇时,慢指针在环内走了 X步,走的总步数(包括环内与环外)为 S 步, 显然 S = X + a,那么快指针走了多少步呢?

    快指针在环内已经走了 n圈加 X步,即 nr + X步,其中n至少为 1, 而走的总步数为 nr+X+a步。

    由于快指针走的总步数为慢指针的2倍, 因此nr+X+a =(X + a)*2

    由上式可得: a+X = nr, 即 a = nr - X = (n-1)r + r -X;

    上式的含义:环入口距离起点的距离(等于a)和相遇点距离环入口点的距离(等于 r -X)相差 (n-1)* r

    所以,让慢指针回到起点,快指针从相遇点开始继续走,步长都为1,当两者相遇时,即为环入口。此时慢指针共走了 a 步,而快指针也走了 a 步(a = (n-1)r + r -X)。代码如下:

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <string.h>
      4 #include <time.h>
      5 
      6 typedef struct tag
      7 {
      8     int Nnum_;
      9     struct tag *Nnext_;
     10 }Node, *pNode;
     11 
     12 
     13 void link_print(pNode phead, int size)
     14 {
     15     int i= 0;
     16     while(phead != NULL)
     17     {
     18         printf("%4d", phead->Nnum_);
     19         phead = phead->Nnext_;
     20         if( ++i > size)
     21             break;
     22     }
     23     printf("
    ");
     24 }
     25 //尾插法
     26 void link_init_tail(pNode *phead, int size) //传的是地址
     27 {
     28     pNode pNew = NULL;
     29     pNode pTail = NULL;
     30 
     31     while( size > 0)
     32     {
     33         //申请内存
     34         pNew = (pNode)malloc(sizeof(Node)); //注意这里为何不用pNode而用Node,因为sizeof(pNode) = 4
     35         //赋值
     36         pNew->Nnum_ = rand()%1000;
     37         //插入链表
     38         if(*phead == NULL) //链表为空时
     39         {
     40             *phead = pNew;//连接新的结点
     41             pTail = pNew;
     42         }
     43         else//不为空
     44         {
     45             pTail->Nnext_ = pNew ; //连接新的结点
     46              pTail = pNew; //改名字
     47         }    
     48         size --;
     49     }
     50     //生成一个带环的链表
     51     pNode pCur = *phead;
     52     int i =0;
     53     while( i < 8) //attention
     54     {
     55         pCur = pCur->Nnext_;
     56         i++;
     57     }
     58     pTail->Nnext_ = pCur; //生成一个环
     59 }
     60 
     61 pNode find_entrance_of_loop(pNode phead)
     62 {
     63     pNode pFast = phead; //快指针
     64     pNode pSlow = phead; //慢指针
     65 
     66     while( pFast->Nnext_ != NULL) //找环相遇点
     67     {    
     68         pSlow = pSlow->Nnext_;
     69         pFast = pFast->Nnext_;
     70         pFast = pFast->Nnext_; 
     71         if(pFast == NULL)//没有这句,若length为偶数,则会Segmentation fault (core dumped)
     72         {
     73             printf("there is no loop
    ");
     74             return NULL;
     75         }
     76         if( pSlow == pFast) //有环存在
     77             break;
     78     }
     79     if( pFast == NULL) //无环
     80     {
     81         printf("there is no loop
    ");
     82         return NULL;    
     83     }
     84     //有环
     85     pSlow = phead; 
     86     while( pSlow != pFast) //在环入口位置相遇
     87     {
     88         pSlow = pSlow->Nnext_;
     89         pFast = pFast->Nnext_;        
     90     }
     91     return pSlow;
     92 }
     93 
     94 int main(int argc, char const *argv[])
     95 {
     96     srand(time(NULL));
     97     pNode phead = NULL;
     98     link_init_tail(&phead, 25); //10为初始链表的长度
     99     //从init中可以看出,若真正打印出的数字的长度为15(> 链表的初始长度),则说明存在环
    100     printf("Before:
    ");
    101     link_print(phead,40); 
    102 
    103     pNode pMeetPos = find_entrance_of_loop(phead);
    104     printf("After:
    ");
    105     link_print(pMeetPos, 5);
    106     return 0;
    107 }
    View Code
  • 相关阅读:
    ABP 番外篇-容器
    ABP 番外篇-菜单
    三、写服务
    十二、异步
    一、PHP_OSS使用
    十一、泛型
    Automapper
    ABP实践学习
    【2019-07-26】省是缺点
    【2019-07-25】女人,很强大
  • 原文地址:https://www.cnblogs.com/xfxu/p/4088878.html
Copyright © 2011-2022 走看看