本篇主要介绍快慢指针。
快慢指针中的快慢指的是移动的步长,即每次向前移动速度的快慢。例如可以让快指针每次沿链表向前移动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("\n"); 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\n", isloop(phead)); 93 94 return 0; 95 }