複雜鏈表的複製

題目:一個鏈表的每個節點,有一個指向next指針指向先一個節點,還有一個random指針指向這個鏈表的一個隨機節點或者NULL,現在要求實現複製這個複雜的鏈表,返回複製後的新鏈表。

複雜鏈表的結構

template<class T>
struct ComplexNode
{
public:
	ComplexNode(const T& data)
		:_data(data)
		,_next(NULL)
		,_random(NULL)
	{}
public:
	T _data;//數據
	ComplexNode* _next;//指向下一個節點
	ComplexNode* _random;//指向隨機節點(可以是鏈表中的任意節點或者空)
};

wKioL1cPn0Py_EdoAABjSdNvZ5A278.png

思路分析:

看到這道題目時,最常規的思路就是,要分成兩步。首先要複製原來單鏈表上的每一個結點並且連接起來。最後將單鏈表的random指針更改。


更改random的時候有兩種解決方法:


1)更改每個結點的時候都記錄下該結點的位置,即第幾個,通過計數器更改。

對一個含有n個結點的鏈表,由於定位每個結點的_random,都需要從鏈表頭結點開始經過O(n)步才能找到,因此這種方法的總時間複雜度是O(n2)。

評價:時間複雜度太太。


2)由於上述方法的時間主要花費在定位結點的_random上面,我們試着在這方面去做優化。這裏我們對<N,N’>的配對信息放到一個哈希表中。設置複製鏈表上每個結點的_random。如果在原始鏈表中結點N的_random指向結點S,那麼在複製鏈表中,對應的N’應該指向S’。由於有了哈希表,我們可以用O(1)的時間根據S找到S’。

評價:用空間換時間,以O(n)的空間消耗實現了O(n)的時間效率。



接着我們來換一種思路,在不用輔助空間的情況下實現O(n)的時間效率。第三種方法的第一步仍然是根據原始鏈表的每個結點N,創建對應的N’。這一次,我們把新創建的每個結點N’鏈接在原先結點N的後面。

wKioL1cPoq2CmRijAAC5gWHh4NM113.png

最後,將整個鏈表拆分成原始鏈表和拷貝出的鏈表。

實現代碼:

template<class T>
struct ComplexNode
{
public:
	ComplexNode(const T& data)
		:_data(data)
		,_next(NULL)
		,_random(NULL)
	{}
public:
	T _data;//數據
	ComplexNode* _next;//指向下一個節點
	ComplexNode* _random;//指向隨機節點(可以是鏈表中的任意節點或者空)
};

template<class T>
void CloneListNode(ComplexNode<T>* pHead)//複製鏈表節點並連接在該節點的後邊
{
	if (pHead)//鏈表不爲空
	{
		ComplexNode<T> * cur = pHead;
		while (cur)
		{
			ComplexNode<T>* NewNode = new ComplexNode<T>(cur->_data);
			NewNode->_next = cur->_next;
			cur->_next = NewNode;
			cur = NewNode->_next;//下一個未被複制的節點
		}
	}
}


template<class T>
void ConnectionNode(ComplexNode<T>* pHead)//更新新節點的隨機指針
{
	if (pHead)//鏈表不爲空
	{
		ComplexNode<T> * cur = pHead;
		while (cur)
		{
			ComplexNode<T> * Random = cur->_random;//隨機節點

			if (Random)//隨機節點可能爲空
			{
				Random = cur->_random;
				cur->_next->_random = Random->_next;//將隨機指針複製
			}

			cur = cur->_next->_next;//節點向後移動
		}
	}
}


template<class T>
ComplexNode<T>* ReconnectNodes(ComplexNode<T>* pHead)//將原來鏈表產分爲兩個
{
	ComplexNode<T>* newHead = NULL;
	ComplexNode<T>* first = pHead;//原來的鏈表
	ComplexNode<T>* second = newHead;//新的鏈表

	if (pHead!=NULL)//鏈表不爲空
	{
		newHead = first->_next;
		second = newHead;
		first = first->_next;
	}

	while (first)//遍歷鏈表
	{

		second->_next = first->_next;
		second = second->_next;

		if (second)//防止空指針的引用
		{
			first->_next = second->_next;
		}
		first = first->_next;
		
	}

	return newHead;
}


template<class T>
ComplexNode<T>*  CopyList(ComplexNode<T>* pHead)
{
	if (pHead)//鏈表不爲空
	{
		CloneListNode(pHead);//克隆節點並且連在該節點的後邊
		ConnectionNode(pHead);//連接
		ComplexNode<T>* NewHead = ReconnectNodes(pHead);//拆分
		return NewHead;
	}
	return NULL;
}

創建複雜的鏈表

template<class T>
void CreateList(ComplexNode<T>* &pHead)//複雜鏈表的建立
{
	ComplexNode<T> *Node1 = new ComplexNode<T>('A');
	ComplexNode<T> *Node2 = new ComplexNode<T>('B');
	ComplexNode<T> *Node3 = new ComplexNode<T>('C');
	ComplexNode<T> *Node4 = new ComplexNode<T>('D');
	ComplexNode<T> *Node5 = new ComplexNode<T>('E');

	pHead = Node1;
	Node1->_next = Node2;
	Node2->_next = Node3;
	Node3->_next = Node4;
	Node4->_next = Node5;

	Node1->_random = Node3;
	Node2->_random = Node4;
	Node3->_random = Node5;
	Node4->_random = Node1;
	Node5->_random = Node2;
}

打印複雜鏈表

template<class T>

void PrintList(ComplexNode<T>* pHead)//打印複雜鏈表
{

	if (pHead)//鏈表不爲空
	{
		ComplexNode<T> * cur = pHead;
		while (cur)
		{
			cout << "(" << cur->_data << " " << cur->_random->_data << ")" << "->";
			//注意隨機指針可能爲空,不指向任何數據,此時會導致空指針的簡引用,此處爲了測試效果,所以複雜鏈表中沒有指向空指針
			cur = cur->_next;
		}
		cout<<"over" << endl;
	}
}

測試函數

template<class T>
void test()
{
	ComplexNode<T>* _head;
	CreateList(_head);
	cout << "原來的鏈表:";
	PrintList(_head);
	cout << "複製的鏈表:";
	PrintList(CopyList(_head));
}


測試結果:

wKiom1cPtwPC504aAAAH7seuSWQ067.png

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章