微软面试题解题笔记——二元查找树的镜像转换

(题目来源于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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章