一、什么是回环链表
二、如何判断一个链表有回环?
- 方法有:
- 定义两个指针p1和p2,指向于头结点
- 两个指针循环遍历链表,假设p1每次向前走1步,p2每次向前走2步
- 如果p2遇到了NULL指针,或者p1与p2指针相等时循环结束
- 如果是p2遇到了NULL指针结束,则链表没有环
- 如果是p1与p2指针相等导致的循环结束,则链表存在环
实现代码如下
/**
* @description: 判断链表是否有回环
* @param:
_head: 链表的头结点
start: 保存回环点相遇点
* @return: true/false
* @author: Dongshao
*/
bool isLoop(Node* _head, Node**start)
{
if(_head == NULL || _head->_pNext == NULL)
return false;
Node *temp1 = _head;
Node *temp2 = _head;
// 向后遍历
do {
// temp1每次走1步, temp2每次走2步
temp1 = temp1->_pNext;
temp2 = temp2->_pNext->_pNext;
// 因为temp2每次向后走2步, 因此如果其下一步为NULL, 则下下步就会报错, 因此需要判断temp2->_pNext != NULL
} while((temp2 != NULL) && (temp2->_pNext != NULL) && (temp1 != temp2));
// 如果指针相等, 则有回环
if(temp1 == temp2)
{
// 保存temp1和temp2相遇的那个节点指针, 并返回出去
*start = temp1;
return true;
}
return false;
}
测试代码如下
#include <iostream>
#include <stdlib.h>
using namespace std;
typedef struct node
{
struct node* _pNext;
int _data;
}Node;
/**
* @description: 判断链表是否有回环
* @param:
_head: 链表的头结点
start: 保存回环开始节点指针
* @return: true/false
* @author: Dongshao
*/
bool isLoop(Node* _head, Node**start)
{
if(_head == NULL || _head->_pNext == NULL)
return false;
Node *temp1 = _head;
Node *temp2 = _head;
// 向后遍历
do {
// temp1每次走1步, temp2每次走2步
temp1 = temp1->_pNext;
temp2 = temp2->_pNext->_pNext;
// 因为temp2每次向后走2步, 因此如果其下一步为NULL, 则下下步就会报错, 因此需要判断temp2->_pNext != NULL
} while((temp2 != NULL) && (temp2->_pNext != NULL) && (temp1 != temp2));
// 如果指针相等, 则有回环
if(temp1 == temp2)
{
// *start保存回环开始节点指针并返回
*start = temp1;
return true;
}
return false;
}
/**
* @description: 创建链表
* @param:
num: 链表节点数量
* @return: 创建新链表的头指针
* @author: Dongshao
*/
Node* createList(int num)
{
if(num < 0)
return NULL;
Node *_head = (Node*)malloc(sizeof(Node));
_head->_pNext = NULL;
Node *_tempHead = _head;
Node *_newNode;
for(int i = 0; i < num; ++i)
{
// 创建新节点
_newNode = (Node*)malloc(sizeof(Node));
_newNode->_data = (i+1);
_newNode->_pNext = NULL;
// 向后偏移
_tempHead->_pNext = _newNode;
_tempHead = _newNode;
_newNode = NULL;
}
return _head;
}
/**
* @description: 打印链表
* @param:
_head: 链表的头结点
* @return: 无
* @author: Dongshao
*/
void showList(Node *_head)
{
if(_head == NULL || _head->_pNext == NULL)
return;
Node *_tempNode = _head;
// 循环打印
while(_tempNode->_pNext != NULL)
{
if(_tempNode->_pNext->_pNext == NULL)
break;
else
printf("%d->", _tempNode->_pNext->_data);
_tempNode = _tempNode->_pNext;
}
// 打印最后一个节点的
printf("%d\n", _tempNode->_pNext->_data);
}
/**
* @description: 销毁链表
* @param:
_head: 链表的头结点
* @return: 无
* @author: Dongshao
*/
void freeList(Node* _head)
{
if(_head == NULL || _head->_pNext == NULL)
return;
Node *temp1 = _head->_pNext;
Noode *temp2
while(temp1 != NULL)
{
temp2 = temp1->_pNext;
free(temp1);
temp1 = temp2;
}
}
int main()
{
Node *headList = NULL;
// 创建链表, 并打印链表
headList = createList(7);
showList(headList);
// temp1指向值为4的那个节点, temp2指向尾节点
Node *temp1, *temp2;
temp1 = headList->_pNext->_pNext->_pNext->_pNext;
temp2 = headList->_pNext;
while(temp2->_pNext != NULL)
temp2 = temp2->_pNext;
// 此处我们改变链表, 让temp2指向于temp1造成回环
temp2->_pNext = temp1;
// 判断是否有回环, 并打印回环开始节点的指针(为4)
Node *start;
std::cout << boolalpha << "isLoop: " << isLoop(headList, &start) << std::endl;
std::cout << "loopNode data: " << start->_data << std::endl;
// 释放链表
freeList(headList);
return 0;
}
三、环的连接点在哪?
- 从上面我们知道,p1和p2第一次的碰撞点就是环的连接点
四、如何判断环的长度?
- 从上面我们知道,p1和p2第一次的碰撞点就是环的连接点
- 因此当第一次碰撞之后,从碰撞点继续遍历,当再次碰撞时判断走过的操作数就是该环的长度
五、带环链表的长度为多少?
- “头结点到回环节点的长度 + 环的长度”即为带环链表的长度