二叉树:
树是一种重要的非线性数据结构,直观地看,它是数据元素(在树中称为结点)按分支关系组织起来的结构。二叉树是
每个节点最多有两个子树的有序树。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。
二叉树常被用于实现二叉查找树和二叉堆。值得注意的是,二叉树不是树的特殊情形。在图论中,二叉树是一个连通的
无环图,并且每一个顶点的度不大于3。有根二叉树还要满足根结点的度不大于2。有了根结点后,每个顶点定义了唯一
的根结点,和最多2个子结点。然而,没有足够的信息来区分左结点和右结点。
《数据结构》面向对象这本书当中介绍了二叉树的建立:
基本形态
二叉树也是递归定义的,其结点有左右子树之分,逻辑上二叉树有五种基本形态:
(1)空二叉树——(a);
(2)只有一个根结点的二叉树——(b);
(3)只有左子树——(c);
(4)只有右子树——(d);
(5)完全二叉树——(e)
注意:尽管二叉树与树有许多相似之处,但二叉树不是树的特殊情形。
重要概念
(1)完全二叉树——若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。
(2)满二叉树——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。
(3)深度——二叉树的层数,就是高度。
性质
(1) 在二叉树中,第i层的结点总数不超过2^(i-1);
(2) 深度为h的二叉树最多有2^h-1个结点(h>=1),最少有h个结点;
(3) 对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1;
(4) 具有n个结点的完全二叉树的深度为int(log2n)+1
(5)有N个结点的完全二叉树各结点如果用顺序方式存储,则结点之间有如下关系:
若I为结点编号则 如果I>1,则其父结点的编号为I/2;
如果2*I<=N,则其左儿子(即左子树的根结点)的编号为2*I;若2*I>N,则无左儿子;
如果2*I+1<=N,则其右儿子的结点编号为2*I+1;若2*I+1>N,则无右儿子。
(6)给定N个节点,能构成h(N)种不同的二叉树。
h(N)为卡特兰数的第N项。h(n)=C(n,2*n)/(n+1)。
(7)设有i个枝点,I为所有枝点的道路长度总和,J为叶的道路长度总和J=I+2i
存储结构
(1)顺序存储方式
(2)链表存储方式
类的定义:
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <map>
#include <vector>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
char A[2050];
int pos;
typedef char T;
int ans;
struct BinTreeNode
{
T data;
BinTreeNode *leftChild ,*rightChild;
BinTreeNode():leftChild(NULL), rightChild(NULL) {}
BinTreeNode(T x, BinTreeNode *l = NULL , BinTreeNode *r= NULL):data(x), leftChild(l),rightChild(r) {}
};
class BinaryTree
{
public:
BinaryTree();
void InOrder(BinTreeNode *p);
void PostOrder(BinTreeNode *p);
void LevelOrder(BinTreeNode *p);
void Increate(BinTreeNode *&t, char p[]);
private:
BinTreeNode *root;
};
BinaryTree::BinaryTree()
{
root=NULL;
}
void BinaryTree::InOrder(BinTreeNode *subTree)
{
if(subTree != NULL)
{
InOrder(subTree -> leftChild);
cout << subTree -> data;
InOrder(subTree -> rightChild);
}
else
{
}
}
void BinaryTree::PostOrder(BinTreeNode *subTree)
{
if(subTree != NULL)
{
PostOrder(subTree -> leftChild);
PostOrder(subTree -> rightChild);
cout << subTree -> data;
}
else
{
}
}
void BinaryTree::LevelOrder(BinTreeNode *root)
{
queue<BinTreeNode *>Q;
BinTreeNode *p = root;
Q.push(p);
while(!Q.empty())
{
p = Q.front();
Q.pop();
cout <<p->data;
if(p -> leftChild != NULL)
{
Q.push(p->leftChild);
}
if(p -> rightChild != NULL)
{
Q.push(p->rightChild);
}
}
}
void BinaryTree::Increate(BinTreeNode *&t,char p[])
{
char item;
item = p[pos];
if(item=='#')
{
t = NULL;
pos++;
}
else
{
t = new BinTreeNode();
t -> data = item;
pos++;
Increate(t->leftChild,p);
Increate(t->rightChild,p);
}
}
int main()
{
while(scanf("%s",A) != EOF)
{
BinaryTree TT; //指向二叉树根结点的指针
BinTreeNode *bt;
pos=0;
TT.Increate(bt,A);
if(bt!=NULL)
TT.InOrder(bt);
cout <<' ';
if(bt!=NULL)
TT.PostOrder(bt);
cout <<' ';
if(bt!=NULL)
TT.LevelOrder(bt);
cout << endl;
memset(A,0,sizeof(A));
}
return 0;
}
上面的类就主要写了二叉树的遍历和使用其先序结果建立二叉树,在《数据结构》上面还有详细的介绍关于二叉树的其它性质及建立方法等等。
下面是一个霍夫曼树的类,可以用来求最短编码等:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//Huffman树的存储结构
#define n 5 //叶子数目
#define m 2*n-1 //树中结点总数
typedef struct //结点类型
{ float weight; //权值,不妨设权值均大于零
int lchild,rchild,parent; //左右孩子及双亲指针
}HTNode;
typedef HTNode HuffmanTree[m]; //HuffmanTree是向量类型
typedef struct
{ char ch; //存储字符
char bits[n+1]; //存放编码位串
}CodeNode;
typedef CodeNode HuffmanCode[n];
void InitHuffmanTree(HuffmanTree T); //初始化Huffman树
void InputWeight(HuffmanTree T); //输入权值
void SelectMin(HuffmanTree T,int i,int *p1,int *p2);
void main()
{
void CreateHuffmanTree(HuffmanTree T); //构造Huffman树
void CharSetHuffmanEncoding(HuffmanTree T,HuffmanCode H);
HuffmanTree T;
HuffmanCode H;
CreateHuffmanTree(T);
CharSetHuffmanEncoding(T,H);
}
void CreateHuffmanTree(HuffmanTree T)
{ //构造Huffman树,T[m-1]为其根结点
int i,p1,p2;
InitHuffmanTree(T); //将T初始化
InputWeight(T); //输入叶子权值至T[0..n-1]的weight域
for(i=n;i<m;i++) //共进行n-1次合并,新结点依次存于T[i]中
{ SelectMin(T,i-1,&p1,&p2);
//在T[0..i-1]中选择两个权最小的根结点,其序号分别为p1和p2
T[p1].parent=T[p2].parent=i;
T[i].lchild=p1; //最小权的根结点是新结点的左孩子
T[i].rchild=p2; //次小权的根结点是新结点的右孩子
T[i].weight=T[p1].weight+T[p2].weight;
}
}
void InitHuffmanTree(HuffmanTree T)
{ //初始化Huffman树
int i;
for (i=0;i<m;i++)
{
T[i].weight=0;
T[i].lchild=T[i].rchild=T[i].parent=NULL;
}
}
void InputWeight(HuffmanTree T)
{ //输入权值
int i;
for (i=0;i<n;i++)
{
printf("请输入第%d个权值:",i+1);
scanf("%f",&T[i].weight);
}
}
void SelectMin(HuffmanTree T,int i,int *p1,int *p2)
{ //在T中选择两个权最小的根结点
int j;
float min1,min2;
min1=min2=-1;
for(j=0;j<=i;j++)
if(T[j].parent==NULL)
{
if(T[j].weight<min1||min1==-1)
{
if(min1!=-1)
{
min2=min1;
*p2=*p1;
}
min1=T[j].weight;
*p1=j;
}
else
if(T[j].weight<min2||min2==-1)
{
min2=T[j].weight;
*p2=j;
}
}
}
void CharSetHuffmanEncoding(HuffmanTree T,HuffmanCode H)
{ //根据Huffman树T求Huffman编码表H
int c,p,i; //c和p分别指示T中孩子和双亲的位置
char cd[n+1]; //临时存放编码
int start; //指示编码在cd中的起始位置
cd[n]='\0'; //编码结束符
printf("请输入字符:");
for(i=0;i<n;i++) //依次求叶子T[i]的编码
{
H[i].ch=getchar(); //读入叶子T[i]对应的字符
start=n; //编码起始位置的初值
c=i; //从叶子T[i]开始上溯
while((p=T[c].parent)!=NULL)//直至上溯到T[c]是树根为止
{ //若T[c]是T[p]的左孩子,则生成代码0;否则生成代码1
cd[--start]=(T[p].lchild==c)?'0':'1';
c=p; //继续上溯
}
strcpy(H[i].bits,&cd[start]); //复制编码位串
}
for(i=0;i<n;i++)
printf("第%d个字符%c的编码为%s\n",i+1,H[i].ch,H[i].bits);
}
如何判断一棵树是不是完全二叉树:
判断二叉树是否是完全二叉树(用队列)
int IsFull_Bitree(Bitree T)//是完全二叉树返回1,否则返0
{ InitQueue(Q); //建队(初始化队列)
flag=0; //标志初始化
EnQueue(Q,T); //结点T入队(空指针也要入队)
while(!QueueEmpty(Q))
{ DeQueue(Q,&p); //队首结点出队(送入p)
if(!p) flag=1; //队首结点为空则标志变,但也许是队尾?
else if(flag)return 0; //下一轮才能确定是不是完全二叉树
else
{ EnQueue(Q,p->lchild); //不管孩子是否为空,都入队列
EnQueue(Q,p->rchild);
}
}//while
return 1; //执行至此必为队空且中间无空指针,完全二叉
}//IsFull_Bitree
如何交换二叉树的左右子树:
交换所有结点的左右子树
void Bitree_Revolute(Bitree T)//交换所有结点的左右子树
{
T->lchild<->T->rchild; //交换左右子树
if(T->lchild) Bitree_Revolute(T->lchild);
if(T->rchild) Bitree_Revolute(T->rchild); //左右子树再分别交换各自的左右子树
}//Bitree_Revolute
实现非递归复制二叉树:
非递归复制二叉树
void Bitree_Copy_Nonrecursive(Bitree T,Bitree &U)//非递归复制二叉树
{
InitStack(S1);InitStack(S2);
push(S1,T); //根指针进栈
U=(BTNode*)malloc(sizeof(BTNode));
U->data=T->data;
q=U;push(S2,U);
while(!StackEmpty(S))
{
while(Gettop(S1,p)&&p)
{
q->lchild=(BTNode*)malloc(sizeof(BTNode));
q=q->lchild;q->data=p->data;
push(S1,p->lchild);
push(S2,q);
} //向左走到尽头