您的位置:首页 > 其它

二叉树的建立与遍历

2013-06-23 23:35 239 查看

一、概述

树是一种重要的数据结构,而二叉树则是树中重要的一类

二叉树是特殊的树,最明显的特点在于,每个结点都有左子结点和右子结点,且二者有序不能随意互换。

二叉树有两种特殊形式:满二叉树和完全二叉树,而且都具有一定的性质,具体参照偶之前写过《堆和堆排序》的文章

二叉树的重点是结点,通常存在二叉链接和三叉链接两种结点,区别在于三叉链接多了一个指向父结点的指针,因而三叉链接结点在遍历时比二叉具有优势。二叉与三叉结点适用于不同的场合和需求。

如无特殊说明,本文采用的是二叉结点

二、二叉树的建立

在堆中,我们利用数组来构建二叉树(完全二叉树)。但是如果用数组来表示一般的二叉树,则会造成内存空间的极度浪费。

所以,通常情况下,我们通过链表来建立二叉树。

和遍历类似,建立二叉树也有不同的方法,其建立的结果自然也有所不同。用的比较多的有“按顺序建立二叉树”、“从已有数据建立完全二叉树”等等。但是无论哪种方法,本质都需要通过递归实现。

本文的范例采用的是建立完全二叉树,因而需要了解一些与完全二叉树有关的数学性质

01
template
<
typename
T>
02
struct
TreeNode
03
{
04
T
 Data;
05
TreeNode<T>*
 pLeftNode;
06
TreeNode<T>*
 pRightNode;
07
};
08
09
template
<
typename
T>
10
CBinTree<T>::CBinTree(
const
T
 ary[],
int
nCount)
11
{
12
CreateBinTree(m_pRoot,
 ary, 0, nCount - 1);
13
}
14
15
template
<
typename
T>
16
void
CBinTree<T>::CreateBinTree(TreeNode<T>*&
 pNode,
const
T
 ary[],
int
i,
int
nIdxMax)
17
{
18
pNode
 =
new
TreeNode<T>;
19
_ASSERT(m_pRoot
 != NULL);
20
21
//
 zero-based
22
int
nLeftIdx
 = (i << 1) + 1;
23
int
nRightIdx
 = (i + 1) << 1;
24
25
pNode->Data
 = ary[i];
26
27
//
 if leftchild is empty, rightchild is empty too
28
if
(nLeftIdx
 <= nIdxMax)
29
{
30
CreateBinTree(pNode->pLeftNode,
 ary, nLeftIdx, nIdxMax);
31
32
if
(nRightIdx
 <= nIdxMax)
33
{
34
CreateBinTree(pNode->pRightNode,
 ary, nRightIdx, nIdxMax);
35
}
36
else
37
{
38
pNode->pRightNode
 = NULL;
39
}
40
}
41
else
42
{
43
pNode->pLeftNode
 = NULL;
44
pNode->pRightNode
 = NULL;
45
}
46
}
三、二叉树的遍历

二叉树的遍历方法有多种,最常用的有中序遍历、先序遍历和后序遍历。毫无例外,这三种遍历方法都是基于递归/迭代的思想

为了更好的说明三种遍历,结合图片。

假设现在存在{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

三种遍历代码如下:

01
template
<
typename
T>
02
inline
void
CBinTree<T>::DoVisit(TreeNode<T>*
 pNode)
03
{
04
wcout<<pNode->Data<<L
"\t"
;
05
}
06
07
08
template
<
typename
T>
09
void
CBinTree<T>::InorderTraversal()
10
{
11
InorderTraversal(m_pRoot);
12
}
13
14
15
template
<
typename
T>
16
void
CBinTree<T>::InorderTraversal(TreeNode<T>*
 pNode
/*
 = m_pRoot */
)
17
{
18
if
(pNode)
19
{
20
InorderTraversal(pNode->pLeftNode);
21
DoVisit(pNode);
22
InorderTraversal(pNode->pRightNode);
23
}
24
}
25
26
27
template
<
typename
T>
28
void
CBinTree<T>::PreorderTraversal()
29
{
30
PreorderTraversal(m_pRoot);
31
}
32
33
34
template
<
typename
T>
35
void
CBinTree<T>::PreorderTraversal(TreeNode<T>
 *pNode)
36
{
37
if
(pNode)
38
{
39
DoVisit(pNode);
40
PreorderTraversal(pNode->pLeftNode);
41
PreorderTraversal(pNode->pRightNode);
42
}
43
}
44
45
46
template
<
typename
T>
47
void
CBinTree<T>::PostorderTraversal()
48
{
49
PostorderTraversal(m_pRoot);
50
}
51
52
53
template
<
typename
T>
54
void
CBinTree<T>::PostorderTraversal(TreeNode<T>
 *pNode)
55
{
56
if
(pNode)
57
{
58
PostorderTraversal(pNode->pLeftNode);
59
PostorderTraversal(pNode->pRightNode);
60
DoVisit(pNode);
61
}
62
}
对比发现,差别只是在于DoVisit调用的位置

实例代码下载:Downlaod_Sample_Code

原文地址:点击打开链接
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: