題目:一個鏈表的每一個節點,有一個指向next指針指向先一個節點,還有一個random指針指向這個鏈表的一個隨機節點或者NULL,如今要求實現複製這個複雜的鏈表,返回複製後的新鏈表。dom
複雜鏈表的結構ide
template<class T> struct ComplexNode { public: ComplexNode(const T& data) :_data(data) ,_next(NULL) ,_random(NULL) {} public: T _data;//數據 ComplexNode* _next;//指向下一個節點 ComplexNode* _random;//指向隨機節點(能夠是鏈表中的任意節點或者空) };
思路分析:函數
看到這道題目時,最常規的思路就是,要分紅兩步。首先要複製原來單鏈表上的每個結點而且鏈接起來。最後將單鏈表的random指針更改。測試
更改random的時候有兩種解決方法:優化
1)更改每一個結點的時候都記錄下該結點的位置,即第幾個,經過計數器更改。spa
對一個含有n個結點的鏈表,因爲定位每一個結點的_random,都須要從鏈表頭結點開始通過O(n)步才能找到,所以這種方法的總時間複雜度是O(n2)。指針
評價:時間複雜度太太。blog
2)因爲上述方法的時間主要花費在定位結點的_random上面,咱們試着在這方面去作優化。這裏咱們對<N,N’>的配對信息放到一個哈希表中。設置複製鏈表上每一個結點的_random。若是在原始鏈表中結點N的_random指向結點S,那麼在複製鏈表中,對應的N’應該指向S’。因爲有了哈希表,咱們能夠用O(1)的時間根據S找到S’。get
評價:用空間換時間,以O(n)的空間消耗實現了O(n)的時間效率。it
接着咱們來換一種思路,在不用輔助空間的狀況下實現O(n)的時間效率。第三種方法的第一步仍然是根據原始鏈表的每一個結點N,建立對應的N’。這一次,咱們把新建立的每一個結點N’連接在原先結點N的後面。
最後,將整個鏈表拆分紅原始鏈表和拷貝出的鏈表。
實現代碼:
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)); }
測試結果: