题目:输入一颗二元查找树,将该树转换为它的镜像,即在转换后的二元查找树中,左子树的结点都大于右子树的结点。用递归和循环两种方法完成树的镜像转换。
例如输入:
8
/ /
6 10
// //
5 7 9 11
输出:
8
/ /
10 6
// //
11 9 7 5
定义二元查找树的结点为:
template<typename T> struct BinTreeNode{ T data; BinTreeNode<T> *leftChild; BinTreeNode<T> *rightChild; BinTreeNode():leftChild(NULL),rightChild(NULL){} BinTreeNode(T x, BinTreeNode<T> *l = NULL,BinTreeNode<T> *r = NULL):leftChild(l),rightChild(r) { data = x; } };
分析:尽管我们可能一下子不能理解镜像是什么意思,但上面的例子给我们的直观感觉,就是交换结点的左右子树。我们试着在遍历例子中的二元查找树的同时来交换每个结点的左右子树。遍历时首先访问头结点8,我们交换它的左右子树得到:
8
/ /
10 6
// //
9 11 5 7
我们发现两个结点6和10的左右子树仍然是左结点的值小于右结点的值,我们再试着交换他们的左右子树,得到:
8
/ /
10 6
// //
11 9 7 5
刚好就是要求的输出。
上面的分析印证了我们的直觉:在遍历二元查找树时每访问到一个结点,交换它的左右子树。这种思路用递归不难实现,将遍历二元查找树的代码稍作修改就可以了。参考代码如下:
void Mirror(BinTreeNode<int> *pNode) { if (NULL == pNode) { return; } if (pNode->leftChild && pNode->rightChild) { BinTreeNode<int> *pTempNode = pNode->leftChild; pNode->leftChild = pNode->rightChild; pNode->rightChild = pTempNode; } if (pNode->leftChild) { MirrorRecursively(pNode->leftChild); } if (pNode->rightChild) { MirrorRecursively(pNode->rightChild); } }
由于递归的本质是编译器生成了一个函数调用的栈,因此用循环来完成同样任务时最简单的办法就是用一个辅助栈来模拟递归。首先我们把树的头结点放入栈中。在循环中,只要栈不为空,弹出栈的栈顶结点,交换它的左右子树。如果它有左子树,把它的左子树压入栈中;如果它有右子树,把它的右子树压入栈中。这样在下次循环中就能交换它儿子结点的左右子树了。参考代码如下:
void Mirror(BinTreeNode<int> *pTreeHead) { if (!pTreeHead) { return; } stack<BinTreeNode<int> *> stackTreeNode; stackTreeNode.push(pTreeHead); while (stackTreeNode.size()) { BinTreeNode<int> *pCurNode = stackTreeNode.top(); stackTreeNode.pop(); BinTreeNode<int> * pTempNode = pCurNode->leftChild; pCurNode->leftChild = pCurNode->rightChild; pCurNode->rightChild = pTempNode; if (pCurNode->leftChild) { stackTreeNode.push(pCurNode->leftChild); } if (pCurNode->rightChild) { stackTreeNode.push(pCurNode->rightChild); } } }