一、概述
树是一种重要的数据结构,而二叉树则是树中重要的一类
二叉树是特殊的树,最明显的特点在于,每个结点都有左子结点和右子结点,且二者有序不能随意互换。
二叉树有两种特殊形式:满二叉树和完全二叉树,而且都具有一定的性质,具体参照偶之前写过《堆和堆排序》的文章
二叉树的重点是结点,通常存在二叉链接和三叉链接两种结点,区别在于三叉链接多了一个指向父结点的指针,因而三叉链接结点在遍历时比二叉具有优势。二叉与三叉结点适用于不同的场合和需求。
如无特殊说明,本文采用的是二叉结点
二、二叉树的建立
在堆中,我们利用数组来构建二叉树(完全二叉树)。但是如果用数组来表示一般的二叉树,则会造成内存空间的极度浪费。
所以,通常情况下,我们通过链表来建立二叉树。
和遍历类似,建立二叉树也有不同的方法,其建立的结果自然也有所不同。用的比较多的有“按顺序建立二叉树”、“从已有数据建立完全二叉树”等等。但是无论哪种方法,本质都需要通过递归实现。
本文的范例采用的是建立完全二叉树,因而需要了解一些与完全二叉树有关的数学性质
05 | TreeNode<T>*
pLeftNode; |
06 | TreeNode<T>*
pRightNode; |
10 | CBinTree<T>::CBinTree( const T
ary[], int nCount) |
12 | CreateBinTree(m_pRoot,
ary, 0, nCount - 1); |
16 | void CBinTree<T>::CreateBinTree(TreeNode<T>*&
pNode, const T
ary[], int i, int nIdxMax) |
18 | pNode
= new TreeNode<T>; |
19 | _ASSERT(m_pRoot
!= NULL); |
22 | int nLeftIdx
= (i << 1) + 1; |
23 | int nRightIdx
= (i + 1) << 1; |
27 | //
if leftchild is empty, rightchild is empty too |
28 | if (nLeftIdx
<= nIdxMax) |
30 | CreateBinTree(pNode->pLeftNode,
ary, nLeftIdx, nIdxMax); |
32 | if (nRightIdx
<= nIdxMax) |
34 | CreateBinTree(pNode->pRightNode,
ary, nRightIdx, nIdxMax); |
38 | pNode->pRightNode
= NULL; |
43 | pNode->pLeftNode
= NULL; |
44 | pNode->pRightNode
= NULL; |
三、二叉树的遍历
二叉树的遍历方法有多种,最常用的有中序遍历、先序遍历和后序遍历。毫无例外,这三种遍历方法都是基于递归/迭代的思想
为了更好的说明三种遍历,结合图片。
假设现在存在{1,3,5,7,9,2,4,6,8,10}的一个完全二叉树
中序遍历:遍历时先尝试访问当前结点的左子结点,如果左子结点不存在,则读取当前结点的数据,然后再访问当前结点的右子结点。
对应的遍历结果是:6 7 8 3 10 9 1 2 5 4
先序遍历:遍历时先读取当前结点的数据,然后在访问当前结点左子结点。仅当左子结点不存在,则访问右子结点
对应的遍历结果是:1 3 7 6 8 9 10 5 2 4
后序遍历:遍历时先访问当前结点的左子结点,当左子结点不存在时,访问右子结点。当且仅当右子结点也不存在时,读取当前结点数据
对应遍历结果是:6 8 7 10 9 3 2 4 5 1
三种遍历代码如下:
02 | inline void CBinTree<T>::DoVisit(TreeNode<T>*
pNode) |
04 | wcout<<pNode->Data<<L "\t" ; |
09 | void CBinTree<T>::InorderTraversal() |
11 | InorderTraversal(m_pRoot); |
16 | void CBinTree<T>::InorderTraversal(TreeNode<T>*
pNode /*
= m_pRoot */ ) |
20 | InorderTraversal(pNode->pLeftNode); |
22 | InorderTraversal(pNode->pRightNode); |
28 | void CBinTree<T>::PreorderTraversal() |
30 | PreorderTraversal(m_pRoot); |
35 | void CBinTree<T>::PreorderTraversal(TreeNode<T>
*pNode) |
40 | PreorderTraversal(pNode->pLeftNode); |
41 | PreorderTraversal(pNode->pRightNode); |
47 | void CBinTree<T>::PostorderTraversal() |
49 | PostorderTraversal(m_pRoot); |
54 | void CBinTree<T>::PostorderTraversal(TreeNode<T>
*pNode) |
58 | PostorderTraversal(pNode->pLeftNode); |
59 | PostorderTraversal(pNode->pRightNode); |
对比发现,差别只是在于DoVisit调用的位置
实例代码下载:
Downlaod_Sample_Code原文地址:
点击打开链接