二叉搜索樹於雙向鏈表

前不久剛參加一個面試,當面試官問道這個類似的問題時,有點懵了,號不容易才答上來,面試官對於我的結果,認爲考慮不全面,今天在面經上看到相似的題目,故作次記錄。

題目:輸入一顆二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的節點,只能調整樹中的結點指針的指向。比如,輸入如圖所示的左邊的二叉搜索樹,則輸出轉換之後的排序雙向鏈表。


二叉樹結點的定義如下:

struct BinaryTreeeNode
{
      int                             m_nValue;
      BinaryTreeeNode*    m_nLeft;
      BinaryTreeeNode*    m_nRight;
};
在二叉樹中,每個結點都有兩個指向子結點的指針。在雙向鏈表中,每個結點也有兩個指針,他們分別指向前一個結點和後一個結點。由於這兩種結點的結構相似,同時二叉搜索樹也是一種排序的數據結構,因此理論上有可能實現二叉搜索樹和排序的雙向鏈表的轉換。在搜索二叉樹中,左子結點的值總是小於父子結點的值,右子結點的值總是大於父結點的值。因此我們在轉換成排序雙向鏈表時,原先指向左子結點的指針調整爲鏈表中指向前一個結點的指針,原先指向右子結點的指針調整爲鏈表中指向後一個結點指針。接下來我們考慮該如何轉換。

由於要求轉換之後的鏈表時排序好的,我們可以中序遍歷樹中的每個結點,這是因爲中序遍歷算法的特點是按照從小到大的順序遍歷二叉樹的每個結點。當遍歷到根節點的時候,我們把樹看成三部分:值爲10的節點,根結點值6得左子樹和根爲14的右子樹。根據排序鏈表的定義,值爲10的結點將和他的左子樹最大一個結點鏈接起來,同時它可以將和右子樹的最小的結點(即值爲12所謂·結點)鏈接起來,如下圖所示


注:根結點,左子樹和右子樹。把左右子樹都換成排序成排序好的雙向鏈表之後再跟根結點鏈接起來,整棵二叉搜索樹也就轉換成了排序的雙鏈表。


按照中序遍歷的順序,當我們遍歷轉換到根結點時,它的左子樹已經轉換成一個排序的雙鏈表了,而且處在鏈表中的最後一個結點是當前值最大的結點。我們把值爲8的結點和根結點鏈接起來,此時鏈表中的最後一個結點就是10了。接着我們去遍歷轉換右子樹,並把根節點和右子樹中最小的結點鏈接起來。至於怎麼去轉換它的左子樹和右子樹,由於遍歷和轉換過程是一樣的,我們很自然的想到可以用遞歸。

基於上述分析過程,我們可以寫出如下代碼:

BinaryTreeNode* Convert(BinaryTreeNode* pRootOfTree)
{
	BinaryTreeNode* pLastNodeInList=NULL;
	
	ConvertNode(pRootOfTree,&pLastNodeInList);
	
	//pLastNodeInList指向雙鏈表的尾結點
	//我們需要回到頭結點
	
	BinaryTreeNode* pHeadOfList=pLastNodeInList;
	
	while(pHeadOfList!=NULL && pHeadOfList->m_nLeft!=NULL)
		pHeadOfList=pHeadOfList->m_nLeft;
	
	return pHeadOfList;
}
void ConvertNode(BinaryTreeNode* pNode,BinaryTreeNode* pLastNodeInList)
{
	if(pNode==NULL)
		return;
	
	BinaryTreeNode* pCurrent=pNode;
	
	if(pCurrent->m_nLeft!=NULL)
		ConvertNode(pCurrent->m_nLeft,pLastNodeInList);
		
	pCurrent->m_nLeft!=*pLastNodeInList;
	
	if(*pLastNodeInList!=NULL)
		(*pLastNodeInList)->m_pRight=pCurrent;
		
	*pLastNodeInList=pCurrent;
	
	if(pCurrent->m_pRight!=NULL)
		ConvertNode(pCurrent->m_pRight,pLastNodeInList);
}

上述代碼中,我們用pLastNodeInList指向已經轉換好的鏈表的最後一個結點。我們遍歷到指爲10的結點的時候,它的左子樹都已經轉換好了,因此pLastNodeInList指向指爲8的結點。接着把根結點鏈表接到鏈表中之後,指爲10的結點成了鏈表中的最後一個結點(新的最大得結點),於是pLastInLIst指向了這個值爲10的結點。接下來把pLastNodeInList作爲參數傳入函數遞歸遍歷右子樹。我們找到右子樹中最左邊的子結點,並把該結點和值爲10的結點鏈接起來。

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