链表的环问题

问题:如何判断单链表是否有环?如何找到环的起始点?如何知道环的长度。


方法:使用快慢指针法,即快指针每次移动两个节点,慢指针每次移动一个节点


(1)判断是否有环

如图所示,假设有两个节点fast,slow分别指向链表头,即图中的节点1所在的地址。然后令

slow = slow->next;
fast = fast->next->next;
然后循环执行知道slow等于fast时,即可推断出环的存在。注意循环条件为fast != NULL && fast->next != NULL。

代码如下所示:

#include <stdio.h>
#include <stdlib.h>


typedef struct node
{
    int data;
    struct node * next;
}Node;


Node * get_circle_link(int length, int local)
{
    Node *temp = NULL;
    Node *phead = NULL;
    Node *tail = NULL;


    int i;


    //创建单链表
    for (i = 0; i < length; i++)
    {
        temp = (Node *)malloc(sizeof(Node));
	temp->next = NULL;
	temp->data = length - i;
	if (phead == NULL)
	{
	    tail = temp;
	}


	temp->next = phead;
	phead = temp;
    }


    //形成环
    for (temp = phead, i =1; i < local; i++)
    {
	temp = temp->next;
    }
    tail->next = temp;


    return phead;
}


void link_print(Node *temp)
{
    while (temp != NULL)
    {
	printf("%p:%d\t",temp,temp->data);
	temp = temp->next;
	getchar();
    }
}


//利用快慢指针判断链表是否有环
int get_circle_local(Node *phead)
{
    Node * fast = phead;
    Node * slow = phead;


    while (fast != NULL && fast->next != NULL)
    {
	fast = fast->next->next;	//fast一次跳两个节点
	slow = slow->next;	//slow一次跳一个节点


	if (fast == slow)
	{
	    return 1;
	}
    }
    return 0;
}
int main()
{
    Node * phead = NULL;
    int local = 0;
    int number = 0;
 
    scanf("%d",&number); 	//链表的节点数
    scanf("%d",&local);		//链表环的节点数


    if (number < local || local <= 0 || number <= 0)
    {
	printf("number MUST >= local, and MUST be positive integer\n");
	exit(EXIT_FAILURE);
    }


    phead = get_circle_link(number,local);
    //link_print(phead);


    int ret = 0;
    if (get_circle_local(phead) == 1)
    {
	printf("has circle\n");
    }
    else
	printf("no circle\n");


    return 0;
}


输入:6 3

运行结果:



(2) 寻找环的起始点


假设节点1到节点3距离为L,而节点3到节点5的距离为A,则有L + A = (L + A + N*X)/2,其中N为快捷点与慢节点相遇时走过的多少个环,X为1个环的环数。

因此,当判断fast=slow时,将slow和fast节点的步长都改为1步,即

slow = slow->next;
fast = fast->next;

当slow再次等于fast时,相遇的节点即为起始点

程序为:

#include <stdio.h>
#include <stdlib.h>

typedef struct node
{
	int data;
	struct node * next;
}Node;

Node * get_circle_link(int length, int local)
{
	Node *temp = NULL;
	Node *phead = NULL;
	Node *tail = NULL;

	int i;

	//创建单链表
	for (i = 0; i < length; i++)
	{
		temp = (Node *)malloc(sizeof(Node));
		temp->next = NULL;
		temp->data = length - i;
		if (phead == NULL)
		{
			tail = temp;
		}

		temp->next = phead;
		phead = temp;
	}

	for (temp = phead, i =1; i < local; i++)
	{
		temp = temp->next;
	}
	tail->next = temp;

	return phead;
}

void link_print(Node *temp)
{
	while (temp != NULL)
	{
		printf("%p:%d\t",temp,temp->data);
		temp = temp->next;
		getchar();
	}
}

//利用快慢指针判断链表是否有环
int get_circle_local(Node *phead)
{
	Node * fast = phead;
	Node * slow = phead;

	while (fast != NULL && fast->next != NULL)
	{
		fast = fast->next->next;	//fast一次跳两个节点
		slow = slow->next;			//slow一次跳一个节点

		if (fast == slow)
		{
			//return 1;
			for (slow = phead; slow != fast;)
			{
				slow = slow->next;
				fast = fast->next;
			}
			return slow->data;

		}
	}
	return 0;
}
int main()
{
	Node * phead = NULL;
	int local = 0;
	int number = 0;
	
	scanf("%d",&number);	//链表的节点数
	scanf("%d",&local);	//链表环的节点数

	if (number < local || local <= 0 || number <= 0)
	{
		printf("number MUST >= local, and MUST be positive integer\n");
		exit(EXIT_FAILURE);
	}

	phead = get_circle_link(number,local);
	//link_print(phead);

	int ret = 0;
	if ((ret = get_circle_local(phead)) > 0)
	{
		printf("has circle,local is %d\n",ret);
	}
	else
		printf("no circle\n");

	return 0;
}
输入:6 4

结果为:


(3)判断环的长度

当判断链表有环(slow==fast)时,设置fast的步长为2,slow的步长为1,即

slow = slow->next;
fast = fast->next->next;

当下次再次相遇时,两者的走过的距离差即为环的长度。

 
 
<span style="font-size:18px;">程序如下:</span>
<pre name="code" class="cpp">#include <stdio.h>
#include <stdlib.h>

typedef struct node
{
	int data;
	struct node * next;
}Node;

Node * get_circle_link(int length, int local)
{
	Node *temp = NULL;
	Node *phead = NULL;
	Node *tail = NULL;

	int i;

	//创建单链表
	for (i = 0; i < length; i++)
	{
		temp = (Node *)malloc(sizeof(Node));
		temp->next = NULL;
		temp->data = length - i;
		if (phead == NULL)
		{
			tail = temp;
		}

		temp->next = phead;
		phead = temp;
	}

	for (temp = phead, i =1; i < local; i++)
	{
		temp = temp->next;
	}
	tail->next = temp;

	return phead;
}

void link_print(Node *temp)
{
	while (temp != NULL)
	{
		printf("%p:%d\t",temp,temp->data);
		temp = temp->next;
		getchar();
	}
}

//利用快慢指针判断链表是否有环
int get_circle_local(Node *phead)
{
	Node * fast = phead;
	Node * slow = phead;

	int temp = 0;
	while (fast != NULL && fast->next != NULL)
	{
		fast = fast->next->next;	//fast一次跳两个节点
		slow = slow->next;			//slow一次跳一个节点

		if (fast == slow)
		{

			while (fast != NULL && fast->next != NULL)
			{
				fast = fast->next->next;
				slow = slow->next;
				
				temp++;
				if (fast == slow)
				{
					return temp;
				}
			}
		}
	}
	return 0;
}
int main()
{
	Node * phead = NULL;
	int local = 0;
	int number = 0;
	
	scanf("%d",&number);	//链表的节点数
	scanf("%d",&local);	//链表环的节点数

	if (number < local || local <= 0 || number <= 0)
	{
		printf("number MUST >= local, and MUST be positive integer\n");
		exit(EXIT_FAILURE);
	}

	phead = get_circle_link(number,local);
	//link_print(phead);

	int ret = 0;
	if ((ret = get_circle_local(phead)) > 0)
	{
		printf("has circle,local is %d\n",ret);
	}
	else
		printf("no circle\n");

	return 0;
}


 输入:6 4 
 
 
 
运行结果为:
<img src="https://img-blog.csdn.net/20140913223547183" alt="" />
 
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mengrennwpu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值