微軟面試題解題筆記——二元查找樹的鏡像轉換

(題目來源於v_JULY_v的整理,微軟公司等數據結構+算法面試100題,July博客http://blog.csdn.net/v_JULY_v

題目:輸入一顆二元查找樹,將該樹轉換爲它的鏡像,
即在轉換後的二元查找樹中,左子樹的結點都大於右子樹的結點。
遞歸循環兩種方法完成樹的鏡像轉換。  
例如輸入:
  8
  / \
  6 10
 / \    / \
5 7  9 11

輸出:
   8
  / \
 10 6
 /  \   /\
11 9 7 5

解題思路:在此二叉查找樹中,要實現鏡像的轉換,實際只需要修改結點左右結點的指向,首先在使用遞歸調用時想到先序、中序和後續,若採用先序或後續遍歷實現都可以,區別在於先序是衝根結點開始修改,二後續遍歷是從葉子結點開始修改,中序遍歷在二叉查找樹中是從小到大的遍歷過程,雖然可以實現,但是需要額外的臨時變量保存指針的值。這裏採用後序遍歷實現的。

struct BSTreeNode
{
	int m_nValue; // value of node
	BSTreeNode *m_pLeft; // left child of node
	BSTreeNode *m_pRight; // right child of node
};

typedef BSTreeNode* Tree;

void addNode(Tree &tree, int data)
{
	if (tree == NULL)
	{
		tree = new BSTreeNode;
		tree->m_nValue = data;
		tree->m_pLeft = NULL;
		tree->m_pRight = NULL;
		return ;
	}
	else if ( tree->m_nValue >= data)
	{
		addNode(tree->m_pLeft, data);
	}
	else
		addNode(tree->m_pRight, data);
}

void Translate(Tree &tree)
{
	if (!tree)
	{
		return ;
	}
	if (tree->m_pLeft)
	{
		Translate(tree->m_pLeft);
	}
	if (tree->m_pRight)
	{
		Translate(tree->m_pRight);
	}
	Tree temp = tree->m_pLeft;
	tree->m_pLeft = tree->m_pRight;
	tree->m_pRight = temp;
	
}

void PrintTree(Tree &tree)
{
	if (!tree)
	{
		return;
	}
	PrintTree(tree->m_pLeft);
	cout<<tree->m_nValue<<' ';
	PrintTree(tree->m_pRight);
}

int _tmain(int argc, _TCHAR* argv[])
{
	Tree BinaryTree = NULL;//頭結點
	//bool bEnd = false;
	//while (!bEnd)
	//{
	//	int data = 0;
	//	cin>>data;
	//	if (data == -1)
	//	{
	//		bEnd = true;
	//	}
	//	else
	//		addNode(BinaryTree, data);
	//}
	addNode(BinaryTree, 8);
	addNode(BinaryTree, 6);
	addNode(BinaryTree, 10);
	addNode(BinaryTree, 5);
	addNode(BinaryTree, 7);
	addNode(BinaryTree, 9);
	addNode(BinaryTree, 11);

	Translate(BinaryTree);

	PrintTree(BinaryTree);
	system("pause");

	return 0;
}

思路:如果採用循環的的方式,這裏藉助容器,採用採用層序遍歷的思想將遍歷的所有節點保存在容器中,循環遍歷。

int _tmain(int argc, _TCHAR* argv[])
{
	Tree BinaryTree = NULL;//頭結點
	//bool bEnd = false;
	//while (!bEnd)
	//{
	//	int data = 0;
	//	cin>>data;
	//	if (data == -1)
	//	{
	//		bEnd = true;
	//	}
	//	else
	//		addNode(BinaryTree, data);
	//}
	addNode(BinaryTree, 8);
	addNode(BinaryTree, 6);
	addNode(BinaryTree, 10);
	addNode(BinaryTree, 5);
	addNode(BinaryTree, 7);
	addNode(BinaryTree, 9);
	addNode(BinaryTree, 11);

	//Translate(BinaryTree);

	vector<Tree> NodeVct;
	NodeVct.push_back(BinaryTree);
	int start = 0;
	int end = 1;
	Tree tree = NULL;
	Tree temp = NULL;
	while (start<end)
	{
		tree = NodeVct[start];
		if (tree)
		{
			temp = tree->m_pLeft;
			tree->m_pLeft = tree->m_pRight;
			tree->m_pRight = temp;
		}
		if (tree->m_pLeft)
		{
			NodeVct.push_back(tree->m_pLeft);
			++end;
		}
		if (tree->m_pRight)
		{
			NodeVct.push_back(tree->m_pRight);
			++end;
		}
		++start;
	}
	PrintTree(BinaryTree);
	system("pause");

	return 0;
}

July提供的答案

void Revertsetree(list *root)
{
    if(!root)
       return;
    list *p;

    p=root->leftch;
    root->leftch=root->rightch;
    root->rightch=p;

    if(root->leftch)
      Revertsetree(root->leftch);
    if(root->rightch)
      Revertsetree(root->rightch);
}

方法二

由於遞歸的本質是編譯器生成了一個函數調用的棧,
因此用循環來完成同樣任務時最簡單的辦法就是用一個輔助棧來模擬遞歸。

首先我們把樹的頭結點放入棧中。
在循環中,只要棧不爲空,彈出棧的棧頂結點,交換它的左右子樹。

如果它有左子樹,把它的左子樹壓入棧中;
如果它有右子樹,把它的右子樹壓入棧中。

這樣在下次循環中就能交換它兒子結點的左右子樹了。

//再用輔助棧模擬遞歸,改成循環的(有誤之處,望不吝指正):

void Revertsetree(list *phead)
{
    if(!phead)
       return;

    stack<list*> stacklist;
    stacklist.push(phead);         //首先把樹的頭結點放入棧中。

    while(stacklist.size())
    //在循環中,只要棧不爲空,彈出棧的棧頂結點,交換它的左右子樹
    {
      list* pnode=stacklist.top();
      stacklist.pop();
  
      list *ptemp;
      ptemp=pnode->leftch;
      pnode->leftch=pnode->rightch;
      pnode->rightch=ptemp;

      if(pnode->leftch)
        stacklist.push(pnode->leftch);   //若有左子樹,把它的左子樹壓入棧中
      if(pnode->rightch)
        stacklist.push(pnode->rightch);  //若有右子樹,把它的右子樹壓入棧中
    }
}

看答案後的感悟:

①在使用遞歸方法解決問題時,總是涉及到函數參數的進棧和出棧,我們可以合理的選擇輔助棧來解決這類問題。

②先序遍歷和後序遍歷對結果並無影響。

③採用輔助棧時,遍歷的順序實際是根右左。

發佈了21 篇原創文章 · 獲贊 9 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章