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);
}