您的位置:首页 > 其它

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

2013-06-12 22:21 696 查看
//////题目:两个单向链表,找出它们的第一个公共结点。
//////
//////链表的结点定义为:
//////
//////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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐