剑指Offer第二版 面试题35:复杂链表的复制 C++三种实现方式

面试题35:复杂链表的复制

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

复杂链表的结构体定义:

struct ListNode
{
	int Value;
	ListNode *Next, *Sibling;
	ListNode(int value):Value(value),Next(nullptr),Sibling(nullptr){};
};

思路一:直接将整个链表进行复制,分两步进行。首先复制每个链表的下一个结点,然后复制每个结点的sibling,但是由于每个结点的sibling又需要从头开始遍历,因此每个结点需要O(n)步完成,那么n个结点的时间复杂度为O(n2)。代码如下:

class solution
{
public:
	ListNode* clone(ListNode* Head)
	{
		ListNode* result = new ListNode(0);
		ListNode* newNode = result;
		ListNode* Node = Head;
		while (Node)
		{
			ListNode* newItem = new ListNode(Node->Value);
			newNode->Next = newItem;
			Node = Node->Next;
			newNode = newItem;
		}
		ListNode* newHead = result->Next;
		delete result;

		Node = Head;
		newNode = newHead;
		while (Node)
		{
			if (Node->Sibling)
			{
				ListNode* tempNode = newHead;
				while (tempNode)
				{
					if (tempNode->Value == Node->Sibling->Value)
					{
						newNode->Sibling = tempNode;
						break;
					}
					tempNode = tempNode->Next;
				}
			}
			Node = Node->Next;
			newNode = newNode->Next;
		}
		return newHead;
	}
};

思路二:用空间换时间。将每个结点与复制的结点进行配对,放入hash表中,这样就可以通过旧结点的sibling快速找到新结点的sibling,时间复杂度为O(1),但是需要消耗O(n)的空间。代码如下:

class solution
{
public:
	typedef map<ListNode*, ListNode*> Map;
	ListNode* cloneNode(ListNode* Head, Map& hashNode)
	{
		ListNode* newHead = new ListNode(0);
		ListNode* Node = Head;
		ListNode* item = newHead;

		while (Node)
		{
			ListNode* newNode = new ListNode(Node->Value);
			item->Next = newNode;
			hashNode.insert(make_pair(Node, newNode));
			Node = Node->Next;
			item = newNode;
		}

		ListNode* returnHead = newHead->Next;
		delete newHead;
		return returnHead;
	}
	void connectSibling(ListNode* Head, ListNode* newHead, Map& hashNode)
	{
		ListNode* Node = Head;
		ListNode* newNode = newHead;
		while (Node)
		{
			newNode->Sibling = hashNode[Node->Sibling];
			Node = Node->Next;
			newNode = newNode->Next;
		}
	}
	ListNode* clone(ListNode* Head)
	{
		Map hashNode;
		ListNode* newHead = cloneNode(Head, hashNode);
		connectSibling(Head, newHead, hashNode);
		return newHead;
	}
};

思路三:在不使用辅助空间的情况下实现O(n)的时间效率。分为三步,首先在原链表中复制每个结点,并将该结点插入到原结点后面;然后根据原结点的sibling链接新结点的sibling(也就是说如果结点N的sibling是S,那么结点N1的sibling就是S1);之后将新链表和旧链表断开,得到复制的复杂链表。代码如下:

class solution
{
public:
	void cloneNode(ListNode* Head)
	{
		ListNode* Node = Head;
		while (Node)
		{
			ListNode* copyNode = new ListNode(Node->Value);
			copyNode->Next = Node->Next;
			Node->Next = copyNode;
			Node = copyNode->Next;
		}
	}
	void cloneSibling(ListNode* Head)
	{
		ListNode* Node = Head;
		while (Node)
		{
			if (Node->Sibling)
				Node->Next->Sibling = Node->Sibling->Next;
			Node = Node->Next->Next;
		}
	}
	ListNode* clone(ListNode* Head)
	{
		cloneNode(Head);
		cloneSibling(Head);

		ListNode* Node = Head;
		ListNode* newHead = Head->Next;
		ListNode* newNode = newHead;
		while (Node)
		{
			Node->Next = newNode->Next;
			if (Node->Next == nullptr)
				break;
			newNode->Next = Node->Next->Next;
			Node = Node->Next;
			newNode = newNode->Next;
		}
		return newHead;
	}
};

测试样例如下:
在这里插入图片描述
测试代码如下:

int main()
{
	ListNode* Node1 = new ListNode(1);
	ListNode* Node2 = new ListNode(2);
	ListNode* Node3 = new ListNode(3);
	ListNode* Node4 = new ListNode(4);
	ListNode* Node5 = new ListNode(5);

	Node1->Next = Node2;
	Node2->Next = Node3;
	Node3->Next = Node4;
	Node4->Next = Node5;

	Node1->Sibling = Node3;
	Node2->Sibling = Node5;
	Node4->Sibling = Node2;

	solution result;
	ListNode* newHead = result.clone(Node1);

	while (newHead != nullptr)
	{
		if (newHead->Sibling != nullptr)
			printf("结点%d的Sibling为结点%d\n", newHead->Value, newHead->Sibling->Value);
		newHead = newHead->Next;
	}
	return 0;
}

测试结果如下:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值