程序员面试金典: 9.2链表 2.6给定有环链表,实现算法返回环路的开头节点

#include <iostream>
#include <stdio.h>

using namespace std;

/*
问题:给定一个有环链表,实现一个算法返回环路的开头节点
分析:快慢指针,一个每次走一步,一个每次走两步,如果能够相遇,必定存在环
      现在关键是如何找到环路初始节点
未解决:关键是要寻找相遇点y,环起始点x,环的长度L 三者之间的关系,目前关系忘了

书上分析:设头结点距离环的起始点距离为L,设从环起始点走了长度k处相遇,设环的总长度为m
则第一次相遇时:
快指针走的距离S1=L + m + k (1)
慢指针走的距离S2=L + k     (2)
满足:         S1=2*S2       (3)
解方程得L = m - k
该式子的意思是: 头结点距离环的起始点距离=相遇点距离环起始点的距离
推得规律: 
1 头指针和相遇点处指针同时每次走一步,那么相遇的地方就是环的起始点
2 环的长度=快慢指针在相遇点出继续走再次相遇时所走的步数

输入:
5(链表长度) 2(环的起始下标)
0 1 2 3 4(链表中每个节点的元素)
输出:
又换
换的其实地址
环的长度3

关键:
1 设头结点距离环的起始点距离为L,设从环起始点走了长度k处相遇,设环的总长度为m
则第一次相遇时:
快指针走的距离S1=L + m + k (1)
慢指针走的距离S2=L + k     (2)
满足:         S1=2*S2       (3)
解方程得L = m - k
该式子的意思是: 头结点距离环的起始点距离=相遇点距离环起始点的距离
推得规律: 
1 头指针和相遇点处指针同时每次走一步,那么相遇的地方就是环的起始点
2 环的长度=快慢指针在相遇点出继续走再次相遇时所走的步数

2 环的起始点=头结点,相遇节点分别每次走一步后,相遇之后的节点
3 环的长度=快慢指针在相遇节点处再次相遇所走的步数
*/

typedef struct Node
{
	int value;
	Node* pNext;
}Node;


//构建连边,按照尾插法来做,返回节点值到出现次数的映射,k表示环形链表的值
void buildList(int* pArray , Node* head , int num , int k)
{
	if(pArray == NULL)
	{
		return;
	}
	if(head == NULL)
	{
		return;
	}


	//尾插法: 保留最后一个结尾节点,将新生成的节点插入在结尾节点,并令结尾节点为当前节点
	Node* pLast = head;
	Node* pCyclicNode = NULL;
	for(int i = 0 ; i < num ; i++)
	{
		int value = *(pArray + i);
		Node* pNode = new Node();
		pNode->value = value;
		pLast->pNext = pNode;
		pLast = pNode;

		if(i == k)
		{
			pCyclicNode = pNode;
		}
	}

	//找到环形节点后,就令最后一个节点指向环形节点
	pLast->pNext = pCyclicNode;
}

//如果有环,则返回相遇的节点
Node* isHasCycle(Node* pHead)
{
	if(pHead == NULL)
	{
		return false;
	}
	Node* pFast = pHead->pNext;
	Node* pSlow = pFast;
	while( pSlow && pFast)
	{
		pFast = pFast->pNext->pNext;
		pSlow = pSlow->pNext;
		//相遇
		if(pSlow == pFast)
		{
			break;
		}
	}

	//如果没有环,pFast应该已经为空了
	if(pFast == NULL)
	{
		return NULL;
	}
	return pFast;
}


//寻找环形链表的起始环节点,提供相遇的节点,根据 头结点距离环形节点的距离k=相遇节点距离环形节点的距离,则从头结点和相遇节点分别继续向下走,相遇处即为所求
Node* findCyclicBeginNode(Node* pHead , Node* meetNode)
{
	Node* tempNode = meetNode;
	if(pHead == NULL || meetNode == NULL)
	{
		return NULL;
	}
	Node* pNode = pHead->pNext;
	while(pNode != meetNode)
	{
		pNode = pNode->pNext;
		meetNode = meetNode->pNext;
	}
	meetNode = tempNode;
	return pNode;
}

//计算环形链表中环的长度 = 快慢指针在相遇处继续按照快慢步走后,下次相遇时所走的步数
int calculateCycleLength(Node* pHead , Node* meetNode)
{
	if(pHead == NULL || meetNode == NULL)
	{
		return -1;
	}
	Node* pFast = meetNode->pNext->pNext;
	Node* pSlow = meetNode->pNext;
	int count = 1;
	while(pFast != pSlow)
	{
		pFast = pFast->pNext->pNext;
		pSlow = pSlow->pNext;
		count++;
	}
	return count;
}

void printList(Node* pHead)
{
	if(NULL == pHead)
	{
		return;
	}
	Node* pNode = pHead->pNext;
	while(pNode)
	{
		cout << pNode->value << " ";
		pNode = pNode->pNext;
	}
	cout << endl;
}

//注意去除环形链表中环形节点
void releaseList(Node* pHead)
{
	if(NULL == pHead)
	{
		return;
	}
	Node* pNode = pHead->pNext;
	Node* pPrevious = pHead;
	while(pNode)
	{
		Node* pDeleteNode = pNode;
		pPrevious->pNext = pNode->pNext;
		pNode = pNode->pNext;
		pPrevious = pPrevious->pNext;
		delete pDeleteNode;
	}
	//删除头结点
	delete pHead;
}


int main(int argc, char* argv[])
{
	int n , k;
	while(cin >> n >> k)
	{
		int* pArr = new int[n];
		for(int i = 0 ; i < n ; i++)
		{
			cin >> pArr[i];
		}
		Node* pHead = new Node();
		buildList(pArr , pHead , n , k);
		Node* meetNode = isHasCycle(pHead);
		Node* beginNode = findCyclicBeginNode(pHead , meetNode);
		int length = calculateCycleLength(pHead , meetNode);
		if(meetNode != NULL)
		{
			cout << "链表存在环" << endl; 
			cout << "环的起始节点值为:" << beginNode->value << endl;
			cout << "环的长度为:" << length << endl;
		}
		else
		{
			cout << "链表不存在环" << endl; 
		}
		//printList(pHead);
		releaseList(pHead);
                delete[] pArr;
 }
	system("pause");
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值