链表相关笔试题(二)

来源:互联网 发布:金融行业的网络销售 编辑:程序博客网 时间:2024/04/25 15:55

1.复杂链表的复制

题目:请实现函数,复制一个复杂链表。在复杂链表中,每个结点除了有一个next指针指向下一个结点外,还有一个pSibling指向链表中的任意结点或者NULL。

方法一:先复制原始链表上的每一个结点,并用next链接起来,然后设置每个节点的pSibling指针。假设原始链表中的某个结点N的pSibling指向结点S,由于S的位置在链表中可能在N的前面也可能在N的后面,所以要定位S的位置需要从原始链表的头结点开始找。因此这种方法的时间复杂度为O(n^2).

方法二:第一步先复制原始链表的每个结点N创建N',然后将创建出的结点用next链接起来,同时借助哈希表存储<N,N'>的配对信息,第二步还是设置复制链表上的每个结点的pSibling。如果在原始链表中结点N的pSibling指向结点S,那么在复制链表中,对应的N'指向S'。这种方法的时间复杂度为O(n)。

方法三:第一步,根据原始链表的每个结点N创建N',并把N'链在N的后面,第二步,设置复制出来的结点的pSibling。假设原始链表上的N的pSibling指向S,那么对应的N'是N的next结点,S'是S的next指向的结点,第三步,将这个长链表拆分成两个链表,奇数位置上的结点用next链接起来就是原始链表,偶数位置的结点用next链接起来就是复制出来的链表。

实现代码:

void CloneNodes(ListNode* pHead){ListNode* cur = pHead;while (cur != NULL){ListNode* pCloned = new ListNode();pCloned->data = cur->data;pCloned->next = cur->next;pCloned->pSibling = NULL;cur->next = pCloned;cur = pCloned->next;}}void ConnectSiblingNodes(ListNode* pHead){ListNode* cur = pHead;while (cur != NULL){ListNode* pCloned = cur->next;if (cur->pSibling != NULL){pCloned->pSibling = cur->pSibling->next;}cur = pCloned->next;}}ListNode* ReconnectNodes(ListNode* pHead){ListNode* cur = pHead;ListNode* CloneHead = NULL;ListNode* CloneNode = NULL;if (cur != NULL){CloneHead = CloneNode = cur->next;cur->next = CloneNode->next;cur = cur->next;}while (cur != NULL){CloneNode->next = cur->next;CloneNode = CloneNode->next;cur->next = CloneNode->next;cur = cur->next;}return CloneHead;}ListNode* Clone(ListNode* pHead){CloneNodes(pHead);ConnectSiblingNodes(pHead);return ReconnectNodes(pHead);}


2.两个链表的第一个公共结点

方法一:在第一个链表上顺序遍历每个结点,每遍历到一个结点时,在第二个链表上顺序遍历每个结点,判断是否相同。

方法二:首先遍历两个链表得到它们的长度,就知道那个链表长以及长的链表比短的链表多几个结点。在第二次遍历时,在长链表上先走n步,接着同时遍历两个链表,找到的第一个相同的结点就是它们的第一个公共结点。

实现代码:

size_t GetListLength(ListNode* pHead){ size_t len = 0; ListNode* cur = pHead; while (cur != NULL) {  ++len;  cur = cur->next; } return len;}ListNode* FindFirstCommonNode(ListNode* pHead1, ListNode* pHead2){      if(pHead1 == NULL || pHead2 == NULL){         return NULL;     }     size_t len1 = GetListLength(pHead1);     size_t len2 = GetListLength(pHead2);     int diflen = len1 - len2;     ListNode* longlist = pHead1;     ListNode* shortlist = pHead2;     if (len2 > len1)     {      longlist = pHead2;      shortlist = pHead1;      diflen = len2 - len1;     }      for (int i = 0; i < diflen; ++i)     {          longlist = longlist->next;     }         while (longlist != NULL && shortlist != NULL)     {          if(longlist == shortlist)          {              return longlist;              }          longlist = longlist->next;          shortlist = shortlist->next;     }     return NULL;}

3.删除排序链表中重复的节点

分析:从头遍历结点,如果当前结点的值与下一个节点的值相同,则删除它们,要确保删除后的链表仍然是相连的,要把当前结点的前一个结点和后面值比当前值大的结点相连。

实现代码:

void deleteDuplication(ListNode*& pHead){if (pHead == NULL){return;}ListNode *PreNode = NULL;ListNode *cur = pHead;while (cur != NULL){ListNode *pNext = cur->next;if (pNext != NULL && pNext->data == cur->data){int val = cur->data;ListNode* Del = cur;while (Del != NULL && Del->data == val){pNext = Del->next;delete Del;Del = NULL;Del = pNext;}if (PreNode == NULL){pHead = pNext;}else{PreNode->next = pNext;}cur = pNext;}else{PreNode = cur;cur = cur->next;}}}


4.求链表中环的入口结点

方法一:定义两个指针都指向链表的头结点,如果链表中的环有n个结点,先让一个指针在链表上向前移动n步,然后两个指针同时向前移动,当第二个指针指向环的入口结点时,第一个指针已经围绕着环走了一圈又回到了入口结点。

实现代码:

ListNode* HasCycle(ListNode *pHead){ListNode *fast = pHead;ListNode *slow = pHead;while (fast && fast->next){fast = fast->next->next;slow = slow->next;if (slow == fast){return slow;}}return NULL;}int GetLength(ListNode *MeetNode){ListNode *cur = MeetNode;int count = 1;while (cur->next != MeetNode){cur = cur->next;count++;}    return count;}ListNode* EntryNodeOfLoop(ListNode* pHead){ListNode *ret = HasCycle(pHead);    if(ret == NULL){        return NULL;    }int len = GetLength(ret);        ListNode *fast = pHead;    ListNode *slow = pHead;            while(len--)    {        fast = fast->next;    }    while (fast != NULL && slow != NULL){        if(fast == slow){            return fast;        }fast = fast->next;slow = slow->next;}return NULL;}

方法二:如果可以改变链表结构,定义两个指针,一个指向pHead的下一个结点,另一个紧跟着这个指针,两个指针同时向前移动,每移动一次,让后面的指针指向NULL,也就是说,断开访问过的结点,最后到达的结点一定是尾结点的下一个结点,也就是环的入口结点,因为这是第二次访问该结点,它的next指向NULL,因此这时循环结束。

实现代码

    ListNode* EntryNodeOfLoop(ListNode* pHead)    {        if (!pHead->next)            return NULL;        ListNode* previous = pHead;        ListNode* front = pHead ->next;        while (front)        {            previous->next = NULL;            previous = front;            front = front->next;        }        return previous;    }

方法三:第一步,找环中相汇点。分别用两个指向链表头部,一个每次走一步,另一个每次走二步,直到找到在环中的相汇点;第二步,找环的入口。接上步,用一个指针指向链表头部,另一个指向上一步求出的相汇点,当两个指针相遇时,即为环的入口点。

ListNode* HasCycle(ListNode *pHead){ListNode *fast = pHead;ListNode *slow = pHead;while (fast && fast->next){fast = fast->next->next;slow = slow->next;if (slow == fast){return slow;}}return NULL;}ListNode* GetEnterNode(ListNode *pHead, ListNode *MeetNode){ListNode *cur = pHead;ListNode *tmp = MeetNode;while (cur && tmp){if (cur == tmp){return cur;}cur = cur->next;tmp = tmp->next;}return NULL;}ListNode* EntryNodeOfLoop(ListNode* pHead){ListNode *ret = HasCycle(pHead);return GetEnterNode(pHead, ret);}








2 0