面试OR笔试30——单链表环入口节点

61 篇文章 0 订阅
61 篇文章 0 订阅

1 题目及要求

1.1 题目描述

找出一个有环的单链表的入口节点,若没有环则输出空。

 

2 解答

2.1 题目分析

1、可以用两个指针来解决。先定义两个指针p1, p2连个指向链表头节点。如果链表中的环有c个节点,则指针p2在链表上向前移动n步,然后两个指针再以相同的速度移动。当p1指向环入口节点时p2已经绕着环走了一圈后又回到了环入口节点。所以解题思路如下:

1)先求环内的任意一个节点;

2)求环的长度;

3)求入口节点。

 

2、设置两个指针slow和fast,分别每次向前走一步和走两步。若有环则必会相遇,且相遇时slow肯定没有遍历完链表,而是停在环内的某一节点,而fast已经在环内走了k1圈了(1<=k1)。假设链表非环的部分长为n,环部分长为c,环入口到相遇节点的距离为d。于是有,slow走的距离为n+d,fast走的距离为n+d+k1*c。由于fast是slow速度的两倍因此有

2*(n+d) = n+d+k1*c     ==>  n+d = k1*c       ==>   n = (k1-1)*c + (c-d)

c-d为相遇节点到环入口节点的距离,由此可见,从链表头到环入口节点的距离等于k1-1圈内环长度+相遇几点到环入口的距离。于是我们可以让两个指针分别从头结点和相遇节点相同速度同时出发向前走,则必会在环入口相遇。

 

2.2 代码


#include <iostream>
using namespace std;

// 链表节点类
struct ListNode{
	int val;
	ListNode *next;
	ListNode(int x = 0):val(x),next(nullptr){}
};

ListNode* getCycleNode(ListNode* l1){
	ListNode *np(l1);
	while(np && np->next){
		l1 = l1->next;
		np = np->next->next;
		if(l1==np)return l1;
	}
	return nullptr;
}

int cycleLength(ListNode* l1){
	l1 = getCycleNode(l1);
	if(!l1) return 0;
	ListNode *np(l1->next);
	int res(1);
	while(np!=l1) {
		++res;
		np = np->next;
	}
	return res;
}

ListNode* getEnterNode1(ListNode* l1){
	ListNode *np(l1);
	int k1 = cycleLength(l1);
	if(k1<1)return nullptr;
	for(;0<k1;--k1)
		np = np->next;
	while(l1!=np){
		l1=l1->next;
		np=np->next;
	}
	return l1;
}

ListNode* getEnterNode(ListNode* l1){
	ListNode *slow(l1), *fast(l1);
	while(fast && fast->next){
		slow = slow->next;
		fast = fast->next->next;
		if(slow==fast) {
			while(l1!=slow){
				l1=l1->next;
				slow=slow->next;
			}
			return l1;
		}
	}
	return nullptr;
}

int main(){
	ListNode ht[7]={1,2,3,4,5,6,7};
	for(int k1(0);k1<6;++k1)ht[k1].next = ht+k1+1;

	ListNode *np1(nullptr),*np2(nullptr);
	cout << ">>>>>>>空链表:" << endl;
	cout << "链表内环长度   : " << cycleLength(ht) << endl;
	np1 = getEnterNode1(nullptr);
	np2 = getEnterNode(nullptr);
	cout << "链表环入口节点1: ";
	np1 ? (cout << np1->val):(cout << "NULL"); cout << endl;
	cout << "链表环入口节点2: ";
	np2 ? (cout << np2->val):(cout << "NULL"); cout << endl;
	
	cout << endl << ">>>>>>>无环链表:" << endl;
	cout << "链表内环长度   : " << cycleLength(ht) << endl;
	np1 = getEnterNode1(ht);
	np2 = getEnterNode(ht);
	cout << "链表环入口节点1: ";
	np1 ? (cout << np1->val):(cout << "NULL"); cout << endl;
	cout << "链表环入口节点2: ";
	np2 ? (cout << np2->val):(cout << "NULL"); cout << endl;
	for(int k1(6);-1<k1;--k1){
		ht[6].next = ht+k1;
		cout << endl << ">>>>>>>" << (7-k1) << "环链表:" << endl;
		cout << "链表内环长度   : " << cycleLength(ht) << endl;
		np1 = getEnterNode1(ht);
		np2 = getEnterNode(ht);
		cout << "链表环入口节点1: ";
		np1 ? (cout << np1->val):(cout << "NULL"); cout << endl;
		cout << "链表环入口节点2: ";
		np2 ? (cout << np2->val):(cout << "NULL"); cout << endl;
	}
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值