/*
* @Author: xgh
* @Date: 2020-06-14 14:18:08
* @LastEditTime: 2020-06-14 22:39:38
* @LastEditors: Please set LastEditors
* @Description: 判断单链表中是否带环
* @FilePath: \VS-CODE-C\.vscode\loopSinglyLinkedLists\loopSilnglyLinkedLists.c
*/
#include "data.h"
Statue GreateListTail(LinkList *L, int n);
Statue GetLength(LinkList L);
void ListElemOutput(LinkList L);
Statue setLoopLinkList(LinkList L, int loop_pos);
int isLoopLinkList_01(LinkList L);
int isLoopLinkList_02(LinkList L);
int main(void){
LinkList list = NULL;
GreateListTail(&list, 6);
printf("表长度为:%d\n", GetLength(list));
ListElemOutput(list);
if(setLoopLinkList(list, 4)){
printf("快慢指针方式,环入口为第%d个结点\n\n", isLoopLinkList_01(list));
printf("记步数方式,环入口为第%d个结点\n\n", isLoopLinkList_02(list));
}
return 0;
}
/**
* @description: 输出表内容
* @param {LinkList L: 结构体一级指针}
* @return: NULL
*/
void ListElemOutput(LinkList L)
{
LinkList p = L->next;
printf("当前表数据为:");
while(p){
printf("%4d", p->data);
p = p->next;
}
printf("\n");
}
/**
* @description: 获取表长度
* @param {LinkList L:结构体指针}
* @return: 表长度
*/
Statue GetLength(LinkList L)
{
int length = 0; // 保存表长度变量
LinkList p = L->next; // 获取头结点的下一个结点地址
while (p != NULL) // 结点指针域不为NULL, 说明有数据
{
length++; // 长度加1
p = p->next; // 获取下一个结点地址
}
// 返回结果
return length;
}
/**
* @description: 尾插法创建链表, 数据域data存放随机数[1, 100]
* @param {LinkList *L:结构体二级指针}
* @param {int n: 插入数据的个数}
* @return: FAILUER: 内存分配失败
* @return: SUCCESS: 插入成功
*/
Statue GreateListTail(LinkList *L, int n)
{
LinkList r, s;
int i;
srand(time(0)); // 初始化随机数种子
//获取头指针
*L = (LinkList) malloc (sizeof(node));
if(*L == NULL){
printf("内存分配失败\n\n");
return FAILURE;
}
r = *L; // 将内存分配的头指针给到r
for(i = 0; i < n; i++){
s = (LinkList) malloc (sizeof(node)); // 新结点
s->data = rand() % 100 + 1; // rand()生成随机数, 对100取模, 生成[0, 99]的数, 加1生成[1, 100]
r->next = s; // r指向新结点
r = s; //将r移动到新结点, 即现在r为新结点
}
r->next = NULL; // 表尾
return SUCCESS;
}
/**
* @description: 设置带环的单链表
* @param {LinkList L:一级指针}
* @param {int loop_pos:构建环的位置}
* @return: ERROR: 数据错误
* @return:SUCCESS: 设置成功
*/
Statue setLoopLinkList(LinkList L, int loop_pos){
// 判断链表是否为空或者构建回环的位置是否正确
if(L == NULL || GetLength(L) == 0 || loop_pos >= GetLength(L)){
return ERROR;
}
// 头结点
LinkList node = L;
// 第loop_pos位置的结点
LinkList loop_node = NULL;
int i = 0;
// 循环判断, 到表尾退出
while(1){
// 判断是否到达表尾
if(node->next != NULL){
// 未到达表尾, 则结点后移
node = node->next;
i++; // 结点数加1
// 判断是否到达设置回环的结点
if(i == loop_pos){
// 获取该结点
loop_node = node;
}
}else{ // 到达表尾退出
break;
}
}
// 循环结束, 此时node为表尾结点, 将它的指针域指向回环的结点
node->next = loop_node;
printf("单链表回环设置成功\n\n");
return SUCCESS;
}
/**
* @description: 判断单链表是否是带环(快慢指针方式)
* @param {LinkList L: 一级指针}
* @return: 环的位置
* @return: ERROR: 数据错误或没有环
*/
int isLoopLinkList_01(LinkList L){
if(L == NULL || L->next == NULL){
printf("表错误\n\n");
return ERROR;
}
LinkList slow = L; // 慢指针
LinkList fast = L; // 快指针
int pos = 0; // 相遇标志
int loop_In = 0; // 到环入口的结点数
// 判断有没有环
while(fast != NULL && fast->next != NULL ){
fast = fast->next->next;
slow = slow->next;
// 快慢指针是相遇
if(fast == slow){
pos = 1;
break; // 跳出循环
}
}
// 在有环的基础上找到第一个入口
if(pos == 1){
fast = L; // 快指针从头开始走,且这次一次只后移一个结点
// 快慢指针是否相遇
while(fast != slow){
// 快慢指针后移一个结点
fast = fast->next;
slow = slow->next;
loop_In++;
}
printf("快慢指针的方式,环入口的结点数据为%d\n\n", slow->data);
return loop_In;
}
return ERROR;
}
/**
* @description: 判断单链表是否是带环(计算步数方式)
* @param {LinkList L: 一级指针}
* @return: 环的位置
* @return: ERROR: 数据错误或没有环
*/
int isLoopLinkList_02(LinkList L){
int count_01 = 0, count_02; // 计算两个指针的步数
LinkList p1 = L, p2;
while(p1)
{
count_02 = 0; // 每次指针p1后移一个结点, 指针p2都从新计数
p2 = L;
// 每次指针p2重新走一次, 走到指针p1的位置
while(p2 && (p1 != p2)){
p2 = p2->next;
count_02++;
}
// 判断是不是有环, 有环则是屏p1 == p2, 且步数不一样
if(p1 == p2 && (count_01 != count_02)){
printf("记步数的方式,环入口的结点数据为%d\n\n", p2->data);
return count_02;
}
// 指针p1往后移一个结点, 步数加一
p1 = p1->next;
count_01++;
}
return ERROR;
}