您的位置:首页 > 编程语言

编程之美--编程判断两个链表是否相交

2012-03-31 20:21 417 查看
输入一个单向链表,输出该链表中倒数第k个结点,

链表的倒数第0个结点为链表的尾指针。

相信,稍微有点 经验的同志,都会说到:设置两个指针p1,p2,首先p1和p2都指向head,然后p2向前走k步,这样p1和p2之间就间隔k个节点,最后p1和p2同时向前移动,直至p2走到链表末尾。

struct ListNode

{

char data;

ListNode* next;

};

ListNode* head,*p,*q;

ListNode *pone,*ptwo;

ListNode* fun(ListNode *head,int k)

{

assert(k >= 0);

pone = ptwo = head;

for( ; k > 0 && ptwo != NULL; k--)

ptwo=ptwo->next;

if (k > 0) return NULL;

while(ptwo!=NULL)

{

pone=pone->next;

ptwo=ptwo->next;

}

return pone;

}

这是针对链表单项链表查找其中倒数第k个结点。试问,如果链表是双向的,且可能存在环呢?编程判断两个链表是否相交。

给出两个单向链表的头指针(如下图所示),

比如h1、h2,判断这两个链表是否相交。这里为了简化问题,我们假设两个链表均不带环。

编程之美的解析:

直接循环判断第一个链表的每个节点是否在第二个链表中。但,这种方法的时间复杂度为O(Length(h1) * Length(h2))。显然,我们得找到一种更为有效的方法,至少不能是O(N^2)的复杂度。

针对第一个链表直接构造hash表,然后查询hash表,判断第二个链表的每个结点是否在hash表出现,如果所有的第二个链表的结点都能在hash表中找到,即说明第二个链表与第一个链表有相同的结点。时间复杂度为为线性:O(Length(h1) + Length(h2)),同时为了存储第一个链表的所有节点,空间复杂度为O(Length(h1))。是否还有更好的方法呢,既能够以线性时间复杂度解决问题,又能减少存储空间?

进一步考虑“如果两个没有环的链表相交于某一节点,那么在这个节点之后的所有节点都是两个链表共有的”这个特点,我们可以知道,如果它们相交,则最后一个节点一定是共有的。而我们很容易能得到链表的最后一个节点,所以这成了我们简化解法的一个主要突破口。那么,我们只要判断俩个链表的尾指针是否相等。相等,则链表相交;否则,链表不相交,O((Length(h1) + Length(h2))

上面的问题都是针对链表无环的,那么如果现在,链表是有环的呢?还能找到最后一个结点进行判断么?上面的方法还同样有效么?显然,这个问题的本质已经转化为判断链表是否有环。那么,如何来判断链表是否有环呢?

所以,事实上,这个判断两个链表是否相交的问题就转化成了:

1.先判断带不带环

2.如果都不带环,就判断尾节点是否相等

3.如果都带环,判断一链表上俩指针相遇的那个节点,在不在另一条链表上。

如果在,则相交,如果不在,则不相交。
如何编写代码来判断链表是否有环呢?因为很多的时候,你给出了问题的思路后,面试官可能还要追加你的代码,ok,如下(设置两个指针(p1,
p2),初始值都指向头,p1每次前进一步,p2每次前进二步,如果链表存在环,则p2先进入环,p1后进入环,两个指针在环中走动,必定相遇):
1. //copyright@ KurtWang
2. //July、2011.05.27。
3. struct Node
4. {
5. int value;
6. Node * next;
7. };
8.
9. //1.先判断带不带环
10. //判断是否有环,返回bool,如果有环,返回环里的节点
11. //思路:用两个指针,一个指针步长为1,一个指针步长为2,判断链表是否有环
12. bool isCircle(Node * head, Node *& circleNode, Node *& lastNode)
13. {
14. Node * fast = head->next;
15. Node * slow = head;
16. while(fast != slow && fast && slow)
17. {
18. if(fast->next != NULL)
19. fast = fast->next;
20.
21. if(fast->next == NULL)
22. lastNode = fast;
23. if(slow->next == NULL)
24. lastNode = slow;
25.
26. fast = fast->next;
27. slow = slow->next;
28.
29. }
30. if(fast == slow && fast && slow)
31. {
32. circleNode = fast;
33. return true;
34. }
35. else
36. return false;
37. }
如果都不带环,就判断尾节点是否相等,如果都带环,判断一链表上俩指针相遇的那个节点,在不在另一条链表上。下面是综合解决这个问题的代码:
1. //判断带环不带环时链表是否相交
2. //2.如果都不带环,就判断尾节点是否相等
3. //3.如果都带环,判断一链表上俩指针相遇的那个节点,在不在另一条链表上。
4. bool detect(Node * head1, Node * head2)
5. {
6. Node * circleNode1;
7. Node * circleNode2;
8. Node * lastNode1;
9. Node * lastNode2;
10.
11. bool isCircle1 = isCircle(head1,circleNode1, lastNode1);
12. bool isCircle2 = isCircle(head2,circleNode2, lastNode2);
13.
14. //一个有环,一个无环
15. if(isCircle1 != isCircle2)
16. return false;
17. //两个都无环,判断最后一个节点是否相等
18. else if(!isCircle1 && !isCircle2)
19. {
20. return lastNode1 == lastNode2;
21. }
22. //两个都有环,判断环里的节点是否能到达另一个链表环里的节点
23. else
24. {
25. Node * temp = circleNode1->next; //updated,多谢苍狼 and hyy。
26. while(temp != circleNode1)
27. {
28. if(temp == circleNode2)
29. return true;
30. temp = temp->next;
31. }
32. return false;
33. }
34.
35. return false;
36. }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: