微軟面試100題系列-第1題

注:微軟面試100題系列中的題都是v_JULY_vhttp://blog.csdn.net/v_JULY_v)收集的面試題,具體PDF下載地址爲:http://download.csdn.net/detail/v_july_v/4583815

                寫文的目的是鍛鍊自己,歡迎各位大牛提出建議,批評指正~

第一題:把二分查找樹轉變成排序的雙向鏈表

輸入一棵二分查找樹,將該二分查找樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只調整指針的指向。如:
                                                 

                                                        10

                                                      /      \

                                                    6       14

                                                  /   \       /   \

                                                4    8  12   16
 
轉換成雙向鏈表4=6=8=10=12=14=16 。

我們定義的二分查找樹結點的數據結構如下:
struct BSTreeNode
{
  int m_nValue; // value of node
  BSTreeNode *m_pLeft; // left child of node
  BSTreeNode *m_pRight; // right child of node
};

 

我的解題思路如下:

      本題中不能建立新的結點,只能改變指針的指向。先觀察根結點,根據二叉查找樹的特點,可以發現根結點的左指針應該指向左子樹雙向鏈表的尾部,而根結點的右指針

應該指向右子樹雙向鏈表的頭部。因此該問題可以遞歸爲求解左、右孩子的雙向鏈表。求解完左、右孩子的雙向鏈表後,再接上根結點,即可完成對雙向鏈表的求解。

     根據上述思路,先來看一下代碼結構,之後會有例子講解。

      編寫如下的遞歸函數:

      void ConvertRecursive(BSTNode* &subHead, BSTNode* &subTail, BSTNode* subRoot)

      然後在函數中求解左、右孩子的雙向鏈表:

      BSTNode *leftTail = NULL, *rightHead = NULL;

      ConvertRecursive(subHead,leftTail,subRoot->pLeft);  //求解左子的鏈表

      ConvertRecursive(rightHead,subTail,subRoot->pRight);  //求解右子的鏈表

      。。。。。。 //將左子鏈表、根、右子鏈表連接起來

      這個遞歸初看可能有點不清晰,我們可以先看一下求解左子鏈表的語句:

      ConvertRecursive(subHead,leftTail,subRoot->pLeft);   

       這個subHead可看成是當前鏈表的頭,在遞歸結束後,最上一層的subHead將是整個雙向鏈表的頭。而leftTail是我們想要的左孩子雙向鏈表的尾部。

       ConvertRecursive(rightHead,subTail,subRoot->pRight); 

       rightHead是我們想要的右孩子雙向鏈表的頭部,而subTail可看成是當前鏈表的尾,在遞歸結束後,最上一層的subTail將是整個雙向鏈表的尾。

       現在看一下左子鏈表、根與右子鏈表連接的操作:如果左子沒有雙向鏈表,那麼根結點就成爲當前鏈表的頭;如果有,根結點就連接上左子鏈表的尾部。

       同理,如果右子沒有鏈表,那麼根結點就成爲當前鏈表的尾;如果有,則根結點與右子鏈表的頭部相連接。

//連接左子鏈表與根
  if (leftTail == NULL)  //左子沒有雙向鏈表
  {
       subHead = subRoot;
  }
  else
  {
      leftTail->pRight = subRoot;
      subRoot->pLeft = leftTail;
  }

  //連接右子鏈表與根
  if (rightHead == NULL) //右子沒有雙向鏈表
  {
      subTail = subRoot;
  }
  else
  {
      subRoot->pRight = rightHead;
      rightHead->pLeft = subRoot;
  }

         以子樹        6                   爲例,

                          /        \

                       4           8

         遞歸到4時,因爲4是葉子結點,其左、右子樹鏈表爲空,則有subHead -> 4, subTail -> 4。同理8也是這種情況。

         返回到6的調用,因爲6調用4的遞歸中,形參subTail實際上是實參leftTail的引用,因此6的左子鏈表leftTail->4不爲空,按照之前的思路,我們可以將結點6與左子鏈表連

接起來,就有了鏈表4=6;同理,在6調用8的遞歸中,形參subHead實際上是實參rightHead的引用,因此6的右子鏈表rightHead->8也不爲空,將6與右子鏈表連接起來,我們

有了雙向鏈表4=6=8,而此時當前鏈表的subHead->4,subTail->8。

由此,可以看出整個遞歸過程中,leftTail和rightHead起到了連接整個鏈表中間結點的作用,而subHead和subTail則保存了目前遞歸層次中的最小和最大值。

         完整的代碼如下,因爲自己有一個BST樹結點的模板類,所以直接拿來用了,BSTNode<int>和題目中的BSTreeNode的結構是一樣的。

//written by zero

#include "BST.h"
#include <iostream>

using namespace std;

void ConvertRecursive(BSTNode<int>* &subHead, BSTNode<int>* &subTail, BSTNode<int>* subRoot)
{
	 BSTNode<int> *leftTail = NULL, *rightHead = NULL;

	 if (subRoot == NULL)
	 {
		 subHead = NULL;
		 subTail = NULL;
		 return;
	 }

	 ConvertRecursive(subHead,leftTail,subRoot->pLeft);  //求解左子的鏈表
	 ConvertRecursive(rightHead,subTail,subRoot->pRight);  //求解右子的鏈表

	 //將左子鏈表,根,右子鏈表接在一起

	 //連接左子鏈表與根
	 if (leftTail == NULL)  //左子沒有雙向鏈表
	 {
		 subHead = subRoot;
	 }
	 else
	 {
		 leftTail->pRight = subRoot;
		 subRoot->pLeft = leftTail;
	 }

	 //連接右子鏈表與根
	 if (rightHead == NULL) //右子沒有雙向鏈表
	 {
		 subTail = subRoot;
	 }
	 else
	 {
		 subRoot->pRight = rightHead;
		 rightHead->pLeft = subRoot;
	 }

}

int main()
{
	//建立二叉查找樹
	BSTree<int> tree;
	tree.Insert(10);
	tree.Insert(6);
	tree.Insert(14);
	tree.Insert(4);
	tree.Insert(8);
	tree.Insert(12);
	tree.Insert(16);

	/*tree.TravelRecursive(&BSTree<int>::PosOderRecursive,&BSTree<int>::process);
	cout << endl;
	tree.TravelRecursive(&BSTree<int>::PosOderNonRecursive,&BSTree<int>::process);
	cout << endl;*/

	//轉換成雙向鏈表
	BSTNode<int> *head = NULL, *tail = NULL, *pNode;
	ConvertRecursive(head,tail,tree.root);

	//驗證正向遍歷雙向鏈表
	cout << "正向遍歷鏈表:";
	pNode = head;
	while(pNode)
	{
		cout << pNode->value << " ";
		pNode = pNode->pRight;
	}
	cout << endl;

	//驗證逆向遍歷雙向鏈表
	cout << "逆向遍歷鏈表:";
	pNode = tail;
	while(pNode)
	{
		cout << pNode->value << " ";
		pNode = pNode->pLeft;
	}
	cout << endl;
}


運行結果如下:

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