一、树的定义和基本术语
1.树的定义
树(Tree)是n(n≧0)个结点的有限集合T,若n=0时称为空树,否则:
⑴ 有且只有一个特殊的称为树的根(Root)结点;
⑵ 若n>1时,其余的结点被分为m(m>0)个互不相交的子集T1, T2, T3…Tm,其中每个子集本身又是一棵树,称其为根的子树(Subtree)。
这是树的递归定义,即用树来定义树,而只有一个结点的树必定仅由根组成,如图6-1(a)所示。
2树的基本术语
⑴ 结点(node):一个数据元素及其若干指向其子树的分支。
⑵ 结点的度(degree)、树的度:结点所拥有的子树的棵数称为结点的度。树中结点度的最大值称为树的度。
如图6-1(b)中结点A的度是3,结点B的度是2,结点M的度是0,树的度是3 。
⑶ 叶子(left)结点、非叶子结点:树中度为0的结点称为叶子结点(或终端结点)。相对应地,度不为0的结点称为非叶子结点(或非终端结点或分支结点)。除根结点外,分支结点又称为内部结点。
如图6-1(b)中结点H、I、J、K、L、M、N是叶子结点,而所有其它结点都是分支结点。
⑷ 孩子结点、双亲结点、兄弟结点
一个结点的子树的根称为该结点的孩子结点(child)或子结点;相应地,该结点是其孩子结点的双亲结点(parent)或父结点。
如图6-1(b)中结点B、C、D是结点A的子结点,而结点A是结点B、C、D的父结点;类似地结点E、F是结点B的子结点,结点B是结点E、F的父结点。
同一双亲结点的所有子结点互称为兄弟结点。
如图6-1(b)中结点B 、C、D是兄弟结点;结点E、F是兄弟结点。
⑸ 层次、堂兄弟结点
规定树中根结点的层次为1,其余结点的层次等于其双亲结点的层次加1。
若某结点在第l(l≧1)层,则其子结点在第l+1层。
双亲结点在同一层上的所有结点互称为堂兄弟结点。如图6-1(b)中结点E、F、G、H、I、J。
⑹ 结点的层次路径、祖先、子孙
从根结点开始,到达某结点p所经过的所有结点成为结点p的层次路径(有且只有一条)。
结点p的层次路径上的所有结点(p除外)称为p的祖先(ancester)。
以某一结点为根的子树中的任意结点称为该结点的子孙结点(descent)。
⑺树的深度(depth):树中结点的最大层次值,又称为树的高度,如图6-1(b)中树的高度为4。
⑻ 有序树和无序树:对于一棵树,若其中每一个结点的子树(若有)具有一定的次序,则该树称为有序树,否则称为无序树。
⑼森林(forest):是m(m≧0)棵互不相交的树的集合。显然,若将一棵树的根结点删除,剩余的子树就构成了森林。
3 树的表示形式
⑴ 倒悬树。是最常用的表示形式,如图6-1(b)。
⑵ 嵌套集合。是一些集合的集体,对于任何两个集合,或者不相交,或者一个集合包含另一个集合。图6-2(a)是图6-1(b)树的嵌套集合形式。
⑶ 广义表形式。图6-2(b)是树的广义表形式。
⑷ 凹入法表示形式。
树的表示方法的多样化说明了树结构的重要性。
二、树的抽象数据类型定义
ADT Tree{
数据对象D:D是具有相同数据类型的数据元素的集合。
数据关系R:若D为空集,则称为空树;
……
基本操作:
……
} ADT Tree
三、二叉树
1 二叉树的定义
二叉树(Binarytree)是n(n≥0)个结点的有限集合。若n=0时称为空树,否则:
⑴有且只有一个特殊的称为树的根(Root)结点;
⑵若n>1时,其余的结点被分成为二个互不相交的子集T1,T2,分别称之为左、右子树,并且左、右子树又都是二叉树。
由此可知,二叉树的定义是递归的。
二叉树在树结构中起着非常重要的作用。因为二叉树结构简单,存储效率高,树的操作算法相对简单,且任何树都很容易转化成二叉树结构。上节中引入的有关树的术语也都适用于二叉树。
2.二叉树的基本形态
二叉树有5种基本形态,如图6-3所示
满二叉树的特点:
◆ 基本特点是每一层上的结点数总是最大结点数。
◆ 满二叉树的所有的支结点都有左、右子树。
◆ 可对满二叉树的结点进行连续编号,若规定从根结点开始,按“自上而下、自左至右”的原则进行。
完全二叉树(Complete Binary Tree):如果深度为k,由n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1到n的结点一一对应,该二叉树称为完全二叉树。或深度为k的满二叉树中编号从1到n的前n个结点构成了一棵深度为k的完全二叉树。
其中 2k-1 ≦n≦2k-1 。完全二叉树是满二叉树的一部分,而满二叉树是完全二叉树的特例。
完全二叉树的特点:
若完全二叉树的深度为k ,则所有的叶子结点都出现在第k层或k-1层。对于任一结点,如果其右子树的最大层次为l,则其左子树的最大层次为l或l+1。
四、实现代码
Tree.h
#pragma once
#include <iostream>
#include <string.h>
#include "stdio.h"
#include <iostream>
using namespace std;
#define MANLEN 20
typedef char DATA;
typedef struct CBT //定义二叉树的数据结构
{
DATA data;
struct CBT *left;
struct CBT *right;
}CBTType;
class Tree
{
public:
CBTType *InitTree(); //初始化
void AddTreeNode(CBTType *treeNode); //添加结点
CBTType *TreeFindNode(CBTType *treeNode,DATA data); //查找结点
CBTType *TreeLeftNode(CBTType *treeNode); //获得左子树
CBTType *TreeRightNode(CBTType *treeNode); //获得右子树
int TreeIsEmpty(CBTType *treeNode); //判断是否为空树
int TreeDepth(CBTType *treeNode); //计算二叉树深度
void ClearTree(CBTType *treeNode); //清空二叉树
void TreeNodeData(CBTType *p); //显示结点数据
void LevelTree(CBTType *treeNode,void (*TreeNodeData)(CBTType *p));//按层遍历
void DLRTree(CBTType *treeNode,void (*TreeNodeData)(CBTType *p)); //先序遍历
void LDRTree(CBTType *treeNode,void (*TreeNodeData)(CBTType *p)); //中序遍历
void LRDTree(CBTType *treeNode,void (*TreeNodeData)(CBTType *p)); //后序遍历
};
Tree.cpp
#include "queue.h"
#include <conio.h>
CBTType *Tree::InitTree()
{
CBTType *node;
if (node = (CBTType*)malloc(sizeof(CBTType)))
{
std::cout<<"请先输入一个根结点数据:\n";
scanf("%c",&node->data);
node->left = NULL;
node->right = NULL;
if (node != NULL)
{
return node;
}
else
{
return NULL;
}
}
return NULL;
}
void Tree::AddTreeNode(CBTType *treeNode)
{
CBTType *pnode,*parent;
char data;
char menusel;
if (pnode = (CBTType*)malloc(sizeof(CBTType)))
{
std::cout<<"输入二叉树结点数据:\n";
fflush(stdin);
scanf("%c",&pnode->data);
pnode->left = NULL;
pnode->right = NULL;
std::cout<<"输入该结点的父结点数据:\n";
fflush(stdin);
scanf("%c",&data);
parent = TreeFindNode(treeNode, data);
if (!parent)
{
std::cout<<"未找到该父结点!\n";
free(pnode);
return;
}
std::cout<<"1.添加该结点到左子树\n2.添加该结点到右子树\n";
do
{
//scanf("%s",&menusel);
menusel = getch();
menusel -='0';
if (menusel == 1 || menusel == 2)
{
if (parent == NULL)
{
std::cout<<"不存在父结点,请先设置父结点!\n";
}
else
{
switch (menusel)
{
case 1:
if (parent->left)
{
std::cout<<"左子树结点不为空!\n";
}
else
{
parent->left = pnode;
}
break;
case 2:
if (parent->right)
{
std::cout<<"右子树结点不为空!\n";
}
else
{
parent->right = pnode;
}
break;
default:
std::cout<<"无效参数!\n";
}
}
}
} while (menusel != 1 && menusel != 2);
}
}
CBTType *Tree::TreeFindNode(CBTType *treeNode, DATA data)
{
CBTType *ptr;
if (treeNode == NULL)
{
return NULL;
}
else
{
if (treeNode->data == data)
{
return treeNode;
}
else
{
if (ptr = TreeFindNode(treeNode->left, data))
{
return ptr;
}
else if(ptr =TreeFindNode(treeNode->right, data))
{
return ptr;
}
else
{
return NULL;
}
}
}
}
CBTType *Tree::TreeLeftNode(CBTType *treeNode)
{
if (treeNode)
{
return treeNode->left;
}
else
{
return NULL;
}
}
CBTType *Tree::TreeRightNode(CBTType *treeNode)
{
if (treeNode)
{
return treeNode->right;
}
else
{
return NULL;
}
}
int Tree::TreeIsEmpty(CBTType *treeNode)
{
if (treeNode)
{
return 0;
}
else
{
return 1;
}
}
int Tree::TreeDepth(CBTType *treeNode)
{
int depleft,depright;
if (treeNode == NULL)
{
return 0;
}
else
{
depleft = TreeDepth(treeNode->left);
depright = TreeDepth(treeNode->right);
if (depleft > depright)
{
return depleft + 1;
}
else
{
return depright + 1;
}
}
}
void Tree::ClearTree(CBTType *treeNode)
{
if (treeNode)
{
ClearTree(treeNode->left);
ClearTree(treeNode->right);
free(treeNode);
treeNode = NULL;
}
}
void Tree::TreeNodeData(CBTType *p)
{
std::cout<<p->data<<" ";
}
void Tree::LevelTree(CBTType *treeNode, void (*TreeNodeData)(CBTType *))
{
CBTType *p;
CBTType *q[MANLEN];
int head = 0,tail = 0;
if (treeNode)
{
tail = (tail + 1) % MANLEN;
q[tail] = treeNode;
}
while (head != tail)
{
head = (head + 1) % MANLEN;
p = q[head];
TreeNodeData(p);
if (p->left != NULL)
{
tail = (tail + 1) % MANLEN;
q[tail] = p->left;
}
if (p->right != NULL)
{
tail = (tail + 1) % MANLEN;
q[tail] = p->right;
}
}
}
void Tree::DLRTree(CBTType *treeNode, void (*TreeNodeData)(CBTType *p))
{
if (treeNode)
{
TreeNodeData(treeNode);
DLRTree(treeNode->left, TreeNodeData);
DLRTree(treeNode->right,TreeNodeData);
}
}
void Tree::LDRTree(CBTType *treeNode, void (*TreeNodeData)(CBTType *))
{
if (treeNode)
{
LDRTree(treeNode->left, TreeNodeData);
TreeNodeData(treeNode);
LDRTree(treeNode->right, TreeNodeData);
}
}
void Tree::LRDTree(CBTType *treeNode, void (*TreeNodeData)(CBTType *))
{
if (treeNode)
{
LRDTree(treeNode->left, TreeNodeData);
LRDTree(treeNode->right, TreeNodeData);
TreeNodeData(treeNode);
}
}
main.cpp
#include <iostream>
using namespace std;
#include "Tree.h"
#include <conio.h>
#include <string.h>
using namespace std;
void TreeNodeData2(CBTType *p)
{
printf("%c",p->data);
}
int main()
{
CBTType *root = NULL;
char menusel;
Tree *myTree = new Tree();
void(*TreeNodeData1)(CBTType *p);
TreeNodeData1 =TreeNodeData2;
//设置根结点
root = myTree->InitTree();
do
{
std::cout<<"请输入添加二叉树的结点\n";
std::cout<<"输入0退出\n";
std::cout<<"1.添加二叉树的结点\n";
menusel = getch();
switch (menusel) {
case '1':
myTree->AddTreeNode(root);
break;
case '0':
break;
default:
break;
}
} while (menusel != '0');
//遍历
do
{
std::cout<<"请选择遍历二叉树,输入0退出\n";
std::cout<<"1.先序遍历 DLR \t"<<"2.中序遍历 LDR\n";
std::cout<<"3.后序遍历 LRD \t"<<"4.按层遍历 LDR\n";
scanf("%c",&menusel);
switch (menusel) {
case '0':
break;
case '1':
std::cout<<"1.先序遍历 DLR的结果:";
myTree->DLRTree(root, TreeNodeData1);
std::cout<<"\n";
break;
case '2':
std::cout<<"2.中序遍历 LDR的结果:";
myTree->LDRTree(root, TreeNodeData1);
std::cout<<"\n";
break;
case '3':
std::cout<<"3.后序遍历 LRD的结果:";
myTree->LRDTree(root, TreeNodeData1);
std::cout<<"\n";
break;
case '4':
std::cout<<"4.后序遍历 LRD的结果:";
myTree->LevelTree(root, TreeNodeData1);
std::cout<<"\n";
break;
default:
;
}
} while (menusel != '0');
printf("\n二叉树深度为:%d\n",myTree->TreeDepth(root));
myTree->ClearTree(root);
root = NULL;
delete myTree;
cout << "helloWorld!";
system("pause");
return 0;
}
演示效果图:
参考书籍:《C/C++常用算法手册》 《数据结构-清华大学严蔚敏》