链表相关笔试题(二)

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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值