面试题 单链表中判断是否有环以及得出环的入口点

参考链接:单链表中判断是否有环以及得出环的入口点(简单易懂)

1.       判断单链表是否有环

这是很多公司入门级的面试笔试题。单链表由于每个结点只有一个next指针指向下一个结点,不存在其他指针,所以一旦进入环里,就再也出不去了。类似于下图(像一个烤盘)

      

那么怎么样判断单链表是否有环呢?方法很简单,把环看作操场,两位选手在操场上赛跑,一个速度快,一个速度慢,如果他们一直跑,快的选手肯定会在第一次超过慢的选手之后再次追上慢的选手,与他相遇;若现在没有环,那么快的选手将始终跑在慢的选手前面,不可能有第二次相遇。所以我们判断一个链表是否有环的方法就是基于这个思想:

        在链表头部设两个指针,一个每次向后移动两个位置(快指针),另一个每次向后移动一个位置(慢指针)。这相当于在起点设置了两位跑步选手,跑的快的选手的速度是慢的选手的两倍。接下来两个指针在遍历链表过程中,如果快指针到达链表尾还没有和慢指针相遇,说明链表无环,反之说明有环。

2.       获得环的入口点

书上说的公式挺复杂的,我们这里只要考虑两个指针第一次相遇的情况就好了。第一次相遇的条件是:快指针在环内比慢指针多走了一圈。如下图所示,两个指针从起点O出发,在环中的B点相遇,现在我们的目标是如何根据B点找到A点这个结点。注意:结点在环中的前进方向是顺时针,即A→B→A.

              

毫无疑问,两个指针第一次在B点相遇时:

慢指针走过的路程是:

S_slow = |OA| +|AB| = x + y.

 快指针走过的路程是:

S_fast =  |OA| + |AB| + |BA| + |AB| = x + y + z + y.

又因为快指针的速度是慢指针的两倍,所以在相同时间内快指针走过的路程是慢指针的两倍,所以

  S_fast = 2 * S_slow

 即 

 2(x + y) = x + y + z + y.

 求得 

z = x.

所以说明

|BA| = |OA|.

所以在两个指针相遇后,将慢指针移到O点起始位置,即链表头指针位置,快指针仍然在B点。然后它们一起向前移动,每次移动一个位置,由于|BA| = |OA|, 所以他们最终肯定会在A点相遇,A点这个相遇点就是环的入口点。

具体实现代码如下:

#include<stdio.h>
#include<malloc.h>
using namespace std;

typedef struct LNode{
	int data;
	struct LNode *next;
}LNode;

void CreatelistR(LNode *&head,int arr[],int n);//尾插法创建带头结点的单向链表
void ShowList(LNode *head);
LNode * Search(LNode *head,int x);

LNode *HasCircal(LNode *head);//判断是否有环,无返回nullptr,有则返回快慢结点相遇地址 
LNode *FindEnter(LNode *head);//寻找环入口点地址 

int main(){

	LNode *head = nullptr;
	int arr[] = {7,789,112,16,18,23,126,28,435,38,40,58,78,135,477,56};
	CreatelistR(head,arr,sizeof(arr)/sizeof(arr[0]));
	ShowList(head);
	
	//构造一个单向有环链表,环入口结点为435 
	LNode *p = Search(head,435);
	LNode *end = Search(head,56);
	if(p != nullptr && end != nullptr) 
	end->next = p;
	
	if(HasCircal(head) != nullptr)
		puts("有环");
	else
		puts("无环");
	
	LNode *node = FindEnter(head);	
	if(node != nullptr)
		printf("环入口结点为:%d\n",node->data);
	else
		puts("无环");
	
	return 0;
}

LNode *FindEnter(LNode *head){
	LNode *node = HasCircal(head);//相交的点
	if(node == nullptr)
		return nullptr;
	LNode *slow,*fast;
	
	slow = head;
	fast = node;
	
	while(slow != fast){
		slow = slow->next;
		fast = fast->next;
	} 
	return fast;
}

LNode *HasCircal(LNode *head){
	if(head == nullptr || head->next == nullptr)
		return nullptr;
	LNode *slow,*fast;
	
	slow = head->next;
	fast = head->next->next;
	
	while(slow != nullptr && fast != nullptr){
		if(slow == fast)
			return slow;
		slow = slow->next;
		fast = fast->next->next;
	}
	return nullptr;
}

LNode * Search(LNode *head,int x){
	if(head == nullptr)
		return nullptr;
		
	LNode *p = head;
	while(p != nullptr){
		if(p->data == x)
		{
			return p;
		}
		
		p = p->next;
	}
	
	return nullptr;
} 

void CreatelistR(LNode *&head,int arr[],int n)//尾插法 
{
	if(arr == nullptr || n < 0)
		return;
	head = (LNode *)malloc(sizeof(LNode));
	head->next = nullptr;
	head->data = arr[0];
	
	LNode *temp = head;
	for(int i=1;i<n;i++)
	{
		LNode *node = (LNode *)malloc(sizeof(LNode));
		node->data = arr[i];
		node->next = nullptr;
		
		temp->next = node;
		temp = temp->next;	
	}
}  

void ShowList(LNode *head){
	if(head == nullptr)
		return;
	while(head != nullptr)
	{
		printf("%d ",head->data);
		head = head->next;
	}
	puts("");
}

程序运行结果如下:

猜你喜欢

转载自blog.csdn.net/qq_29762941/article/details/81192184