普通链表与循环链表的区别在于:普通链表的最后一个节点指向为NULL,而循环链表最后一个节点指向该链表中的任意一个节点,如同一个环。循环链表问题引出了一个在链表中很重要的概念:快、慢指针。快、慢指针可以帮助我们解决很多经典链表问题,详细如下。
普通链表与循环链表的示意图如下:
判断链表是否循环思路:定义两个指向头节点的指针,分别为fast和slow,从名字就可以看出一个指针为快指针,另一个为慢指针。定义快指针一次走两个节点的距离,而慢指针一次走一个节点的距离,入下图所示:
若fast指针走到NULL时说明链表不是一个循环链表,当fast与slow相等时,即他们指向同一个节点说明该链表是一个循环链表。 因为在循环链表中,fast是slow的两倍速度,则fast迟早会从环中追到slow,这时候他们是指向同一个节点。
代码实现:
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>typedef struct ListNode //创建节点结构体
{int data;struct ListNode* next;
}LNode;bool hasCycle(LNode* head)//bool值表示符合条件返回真,不符合条件返回假
{LNode* slow = head, * fast = head;//定义快慢指针while (fast && fast->next)//当fast或者fast的next为空时跳出循环{slow = slow->next;//slow走一步fast = fast->next->next;//fast走两步if (fast == slow)return true;//是循环链表返回真}return false;//当fast或者fast的next为空时说明不是循环链表,因此返回假
}void Print(LNode* phead)//打印函数
{LNode* cur = phead;while (cur){printf("%d->", cur->data);cur = cur->next;}printf("NULL");//模拟链表最后指向的是NULL
}int main()
{LNode* n1 = (LNode*)malloc(sizeof(LNode));//创建5个节点,为了测试函数LNode* n2 = (LNode*)malloc(sizeof(LNode));LNode* n3 = (LNode*)malloc(sizeof(LNode));LNode* n4 = (LNode*)malloc(sizeof(LNode));LNode* n5 = (LNode*)malloc(sizeof(LNode));LNode* plist = n1;//指向头节点的头指针plistn1->data = 1;//给每个节点都赋值n2->data = 2;n3->data = 3;n4->data = 4;n5->data = 5;//设置循环链表n1->next = n2;//手动构建链表n2->next = n3;n3->next = n4;n4->next = n5;n5->next = n3;//这里将n5指向n3,目的是设置循环链表if (hasCycle(plist) == false)printf("不是循环链表\n");elseprintf("是循环链表\n");return 0;
}
运行结果:
代码中我们将n5->next = n3;因此该链表为循环链表,运行结果符合逻辑。
结语:
以上就是判断链表是否循环的解法,用到的快、慢指针概念是链表中非常经典的一种解题手法,希望本文可以对你起到帮助。( ̄︶ ̄)↗