前言:
其实这个题目是面试官给提的,由于当时在学习数据结构与算法的时候没有接触过这类问题,因此通过在网上查阅资料和自己的理解产生这篇博客,希望能帮得到大家。
如下面的单链表:
如何判断该链表中是否存在环。
方法一:
使用 p、q 两个指针,p 总是向前走,但 q 每次都从头开始走,对于每个节点,看 p 走的步数是否和 q 一样。如果对于每个节点,p、q 走过的步数都是一样的,则证明不存在环,反之,存在环。
代码(C语言实现):
// param 单链表头结点指针
bool has_loop1(struct node * head){
//p、q 指针
node_t p = head,q = head; //typedet struct node * node_t
//步数
int p_count = 0,q_count = 0;
while(q != NULL && p_count == q_count){
//重置p指针和步数
p = head;
p_count = 0;
//q继续遍历链表
q = q -> next;
q_count ++;
while(p != q){
p = p -> next;
p_count ++;
}
//当 p == q 时,判断各自走过的步数
if(p_count != q_count){
return true;
}
}
return false;
}
网上也有不同的类似的解决方法:
首先从头节点开始,依次遍历单链表的每一个节点。每遍历到一个新节点,就从头节点重新遍历新节点之前的所有节点,用新节点ID和此节点之前所有节点ID依次作比较(这个节点ID直接用地址就行)。如果发现新节点之前的所有节点当中存在相同节点ID,则说明该节点被遍历过两次,链表有环;如果之前的所有节点当中不存在相同的节点,就继续遍历下一个新节点,继续重复刚才的操作。
或者是将遍历过的节点放到 hash 表(数组)中,每遍历一个新节点,将新节点和 hash 表(数组)中的旧节点作比较,如果存在,则证明该链表有环。
这些方法的核心思想无非就是将新节点与遍历过的旧节点做比较,因此我就不在展开分析。
方法二:
使用 p、q 两个指针,p 每次向前走一步,q 每次向前走两步,若在某个时候 p == q,则存在环。
该方法应该算是业界中效率最高的吧,网上的博客中都会说到这个方法。
其实理解这个方法不是很难,这个方法就好比有两个人甲、乙在跑道上(环状)跑步(同时同起点出发),乙的速度是甲的两倍,总有某一个时刻乙会追上甲,此时乙跑的路程比甲多一圈。回归到我们的问题,q 的速度是 p 的两倍,那么如果存在环的话,q 和 p 在某个时刻总会相遇的(即 p == q)。
代码:
//param 单链表头结点指针
bool has_loop2(struct node * head){
//同起点
node_t p = head,q = head;
while(p != NULL && q != NULL){
p = p->next;
q = q->next;
if(q != NULL){
q = q->next;
}
//这里 q=q->next->next 分开是为了防止段错误
//p 和 q 相遇
if(p != NULL && q != NULL && p == q){
return true;
}
}
return false;
}
结尾:
现在我们来看看客户端代码:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
//链表长度
#define LEN 8
//节点结构
struct node{
char var;
struct node * next;
};
typedef struct node * node_t;
//方法一:计算步长
bool has_loop1(struct node * head);
//方法二:判断两个指针是否相遇
bool has_loop2(struct node * head);
int main(void){
node_t head = (node_t)malloc(sizeof(struct node));
node_t pre = head;
node_t link = NULL;
int i;
for(i = 0;i < LEN;i ++){
node_t new_node = (node_t)malloc(sizeof(struct node));
pre->next = new_node;
pre = pre->next;
if(i == 2){
link = pre;
}
}
pre->next = pre;
if(has_loop1(head)){
printf("存在环\n");
}else{
printf("不存在环\n");
}
if(has_loop2(head)){
printf("存在环\n");
}else{
printf("不存在环\n");
}
return 0;
}
本博客参考自 http://www.cnblogs.com/shuaiwhu/archive/2012/05/03/2480509.html,加上自己的理解,如果上面代码有错的地方,欢迎指正。