树是n(n>0)个节点的有限集合T,并且满足:
1)有一个被称为根(root)的节点
2) 如果有其他节点,则可分为若干个互不相交的子集。每个子集又是一棵树,且称为根节点的子树。子树的根是树根的子节点。
两种结构:
二叉树(Binary Tree) 是节点的有限集合。与树不同,它在现实生活并没有很广泛的对应关系,偏向工具性。
或者为空,或者由一个根节点及两棵互不相交的左、右子树构成,其左右子树又都是二叉树。
注 意 \color{red}{注意} 注意:二叉树必须严格区分左右子树。
(需注意其中的d和e并不相同)
从中也可以看出,二叉树和树并不相同(树并不会区分左右):
一棵二叉树中任意一层的节点个数都达到了最大值(高度为k,则节点个数为 1 + 2 + 2 2 + 2 3 + ⋯ + 2 k − 1 = 2 k − 1 1+2+2^2 + 2^3+ \cdots+2^{k-1}=2^k-1 1+2+22+23+⋯+2k−1=2k−1)
在满二叉树的最底层自右向左依次去掉若干个节点
(自右向左使得二叉树具有许多好的性质,将会在下面进行介绍)
特点:
完全二叉树的顺序存储
根据2.2 的编号性质,可以省略左右孩子字段(编号为偶数,则为左孩子)
普通二叉树的顺序存储
将普通的树修补成完全二叉树
顺序存储的特点:
二叉树类的定义
//BTree类的前向说明
template<class elemType>
class BTree;
template<class elemType>
class Node {
friend class BTree<elemType>;//声明BTree类为友元
private:
elemType data;
Node* left;
Node* right;
public:
Node() {
left = NULL;
right = NULL;
}
Node(const elemType& e, Node* L = NULL, Node* R = NULL) {
data = e;
left = L;
right = R;
}
};
template<class elemType>
class BTree {
private:
Node<elemType>* root;
//elemType stopFlag;
int Size(Node<elemType>* t);//求以t为根的二叉树的结点个数
int Height(Node<elemType>* t); //求以t为根的二叉树的高度。
void DelTree(Node<elemType>* t);//删除以t为根的二叉树
void PreOrder(Node<elemType>* t);
// 按前序遍历输出以t为根的二叉树的结点的数据值
void InOrder(Node<elemType>* t);
// 按中序遍历输出以t为根的二叉树的结点的数据值
void PostOrder(Node<elemType>* t);
// 按后序遍历输出以t为根的二叉树的结点的数据值
public:
BTree() { root = NULL; }
void createTree(const elemType& flag);//创建一棵二叉树
bool isEmpty() { return (root == NULL); }// 判断叉树是否为空
Node<elemType>* GetRoot() { return root; }
int Size(); //求二叉树的结点个数。
int Height(); //求二叉树的高度。
void DelTree();//删除二叉树
void PreOrder();// 按前序遍历输出二叉树的结点的数据值
void InOrder();// 按中序遍历输出二叉树的结点的数据值
void PostOrder();// 按后序遍历输出二叉树的结点的数据值
void LevelOrder();// 按中序遍历输出二叉树的结点的数据值
};
思路:
算法实现:
实现代码:
template<class elemType>
void BTree<elemType>::createTree(const elemType& flag) {
//因为队列元素为结点地址,即为Node*
seqQueue<Node<elemType>*> que;
elemType x;
Node<elemType>* p;
elemType chL, chR;
Node<elemType>* LP, *RP;
cout << "Please input the value of the root:" << endl;
cin >> x;
if (x == flag) return;
else root = new Node<elemType>(x);
que.enQueue(root);//root地址进队
//如果队列不为空,则出队
while (!que.isEmpty()) {
p = que.deQueue();
cout << "please input the children of " << p->data << ":" << endl;
cin >> chL >> chR;
if (chL != flag) {//该结点有左孩子
LP = new Node<elemType>(chL);//new结点
p->left = LP;//链到父亲节点
que.enQueue(LP);//当前节点地址进队
}
if (chR != flag) {//该结点有右孩子
RP = new Node<elemType>(chR);
p->right = RP;
que.enQueue(RP);
}
}
}
在遍历中逐渐体会 “递归是树的灵魂” 这句话
遍历方式:
//前序遍历的实现
template<class elemType>
void BTree<elemType>::PreOrder() {
PreOrder(root);
}
template<class elemType>
void BTree<elemType>::PreOrder(Node<elemType>* t) {
if (!t) return;
cout << t->data; //访问根节点
PreOrder(t->left);
PreOrder(t->right);
}
//中序遍历的实现
template<class elemType>
void BTree<elemType>::InOrder() {
InOrder(root);
}
template<class elemType>
void BTree<elemType>::InOrder(Node<elemType>* t) {
if (!t) return;
InOrder(t->left);
cout << t->data; //访问根节点
InOrder(t->right);
}
//后序遍历的实现
template<class elemType>
void BTree<elemType>::PostOrder() {
PostOrder(root);
}
template<class elemType>
void BTree<elemType>::PostOrder(Node<elemType>* t) {
if (!t) return;
PostOrder(t->left);
PostOrder(t->right);
cout << t->data; //访问根节点
}
递归的使用条件:
数的规模=左子树的规模+右子树的规模+1(根)
//求规模
template<class elemType>
int BTree<elemType>::Size() {
return Size(root);//要实现递归,就需要传入可以变化的参数
}
template<class elemType>
int BTree<elemType>::Size(Node<elemType>* t) {
if (!t) return 0;//递归终止条件 t=NULL
return 1 + Size(t->left) + Size(t->right);
}
数的高度: 1 + max ( 左 子 树 高 度 , 右 子 树 高 度 ) 1+\max(左子树高度,右子树高度) 1+max(左子树高度,右子树高度)
//求高度
template <class elemType>
int BTree<elemType>::Height() {
return Height(root);
}
template <class elemType>
int BTree<elemType>::Height(Node<elemType>* t)
//得到以t为根二叉树的高度,递归算法实现。
{
int hl, hr;
if (!t) return 0;//如果t为空,递归终止
hl = Height(t->left);
hr = Height(t->right);
if (hl >= hr) return 1 + hl;
return 1 + hr;
}
先清除左右子树,再删除根节点
//删除树
template <class elemType>
void BTree<elemType>::DelTree()
{
DelTree(root);
root = NULL;
}
template <class elemType>
void BTree<elemType>::DelTree(Node<elemType>* t)
//删除以t为根的二叉树,递归算法实现
{
if (!t) return;
DelTree(t->left); DelTree(t->right);
delete t;//删除根节点
}
递归是程序设计中强有力的工具,计算思维中核心思维之一,结构清晰、明了、美观,但是它的时间、空间效率比较低,所以在实际使用中,尝尝希望使用它的非递归版本。
下面这些不太好用语言描述具体过程,但是可以看懂代码的话还是比较好理解的,可以按着代码的程序走一遍(确定是对的)
利用队列实现层次遍历
//层次遍历的非递归实现
template<class elemType>
void BTree<elemType>::LevelOrder() {
seqQueue<Node<elemType>*>que(10);
Node<elemType>* p;
if (!root) return;//二叉树为空
que.enQueue(root);
while (!que.isEmpty()) {
p = que.getHead();
que.deQueue();
cout << p->data;
if (p->left) que.enQueue(p->left);
if (p->right) que.enQueue(p->right);
}
cout << endl;
}
前序遍历: 先访问根,然后访问左子和右子
可以设置一个栈,保存节点的地址
把根节点地址压栈。重复执行下述过程,直至栈空:
//前序遍历的非递归实现
//利用非递归的形式,可以求得许多性质,例如规模、检查某个值是否存在等等
template<class elemType>
void BTree<elemType>::PreOrder() {
if (!root) return;
Node<elemType>* p;
seqStack<Node<elemType>*> s;
s.push(root);
while (!s.isEmpty()) {
p = s.pop();
cout << p->data;
if (p->right) s.push(p->right);
if (p->left) s.push(p->left);
}
}
把根节点地址压栈。重复执行下述过程,直至栈空:
//中序遍历的非递归实现
template<class elemType>
void BTree<elemType>::InOrder() {
if (!root) return;
seqStack<Node<elemType>*> s(10);
Node<elemType>* p;
p = root;
s.push(root);
while (p->left) {//压一路左子
s.push(p->left);
p = p->left;
}
while (!s.isEmpty()) {//如果栈不空
p = s.pop();//弹出并访问
cout << p->data;
if (p->right) {//如果右子存在
s.push(p->right);
p = p->right;
while (p->left) {//如果右子有左子则一路压下去
s.push(p->left);
p = p->left;
}
}
}
}
01状态,用1表示已经访问过该结点的左右子
创建两个栈,把根节点地址压入栈1,访问状态(0表示0次访问,1表示一次访问)压入栈2。重复执行下述过程,直至栈空:
//后序遍历的非递归实现_01状态
template<class elemType>
void BTree<elemType>::PostOrder() {
seqStack<Node<elemType>*> s1;
seqStack<int> s2;
Node<elemType>* p;
int state;
s1.push(root);
s2.push(0);
while (!s1.isEmpty()) {//栈不空
state = s2.pop();
if (state == 1) {
p = s1.pop();
cout << p->data;
}
else {
s2.push(1);//当前节点状态由0变为1
p = s1.Top();
if (p->right) {//如果有右子,右子压栈,且状态为0
s1.push(p->right);
s2.push(0);
}
if (p->left) {//如果有左子,左子压栈,且状态为0
s1.push(p->left);
s2.push(0);
}
}
}
}
12状态,用1表示已经访问过该结点的左子,用2表示已经访问过该结点的右子
根节点入栈,把根节点地址压栈1,状态0压入栈2,重复执行下述过程,直至栈空:
//后序遍历的非递归实现_12状态
template<class elemType>
void BTree<elemType>::PostOrder() {
if (!root) return;
seqStack<Node<elemType>*> s1;
seqStack<int> s2;
Node<elemType>* p;
int state;
s1.push(root);
s2.push(0);
while (!s1.isEmpty()) {
p = s1.Top();
state = s2.pop();
switch (state)
{
case 2:
s1.pop();
cout << p->data;
break;
case 1:
s2.push(2);
if (p->right) {
s1.push(p->right);
s2.push(0);
}
break;
case 0:
s2.push(1);
if (p->left) {
s1.push(p->left);
s2.push(0);
}
break;
default:
break;
}
}
}
直接上结论:
信息互补:
前序和后序可以用来确定根节点
中序确定左右子树
例如:
算法实现:前序+中序
template<class elemType>
Node<elemType>* BTree<elemType>::buildTree(elemType pre[], int pl, int pr, elemType mid[], int ml, int mr) {
//pre数组存储前序遍历序列,pl为序列左边界下标,pr为序列右边界下标
//min数组存储中序遍历序列,ml为序列左边界下标,mr为序列右边界下标
Node<elemType>* p, * leftRoot, * rightRoot;
int i, pos, num;
int lpl, lpr, lml, lmr;//左子树中前序的左右边界、中序的左右边界
int rpl, rpr, rml, rmr;//右子树的前序的左右边界、中序的左右边界
if (pl > pr) return NULL;//递归终止条件
p = new Node<elemType>(pre[pl]);//找到子树的根并创建节点
if (!root) root = p;
//找根在中序中的位置和左子树的节点个数
for (i = ml; i <= mr; i++) {
if (mid[i] == pre[pl])
break;
}
pos = i;
num = pos - ml;//左子树中的节点个数
//找左子树的前序中序下标范围
lpl = pl + 1; lpr = pl + num;
lml = ml; lmr = pos - 1;
leftRoot = buildTree(pre, lpl, lpr, mid, lml, lmr);
//找右子树的前序中序下标范围
rpl = pl + num + 1; rpr = pr;
rml = pos + 1; rmr = mr;
rightRoot = buildTree(pre, rpl, rpr, mid, rml, rmr);
p->left = leftRoot;
p->right = rightRoot;
return p;
}
代码测试:(就不跟下面的3混了,单独测试了一下)
main.cpp
#include
#include"seqQueue.h"
#include"seqStack.h"
#include"BTree.h"
int main() {
char pre[80];
char mid[80];
BTree<char> tree;
//tree.createTree('@');//@为结束符号
cout << "请输入前序序列:" << endl;
cin.getline(pre, 80);
cout << "请输入中序序列:" << endl;
cin.getline(mid, 80);
Node<char>*a = tree.buildTree(pre, 0, 5, mid, 0, 5);
cout << "高度为:" << tree.Height() << endl;
cout << "规模为:" << tree.Size() << endl;
cout << "前序排列为:";
tree.PreOrder();
cout << endl;
cout << "中序排列为:";
tree.InOrder();
cout << endl;
cout << "后序排列为:";
tree.PostOrder();
cout << endl;
cout << "层次排列为:";
tree.LevelOrder();
cout << endl;
return 0;
}
#ifndef Tree_H
#define Tree_H
#include
#include"seqQueue.h"
#include"seqStack.h"
using namespace std;
//BTree类的前向说明
template<class elemType>
class BTree;
template<class elemType>
class Node {
friend class BTree<elemType>;//声明BTree类为友元
private:
elemType data;
Node* left;
Node* right;
public:
Node() {
left = NULL;
right = NULL;
}
Node(const elemType& e, Node* L = NULL, Node* R = NULL) {
data = e;
left = L;
right = R;
}
};
template<class elemType>
class BTree {
private:
Node<elemType>* root;
//elemType stopFlag;
int Size(Node<elemType>* t);//求以t为根的二叉树的结点个数
int Height(Node<elemType>* t); //求以t为根的二叉树的高度。
void DelTree(Node<elemType>* t);//删除以t为根的二叉树
void PreOrder(Node<elemType>* t);
// 按前序遍历输出以t为根的二叉树的结点的数据值
void InOrder(Node<elemType>* t);
// 按中序遍历输出以t为根的二叉树的结点的数据值
void PostOrder(Node<elemType>* t);
// 按后序遍历输出以t为根的二叉树的结点的数据值
public:
BTree() { root = NULL; }
void createTree(const elemType& flag);//创建一棵二叉树
bool isEmpty() { return (root == NULL); }// 判断叉树是否为空
Node<elemType>* GetRoot() { return root; }
int Size(); //求二叉树的结点个数。
int Height(); //求二叉树的高度。
void DelTree();//删除二叉树
void PreOrder();// 按前序遍历输出二叉树的结点的数据值
void InOrder();// 按中序遍历输出二叉树的结点的数据值
void PostOrder();// 按后序遍历输出二叉树的结点的数据值
void LevelOrder();// 按层次遍历输出二叉树的结点的数据值
};
template<class elemType>
void BTree<elemType>::createTree(const elemType& flag) {
//因为队列元素为结点地址,即为Node*
seqQueue<Node<elemType>*> que;
elemType x;
Node<elemType>* p;
elemType chL, chR;
Node<elemType>* LP, *RP;
cout << "Please input the value of the root:" << endl;
cin >> x;
if (x == flag) return;
else root = new Node<elemType>(x);
que.enQueue(root);//root地址进队
//如果队列不为空,则出队
while (!que.isEmpty()) {
p = que.deQueue();
cout << "please input the children of " << p->data << ":" << endl;
cin >> chL >> chR;
if (chL != flag) {//该结点有左孩子
LP = new Node<elemType>(chL);//new结点
p->left = LP;//链到父亲节点
que.enQueue(LP);//当前节点地址进队
}
if (chR != flag) {//该结点有右孩子
RP = new Node<elemType>(chR);
p->right = RP;
que.enQueue(RP);
}
}
}
//求规模
template<class elemType>
int BTree<elemType>::Size() {
return Size(root);//要实现递归,就需要传入可以变化的参数
}
template<class elemType>
int BTree<elemType>::Size(Node<elemType>* t) {
if (!t) return 0;//递归终止条件 t=NULL
return 1 + Size(t->left) + Size(t->right);
}
//求高度
template <class elemType>
int BTree<elemType>::Height() {
return Height(root);
}
template <class elemType>
int BTree<elemType>::Height(Node<elemType>* t)
//得到以t为根二叉树的高度,递归算法实现。
{
int hl, hr;
if (!t) return 0;//如果t为空,递归终止
hl = Height(t->left);
hr = Height(t->right);
if (hl >= hr) return 1 + hl;
return 1 + hr;
}
//删除树
template <class elemType>
void BTree<elemType>::DelTree()
{
DelTree(root);
root = NULL;
}
template <class elemType>
void BTree<elemType>::DelTree(Node<elemType>* t)
//删除以t为根的二叉树,递归算法实现
{
if (!t) return;
DelTree(t->left); DelTree(t->right);
delete t;//删除根节点
}
/*
//前序遍历的递归实现
template
void BTree::PreOrder() {
PreOrder(root);
}
template
void BTree::PreOrder(Node* t) {
if (!t) return;
cout << t->data; //访问根节点
PreOrder(t->left);
PreOrder(t->right);
}
*/
//前序遍历的非递归实现
//利用非递归的形式,可以求得许多性质,例如规模、检查某个值是否存在等等
template<class elemType>
void BTree<elemType>::PreOrder() {
if (!root) return;
Node<elemType>* p;
seqStack<Node<elemType>*> s;
s.push(root);
while (!s.isEmpty()) {
p = s.pop();
cout << p->data;
if (p->right) s.push(p->right);
if (p->left) s.push(p->left);
}
}
/*
//中序遍历的递归实现
template
void BTree::InOrder() {
InOrder(root);
}
template
void BTree::InOrder(Node* t) {
if (!t) return;
InOrder(t->left);
cout << t->data; //访问根节点
InOrder(t->right);
}
*/
//中序遍历的非递归实现
template<class elemType>
void BTree<elemType>::InOrder() {
if (!root) return;
seqStack<Node<elemType>*> s(10);
Node<elemType>* p;
p = root;
s.push(root);
while (p->left) {//压一路左子
s.push(p->left);
p = p->left;
}
while (!s.isEmpty()) {//如果栈不空
p = s.pop();//弹出并访问
cout << p->data;
if (p->right) {//如果右子存在
s.push(p->right);
p = p->right;
while (p->left) {//如果右子有左子则一路压下去
s.push(p->left);
p = p->left;
}
}
}
}
/*
//后序遍历的递归实现
template
void BTree::PostOrder() {
PostOrder(root);
}
template
void BTree::PostOrder(Node* t) {
if (!t) return;
PostOrder(t->left);
PostOrder(t->right);
cout << t->data; //访问根节点
}
*/
/*
//后序遍历的非递归实现_01状态
template
void BTree::PostOrder() {
if(!root) return;
seqStack*> s1;
seqStack s2;
Node* p;
int state;
s1.push(root);
s2.push(0);
while (!s1.isEmpty()) {//栈不空
state = s2.pop();
if (state == 1) {
p = s1.pop();
cout << p->data;
}
else {
s2.push(1);//当前节点状态由0变为1
p = s1.Top();
if (p->right) {//如果有右子,右子压栈,且状态为0
s1.push(p->right);
s2.push(0);
}
if (p->left) {//如果有左子,左子压栈,且状态为0
s1.push(p->left);
s2.push(0);
}
}
}
}
*/
//后序遍历的非递归实现_12状态
template<class elemType>
void BTree<elemType>::PostOrder() {
if (!root) return;
seqStack<Node<elemType>*> s1;
seqStack<int> s2;
Node<elemType>* p;
int state;
s1.push(root);
s2.push(0);
while (!s1.isEmpty()) {
p = s1.Top();
state = s2.pop();
switch (state)
{
case 2:
s1.pop();
cout << p->data;
break;
case 1:
s2.push(2);
if (p->right) {
s1.push(p->right);
s2.push(0);
}
break;
case 0:
s2.push(1);
if (p->left) {
s1.push(p->left);
s2.push(0);
}
break;
default:
break;
}
}
}
//层次遍历的非递归实现
template<class elemType>
void BTree<elemType>::LevelOrder() {
seqQueue<Node<elemType>*>que(10);
Node<elemType>* p;
if (!root) return;//二叉树为空
que.enQueue(root);
while (!que.isEmpty()) {
p = que.getHead();
que.deQueue();
cout << p->data;
if (p->left) que.enQueue(p->left);
if (p->right) que.enQueue(p->right);
}
cout << endl;
}
#endif
#include
#include"seqQueue.h"
#include"seqStack.h"
#include"BTree.h"
int main() {
BTree<char> tree;
tree.createTree('@');//@为结束符号
cout << "高度为:" << tree.Height() << endl;
cout << "规模为:" << tree.Size() << endl;
cout << "前序排列为:";
tree.PreOrder();
cout << endl;
cout << "中序排列为:";
tree.InOrder();
cout << endl;
cout << "后序排列为:";
tree.PostOrder();
cout << endl;
return 0;
}
树的表示:
孩子兄弟表示法——左拉儿子,右拉兄弟
森林的表示:
树的先根遍历:
树的后根遍历
联系:
!!
树的先根遍历 = 二叉树先序遍历
树的后根遍历 = 二叉树中序遍历
森林的先序遍历:
森林的中序遍历:
注:
已知一棵树的先序遍历和后序遍历,是否能唯一确定这棵树?能!
已知一片森林的先序遍历和中序遍历,是否能唯一确定这片森林?能!
#ifndef seqStack_H
#define seqStack_H
#endif