判断单链表是否有环

前言:

其实这个题目是面试官给提的,由于当时在学习数据结构与算法的时候没有接触过这类问题,因此通过在网上查阅资料和自己的理解产生这篇博客,希望能帮得到大家。

如下面的单链表:

《判断单链表是否有环》

如何判断该链表中是否存在环。

方法一:

使用 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,加上自己的理解,如果上面代码有错的地方,欢迎指正。

点赞