两个链表的第一个公共节点(剑指offer)

来源:互联网 发布:c语言改文件名 编辑:程序博客网 时间:2024/05/21 09:23

题目:输入两个链表,找出它们的第一个公共节点.链表节点定义如下:

struct ListNode{       int m_key;       ListNode* m_pNext;}
面试的时候,碰到这道题,我们的第一反应蛮力法:在第一个链表上顺序遍历每个节点,每遍历到一个节点的时候,在第二个链表上顺序遍历每个节点,每遍历到一个节点的时候,在第二个链表上顺序遍历每个节点,如果在第二个链表上有一个节点和第一个链表上的节点一样,说明两个链表在这个节点上重合,于是就找到了它们的第一个公共节点.如果第一个链表的长度为m,第二个链表的长度为n,显然该方法的时间复杂度为O(mn).

通常蛮力法都不是好办法,一定还有更好的办法.



好了,继续题中的思路进行分析吧!!!

1,我们接下来试着分析有公共节点的两个链表有那些特点.从链表节点的定义可以看出,这两个链表是单向链表.

  如果两个单向链表有公共的节点,那么这两个链表从某一节点开始,它们的m_pNext都指向同一个节点.但由于

  是单向链表的节点,每个节点都只有一个m_pNext,因此从第一个节点开始,之后它们所有的节点都是重合的,

  不可能出现分叉.所以两个公共节点而部分重合的链表,拓扑形状看起来像一个Y,而不可能是X,如下:

  

  分析之后,我们发现:如果两个链表有公共节点,那么公共节点出现在两个链表的尾部.如果我们从两个链表的

  尾部开始往前进行比较的话,最后一个相同的节点就是我们要找的节点.但是,但是,

  问题是:在单向链表中,我们只能从头节点开始按顺序遍历,最后才能到达尾节点.最后到达的尾节点却要被先

  比较,这听起来好像"后进先出"????

  于是,我们就能想到用栈的特点来解决这个问题:分别把两个节点放入栈里,这样两个链表的尾节点就位于这两  个栈的栈顶,接下来比较两个栈的栈顶节点是不算相同.如果相同:则把栈顶弹出接着比较下一个栈顶,直到找  到最后一个相同的节点.


  在上述的思路中,我们需要用两个辅助栈.如果链表的长度分别为m和n,那么空间复杂度是O(m+n).这  种思路的时间复杂度也是O(m+n).和最开始的蛮力法相比,时间效率得到了提高,相当于用空间消耗换取

  了时间效率.

 

  之所以需要用到栈,是因为我们想同时遍历到达这两个栈的尾节点.当两个链表的长度不相同时,如果我们从头

  开始遍历到达尾节点的时间就不一致.其实解决这个问题还有一个更简单的办法:首先遍历两个链表得到它们的

  长度,就能知道那个链表比较长,以及长的链表比短的链表多几个节点.在第二次遍历的时候,在较长的链表上  先走若干步,接着再同时在两个链表上遍历,找到相同的节点就是它们的第一个公共节点.

  其实:第三种思路和第二种相比,时间复杂度都是O(m+n),但是我们不再需要辅助的栈,因此提高了空间  效率.所以这应该是我们的最佳选择啊,,,,,


  

ListNode* FindFirstCommonNode(ListNode* pHead1,ListNode *pHead2)  {      //得到两个链表的长度      unsigned int nLength1 = GetListLength(pHead1);      unsigned int nLength2 = GetListLength(pHead2);        int nLengthDif = nLength1 - nLength2;        ListNode *pHeadLong = pHead1;      ListNode *pHeadShort = pHead2;      if(nLength2 > nLength1 )      {          ListNode *pHeadLong = pHead2;          ListNode *pHeadShort = pHead1;         nLengthDif = nLength2 - nLength1;                  }      //先在长链表上走几步,再同时在两个链表上遍历。      for(int i = 0;i < nLengthDif;i++)          pHeadLong = pHeadLong->m_pNext;        while((pHeadLong != NULL)&&(pHeadShort != NULL)          &&(pHeadLong != pHeadShort ))      {                pHeadLong = pHeadLong->m_pNext;          pHeadShort = pHeadShort->m_pNext;        }      //得到第一个公共节点        ListNode *pFirstCommonNode =    pHeadLong ;              return pFirstCommonNode;    }  //求链表长度的函数  unsigned int GetListLength(ListNode *pHead)  {      unsigned int Length = 0;      ListNode *pNode = pHead;        while(pNode != NULL)      {                ++Length;          pNode = pNode->m_pNext;        }        return Length;    } 

本题我们后面并没有浪费额外的空间:需要我们务必注意的是(以空间换时间并不一定都是可行的方案.我们要注意需要的辅助空间的大小,消耗太多的内存可能得不偿失.如果是关于嵌入式方面的,那么对于空间的消耗就要格外留心,因为通常嵌入式系统的内存很有限,,,,)

0 0
原创粉丝点击