树
树的特点:除了根结点之外每个结点只有一个父结点,根结点没有父结点;除了叶结点之外的所有结点都有一个或多个子结点,叶结点没有子结点。
二叉树的遍历:
(1)前序遍历:先访问根结点,再访问左子结点,最后访问右子结点。
(2)中序遍历:先访问左子结点,再访问根结点,最后访问有右子节点。
(3)后序遍历:先访问左子结点,再访问右子结点,最后访问根结点。
在二叉树的前序遍历中,第一个数字总是树的根结点的值。中序遍历的序列中,根结点的值在序列的中间,左子树的结点的值位于根结点的值的左边,而右子树的结点的值位于根结点的值的右边。
优先遍历和深度优先遍历
二叉树的两个特例:堆和红黑树。堆分为最大堆和最小堆。在最大堆中根结点的值最大,在最小堆中根结点的值最小。有很多需要快速找到最大值或者最小值的问题都可以用堆来解决。红黑树是把树中的结点定义为红黑两种颜色,并通过规则确保从根结点到叶结点的最长路径的长度不超过最短路径的两倍。在C++的STL中,set、multiset、map、multimap等数据结构都是基于红黑树实现的。
重建二叉树
6.输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出如下所示的二叉树并输出它的头结点。二叉树结点的定义如下:
struct BinaryTreeNode{ int m_nvalue; BinaryTreeNode* m_pLeft; BinaryTreeNode* m_pRight; };
代码实现:
BinaryTreeNode* Construct(int *preorder, int *inorder, int length){ if(preorder == NULL || inorder == NULL || length <= 0){ return NULL; } return ConstructCore(preorder, preorder + length -1, inorder, inorder + length -1); } BinaryTreeNode* ConstructCore(int *startPreorder, int *endPreorder, int *startInorder, int *endInorder){ //前序遍历序列的第一个数字是根结点的值 int rootValue = startPreorder[0]; BinaryTreeNode* root = new BinaryTreeNode(); root->m_nValue = rootValue; root->m_pLeft = root->m_pRight = NULL; if(startPreorder == endPreorder){ if(startInorder == endInorder && *startPreorder == *startInorder) return ; else throw std::exception("Invalid input."); } //在中序遍历中找到根结点的值 int *rootInorder = startInorder; while(rootInorder <= endInorder && *rootInorder != rootValue) ++rootInorder; if(rootInorder == endInorder - startInorder) throw std::exception("Invalid input."); int leftLength = rootInorder - startInorder; int *LeftPreorderEnd = startPreorder + leftLength; if(leftLength > 0){ //构建左子树 root->m_pLeft = ConstructCore(startPreorder + 1, leftPreorderEnd, startInorder, rootInorder - 1); } if(leftLength < endPreorder - startPreorder){ //构建右子树 root->m_pRight = ConstructCore(leftPreorderEnd + 1, endPreorder, rootInorder + 1, endInorder); } return root; }
在函数ConstructCore中,我们先根据前序遍历序列的第一个数字创建根结点,接下来在中序遍历序列中找到根结点的位置,这样就能确定左、右子树结点的数量。在前序遍历中和中序遍历的序列中划分了左、右子树结点的值之后,递归调用函数ConstructCore,去分别构建它的左右子树。
测试用例:
(1)普通二叉树(完全二叉树,不完全二叉树)
(2)特殊二叉树(所有结点都没有右子树结点的二叉树,所有结点的左子树结点的二叉树,只有一个结点的二叉树)
(3)特殊输入测试(二叉树的根结点指针为NULL、输入前序遍历序列和中序遍历序列不匹配)。