一、树的基本知识
1、定义:有且只有一个称为根的结点,有若干个互不相交的子树,这些子树本身也是树。
2、其组成:
- 树是由节点和边组成
- 每个节点只有一个父节点,但可以有多个子节点
- 根节点没有父节点
- 深度:从根节点到最底层节点的层数,根节点为第一层。
- 叶子节点:没有子节点的节点
- 度:子节点的个数
3、树的分类
- 一般树:任意一个节点的子节点个数都不受限制。
- 二叉树:任意一个节点的子节点个数最多两个,且子节点位置不可以更改
- 森林:n个互不相交的树的集合
4、二叉树的分类(如下分类的前提是二叉树)
- 一般二叉树
- 满二叉树:在不增加树的层数的情况下,无法再添加一个节点
- 完全二叉树:如果只是删除了满二叉树最底层最右边的连续若干个节点,这样形成的树就是完全二叉树。(必须是连续的)
- 二叉搜索树:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。
【注】:完全二叉树的优缺点:方便查到该节点的深度、父节点、是否有子节点等;缺点是比较耗内存。
5、树的存储
(1)、二叉树的存储
- 连续存储(即完全二叉树):以数组的方式来组织。要以数组的方式来存储,必须先将其转换成满二叉树,再转换成完全二叉树。
- 链式存储:是用链表的形式,用指针来保存对应的节点地址。形式如下
(2)、一般树的存储
- 双亲表示法(求父节点容易)
- 孩子表示法(求子节点容易)
- 双亲孩子表示法
- 二叉树表示法
(3)、森林的存储
先将森林转化成二叉树,再进行存储。
6、树的应用
- 树是数据库中数据组织的一种重要形式
- 操作系统子父进程的关系本身是一棵树
二、树的操作(常常指二叉树)
1、二叉树的遍历:先序遍历、中序遍历、后序遍历
(1)、先序遍历:
- 先访问根节点
- 再先序访问左子树
- 再先序访问右子树
(2)、中序遍历:
- 先中序遍历左子树
- 再访问根节点
- 再中序遍历右子树
(3)、后序遍历:
- 先中序遍历左子树
- 再中序遍历右子树
- 再访问根节点
2、已知二叉树的先序遍历、中序遍历、后续遍历中的任意一种,都无法还原二叉树的原始结构。但知道其中的两种序列(除掉 “先序+后序” 这种组合),即可求出该二叉树。【先序+中序、中序+后序 这两种组合可以唯一确定一个二叉树】
(1)、已知二叉树的先序+中序。
(1-1)、先序:ABCDEFGH,中序:BDCEAFHG,求该二叉树及后序。
二叉树如下,后序为:DECB HGFA
(1-2)、先序:ABDGHCEFI,中序:GDHBAECIF,求该二叉树及后序
二叉树如下,后序为:GHDBEIFCA
(2)、已知二叉树的中序+后序
(2-1)、中序:BDCEAFHG,后序:DECBHGFA
二叉树如下。先序为:ABCDEFGH
三、链式二叉树的操作
1、如下图所示的链式二叉树的结构,对其进行创建和遍历
2、代码如下
#include<iostream>
#include<string>
using namespace std;
struct BTreeNode
{
string data;
BTreeNode* pLChild;
BTreeNode* pRChild;
};
// 生成静态的二叉树链表
BTreeNode* CreateBTree()
{
BTreeNode* pA = new BTreeNode();
BTreeNode* pB = new BTreeNode();
BTreeNode* pC = new BTreeNode();
BTreeNode* pD = new BTreeNode();
BTreeNode* pE = new BTreeNode();
pA->data = "A";
pB->data = "B";
pC->data = "C";
pD->data = "D";
pE->data = "E";
pA->pLChild = pB;
pA->pRChild = pC;
pB->pLChild = pB->pRChild = NULL;
pC->pLChild = pD;
pC->pRChild = NULL;
pD->pLChild = NULL;
pD->pRChild = pE;
pE->pLChild = pE->pRChild = NULL;
return pA;
}
// 二叉树的三种遍历方式:先序、中序、后序
// 先序遍历二叉树 :ABCDE
void PreTraverseBTree(BTreeNode* pRNode)
{
if (pRNode!=NULL)
{
cout << pRNode->data << endl;
if (pRNode->pLChild!=NULL)
{
PreTraverseBTree(pRNode->pLChild);
}
if (pRNode->pRChild!=NULL)
{
PreTraverseBTree(pRNode->pRChild);
}
}
/* 伪算法:
if(根节点不为空)
{
先访问根节点
if(左子树不为空)
{
再先序访问左子树
}
if(右子树不为空)
{
再先序访问右子树
}
}
*/
}
// 中序遍历二叉树:BADEC
void InterTraverseBTree(BTreeNode* pRNode)
{
if (pRNode!=NULL)
{
if (pRNode->pLChild!=NULL)
{
InterTraverseBTree(pRNode->pLChild);
}
cout << pRNode->data << endl;
if (pRNode->pRChild!=NULL)
{
InterTraverseBTree(pRNode->pRChild);
}
}
/* 伪算法:
if(根节点不为空)
{
if(左子树不为空)
{
先中序遍历左子树
}
再访问根节点
if(右子树不为空)
{
再中序遍历右子树
}
}
*/
}
// 后序遍历二叉树:BEDCA
void PostTraverseBTree(BTreeNode* pRNode)
{
if (pRNode!=NULL)
{
if (pRNode->pLChild!=NULL)
{
PostTraverseBTree(pRNode->pLChild);
}
if (pRNode->pRChild!=NULL)
{
PostTraverseBTree(pRNode->pRChild);
}
cout << pRNode->data << endl;
}
/* 伪算法:
if(根节点不为空)
{
if(左子树不为空)
{
先中序遍历左子树
}
if(右子树不为空)
{
再中序遍历右子树
}
再访问根节点
}
*/
}
int main()
{
BTreeNode* p = CreateBTree();
cout << "先序遍历--------------\n";
PreTraverseBTree(p);
cout << "中序遍历----------------\n";
InterTraverseBTree(p);
cout << "后序遍历------------------\n";
PostTraverseBTree(p);
system("pause");
}
运行结果如下