单链表是否有环及双链表相交问题

1.单链表是否有环及求环入口


(1)判断单链表是否有环

方法很简单,设置两个指针,如slow和fast,都指向头结点,slow每次走一步,fast每次走两步,如果slow和fast相遇,则说明链表是有环的

这样设置两个指针的方法在一些链表问题上很有帮助的。

例如:a.如何在仅遍历链表一遍的情况下求无环链表的中心节点,即最中间的那个节点。这时就是设置快慢两个指针,快指针每次走两步,慢指针每次走一步,当快指针走到链表尾节点时,慢指针也就走到中间节点了。

   b.求无环链表的倒数第k个节点。同理,设置两个指针,快指针先走k-1步,然后两个指针同时走,这样,当快指针走到尾节点时,慢指针的位置刚好就是倒数第K个节点。

判断单链表是否有环的代码:

bool is_exit_loop(ListNode * head)
{
	//空指针的情况
	if(head == NULL)
		return false;
	ListNode *slow = head, *fast = head;
	while(fast != NULL && fast->next != NULL)
	{
		slow = slow->next;
		fast = fast->next->next;
		if(slow == fast)
			break;
	}
	if(fast != NULL && fast->next != NULL)
		return true;
	else
		return false;
}
求无环链表中心节点的代码:

ListNode * find_mid_node(ListNode * head)
{
	//头结点为空
	if(head == NULL)
		return NULL;
	//只有一个节点的情况,中心节点即为头结点
	if(head->next == NULL)
		return head;
	
	ListNode * one_step = head, *two_step = head;
	while(two_step != NULL && two_step->next != NULL)
	{
		one_step = one_step->next;
		two_step = two_step->next->next;
	}
	//当节点的个数为偶数个,中心节点有两个,这里输出较后的那个
	return one_step;
}

求无环链表倒数第k个节点的代码:

ListNode * find_Kth_to_tail(ListNode * head, unsigned int k)
{
	if(head == NULL || k == 0)
		return NULL;
	//这里要注意一个问题就是,链表的长度小于k
	ListNode * slow = head, * fast = head;
	unsigned int step = 1;
	while(step < k )
	{
		//链表长度小于K的情况
		if(fast->next == NULL)
			return NULL;
		step ++;
		fast = fast->next;
	}
	if(fast->next == NULL)	//链表长度刚好为K
		return slow;
	else
	{
		while(fast->next != NULL)
		{
			fast = fast->next;
			slow = slow->next;
		}
		return slow;
	}

}


(2)找有环链表的入口。


设置连个指针,slow和fast,slow每次走一步,fast每次走两步。定义一些变量:链表环长度为r,链表头结点到环的入口距离为a,环的入口到两指针相遇点的距离为b

假设fast走了k步后,指针slow和fast相遇了。

我们可以得出下列关系式:  k = 2 * k - n * r(n >= 1),即 k = n * r,

 a + b = k;

n * r = a + b  => a = (n - 1) * r + r - b;

r - b为指针相遇点到环入口的距离,即从链表头走到环入口等于从环内(n-1)次循环+从相遇点走到环入口。

所以,得到相遇点后,然后设置两个指针,一个从头结点出发,一个相遇点出发,每次都走一步,则两指针第一次相遇的地方就是环的入口。

代码如下:

ListNode * find_loop_entry(ListNode * head)
{
	//这里默认链表已经有环,所以就进行链表是否有环的判断
	if(head == NULL)
		return NULL;
	ListNode * slow = head, * fast = head;
	while(fast != NULL && fast->next != NULL)
	{
		slow = slow->next;
		fast = fast->next->next;
		//找到相遇点
		if(slow == fast)
			break;
	}
	//找到相遇点后,让slow节点从头结点出发,fast节点从相遇点出发,二者第一次相遇的地方即为链表的环入口
	slow = head;
	while(slow != fast)
	{
		slow = slow->next;
		fast = fast->next;
	}
	return slow;
}

2.两个链表是否相交的问题


判断方法:先判断两个链表是否有环(1)若一个有环,一个无环,则两链表肯定是不相交的

   (2)若两个都是无环链表,则很容易,判断方法比较多,

方法1:暴力破解,设两个链表分别为链表1和链表2,依次判断链表2的每个节点是否在链表1中

方法2:分别遍历两个链表,求出每个的尾节点,若二者的尾节点相同,则说明两个链表是相交的,否则不相交

方法3:将链表2接在链表1的后面,然后判断链表1是否有环,若有环的话,则两链表相交,否则两链表不相交

 (3)若两个链表都是有环链表,方法如下:如果两个链表相交,那么一个链表环的入口一定在另一个链表上,得到一个链表的环入口,然后对另一个链表遍历,依次判断该环入口是否在另一个链表上。

代码如下:

bool if_two_linklist_intersect(ListNode * head1, ListNode * head2)
{
	//假设已知两链表都有环,所以这里不再进行是否都有环的判断
	if(head1 == NULL || head2 == NULL)
		return false;
	ListNode * list1_loop_entry = find_loop_entry(head1);	//得到链表1的环入口
	ListNode * list2_loop_entry = find_loop_entry(head2);	//得到链表2的环入口
	ListNode * start = head2;
	//对链表2遍历,当start节点第二次到达list2_loop_entry时,表示已经完成对链表2的遍历
	int times = 0;	//表示start节点经过list2_loop_entry节点的次数
	while(times < 2)
	{
		if(list1_loop_entry == start)
			break;
		if(list2_loop_entry == start)
			times ++;
		start = start->next;
	}
	if(times == 2)	//如果start完成了对链表2的遍历,则表示两链表不相交
		return false;
	else
		return true;
}


这里对于两个链表的公共交点问题,因为若两链表相交的话,公共交点可能不止一个。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值