单向链表相交的第一个公共结点, 判断链表是否有环以及环的入口节点

//题目:两个单向链表,找出它们的第一个公共结点。
//
//链表的结点定义为:
//
//struct ListNode
//
//{
//
//      int       m_nKey;
//
//      ListNode*   m_pNext;
//
//};
//
//分析:这是一道微软的面试题。微软非常喜欢与链表相关的题目,因此在微软的面试题中,链表出现的概率相当高。
//如果两个单向链表有公共的结点,也就是说两个链表从某一结点开始,它们的m_pNext都指向同一个结点。
//但由于是单向链表的结点,每个结点只有一个m_pNext,因此从第一个公共结点开始,
//之后它们所有结点都是重合的,不可能再出现分叉。所以,两个有公共结点而部分重合的链表,
//拓扑形状看起来像一个Y,而不可能像X。

#include <iostream>
#include <algorithm>
using namespace std;

#define NSIZ 101

struct ListNode 
{ 

	int m_nKey; 
	ListNode*  m_pNext; 

}; 

//获得链表长度
int GetListLength(ListNode * head)
{
	if(!head)
	{
		return 0;
	}
	int len = 0;
	ListNode * tmp = head;

	while(tmp)
	{
		++len;
		tmp = tmp->m_pNext;
	}
	return len;
}

//尾插法构造链表
void CreateList(ListNode * & head, int arr[], int n)
{
	ListNode * p = head;
	if(head == 0)
	{
		head = new ListNode();
		head->m_nKey = arr[0];
		p = head;
	}
	int i = 1;
	for(;i < n; ++i)
	{
		ListNode * tmp = new ListNode();
		tmp->m_nKey = arr[i];
		p->m_pNext = tmp;
		p = tmp;
	}
	p->m_pNext = 0;
}

//合并2个链表
ListNode* UnionList(ListNode * head1, ListNode * head2)
{

	if(head1 == 0 && head2)
	{
		return head2;
	}
	else if(head1 && !head2)
	{
		return head1;
	}
	else if(head1 && head2)
	{
		ListNode * pre = head1;
		ListNode * p = pre->m_pNext;
		while(p)
		{
			pre = p;
			p = p->m_pNext;
		}

		pre->m_pNext = head2;
		return head1;
	}
	else
	{
		return 0;
	}
}

//打印链表元素值
void dump(ListNode * head)
{
	ListNode * p = head;
	while(p)
	{
		printf("%d ", p->m_nKey);
		p = p->m_pNext;
	}

	printf("\n");
}

//判断两个链表是否有交点,如果有则返回第一个交点
//如果head1 && head1 == head2那么显然相交,返回head1
//否则 分别获得head1链表和head2链表的长度为len1和len2 ,并且假设len1 > len2
//然后用指针pLong从head1开始向后移sub = len1 - len2步,指针pShort = head2
//然后p1,p2每次前进一步并且比较p1 == p2,若不为空且相等则返回p1,
//否则说明head1链表和head2链表没有交点
ListNode * FirstCommonNode(ListNode * head1, ListNode * head2)
{
	int len1 = GetListLength(head1);
	int len2 = GetListLength(head2);
	ListNode * Short = head1, *Long = head2;
	int sub = len2 - len1;
	if (len1  > len2)
	{
		Short = head2;
		Long = head1;
		sub  = len1 - len2;
	}

	while(sub > 0)
	{
		Long = Long->m_pNext;
		--sub;
	}

	while(Short && Long && Short != Long)
	{
		Short = Short->m_pNext;
		Long = Long->m_pNext;
	}

	if (Short && Long && Short == Long)
	{
		return Short;
	}

	return 0;
}

//检测链表中是否有环,快慢指针方法
//若有环则返回从头结点进入环的第一个节点
//假设p1 = head1, p2 = head->next->next
//首先检查是否有环,有环的话p1,p2必然在环内相遇
//此时从相遇处端口,方法为
//common = p2;
//head2 = p2->next, p2 ->next=  0;
//此时原链表被分成了2个链表,头结点分别为head1, head2
//利用FirstCommonNode(ListNode * head1, ListNode * head2)函数得到相交节点FirtNode
//然后修复链表:common->next= head2
//最后返回FirstNode
ListNode * HasLoop(ListNode * head)
{
	if (!head)
	{
		return 0;
	}

	ListNode * slow = head;
	ListNode * quick = head;
	while(quick && quick->m_pNext)
	{
		slow = slow->m_pNext;
		quick = quick->m_pNext->m_pNext;
		if (slow == quick)
		{
			break;
		}
	}

	// 无环的话
	if (slow != quick )
	{
		return 0;
	}

	// p1表示相遇的点, p2表示p1->next
	//然后把p1->next 置为0
	//那么原链表形成了2个相交链表,一个以head为表头,一个以p2为表头
	ListNode * p1 = quick;
	ListNode * p2 = p1->m_pNext;
	p1->m_pNext = 0;
	ListNode * firtNode = FirstCommonNode(head,p2);

	p1->m_pNext = p2;
	return firtNode;
}


//给定单链表中某一个节点P,删除该节点(P节点不是最后节点)
//方法:首先存放好p中数据,然后把P->next中数据copy到p中,然后把p->next删除。
int DeleteNode(ListNode * p)
{
	if (!p)
	{
		return INT_MIN;
	}

	int tmp = p->m_nKey;
	ListNode * tmpNode = p->m_pNext;
	p->m_nKey = p->m_pNext->m_nKey;
	p->m_pNext = p->m_pNext->m_pNext;

	if (tmpNode != 0)
	{
		delete tmpNode;
		tmpNode = 0;
	}
}

//给定单链表中某一个非空节点P,在节点P前面插入一个节点
//方法:首先分配一个节点q,将q插在p点之后
//然后将p点数据copy到q节点中,然后再把要插入的数据放到p中
int InsertNode(ListNode * p, int data)
{
	if (!p)
	{
		return INT_MIN;
	}

	ListNode * tmpNode = new ListNode();
	tmpNode->m_nKey = p->m_nKey;
	tmpNode->m_pNext = p->m_pNext;
	p->m_pNext = tmpNode;

	p->m_nKey = data;

}

int main()
{

	int arr[] = {1, 2, 3};
	int brr[] = {4, 5, 6};
	int crr[] = {7, 8, 9};
	int alen = sizeof(arr)/sizeof(arr[0]);
	int blen = sizeof(brr)/sizeof(brr[0]);
	int clen = sizeof(crr)/sizeof(crr[0]);

	ListNode * head1 = 0;
	ListNode * head2 = 0;
	ListNode * head3 = 0;

	CreateList(head1, arr, alen);
	CreateList(head2, brr, blen);
	CreateList(head3, crr, clen);

	//合并链表1和链表3
	head1 = UnionList(head1, head3);
	//合并链表2和链表3
	head2 = UnionList(head2, head3);

	//打印链表1
	dump(head1);

	//打印链表2
	dump(head2);

	//寻找两个链表的第一个公共节点
	ListNode *findNode =  FirstCommonNode(head1, head2);
	printf("第一个公共节,地址为:Ox%x 值为:%d\n", findNode, findNode->m_nKey);

	findNode = HasLoop(head1);
	findNode == 0?printf("没有环\n"):printf("有环,换的入口节点值为:%d\n", findNode->m_nKey);
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值