//题目:两个单向链表,找出它们的第一个公共结点。
//
//链表的结点定义为:
//
//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;
}
单向链表相交的第一个公共结点, 判断链表是否有环以及环的入口节点
最新推荐文章于 2020-02-14 11:48:49 发布