一、定义
1、二叉树
二叉树是一个有限的结点集合,这个集合或者为空、或者由一个根节点和两棵互不相交的左、右子树组成
2、满二叉树
二叉树的所有分支结点都有左、右孩子,且叶子结点都集中再二叉树的最下一层
3、完全二叉树
若二叉树的所有分支结点只有最下面两层的结点度可以小于2,且最下面一层的叶子结点都依次排列在该层的最左边的位置上。(度为1的结点,要么为1,要么为0)
二、性质
- 非空二叉树上的叶子结点等于双分支结点数加1
- 非空二叉树的第i层上最多有个结点()
- 高度为的二叉树,最多有 个结点
- 有个()结点的完全二叉树的高度为
- 完全二叉树中,层序编号为i的结点
- ,为分支结点
为奇数时,度为1的结点数为0;否则,度为1的结点数为1
的左孩子为,右孩子为
除根右孩子节点外,结点的双亲结点为
三、二叉树与树、森林之间的转换
对于树转化为二叉树,是基于数的孩子兄弟存储法。所有的树、森林都可以转化为二叉树,逆向看,所有的二叉树均可以转换为树、森林
1、二叉树变为树、森林
窍门:将二叉树中每个结点的左孩子变为该结点的长子,将该结点的右孩子变为其长子的兄弟
2、树、森林变为二叉树
窍门:将树中每个结点的长子变为该结点的左孩子,将该结点的兄弟变为该结点的
四、存储结构
1、顺序存储
#define MaxSize 100
typedef ElemType SqBinTree[MaxSize];
2、链式存储
typedef ElemType char;
typedef struct node{
ElemType data;
struct node* lchild;
struct node* rchild;
}BTNode;
3、两种存储方式的优缺点
- 顺序存储方便查询孩子、双亲,但是对于插入、删除等操作比较麻烦,而且对于右单支树而言,将浪费大量空间
- 对于一般二叉树而言,也比较节省空间。访问孩子结点方便,但访问双亲结点难。插入、删除操作简单
五、基本运算及其实现
1、创建二叉树
void CreateBTree(BTNode** bt, char* str) {
BTNode* cur, * ins;
InitBTree(&ins);
SqStack* sq;
int dir = 0;//标识插入左子树(dir == 0),还是插入右子树(dir == 1)
InitStack(&sq);
*bt = NULL;
while (*str != '\0') {
switch (*str) {
case '(':
Push(&sq, ins);
dir = 0;
break;
case ',':
dir = 1;
break;
case ')':
Pop(&sq, &cur);
break;
default:
InitBTree(&ins);
ins->val = *str;
if (*bt == NULL)
*bt = ins;
else {
GetTop(sq, &cur);
if (dir == 0)
cur->lchild = ins;
else
cur->rchild = ins;
}
break;
}
str++;
}
}
2、销毁二叉树
void DestroyBTree(BTNode** bt) {
if ((*bt) == NULL)
return;
DestroyBTree(&((*bt)->lchild));
DestroyBTree(&((*bt)->rchild));
free(*bt);
}
3、寻找结点
BTNode* FindNode(BTNode* bt, BTElem target) {
if (bt == NULL)
return NULL;
else if (bt->val == target)
return bt;
else {
BTNode* mid;
mid = FindNode(bt->lchild, target);
if (mid != NULL)
return mid;
else
return FindNode(bt->rchild, target);
}
}
4、找孩子结点
BTNode* LchildNode(BTNode* midnode) {
return midnode->lchild;
}
BTNode* RchildNode(BTNode* midnode) {
return midnode->rchild;
}
5、求高度
int BTreeHeight(BTNode* bt) {
if (bt != NULL) {
return BTreeHeight(bt->lchild) > BTreeHeight(bt->rchild) ? (BTreeHeight(bt->lchild) + 1) : (BTreeHeight(bt->rchild) + 1);
}
else
return 0;
}
6、输出二叉树
void DisplayBTree(BTNode* bt) {
if (bt == NULL)
return;
printf("%c", bt->val);
if (bt->lchild != NULL || bt->rchild != NULL) {
printf("(");
DisplayBTree(bt->lchild);
if (bt->rchild != NULL)
printf(",");
DisplayBTree(bt->rchild);
printf(")");
}
}
7、先序,中序,后续遍历(递归)
void PreOrderByRecursion(BTNode* bt) {
if (bt != NULL) {
printf("%c\t", bt->val);
PreOrderByRecursion(bt->lchild);
PreOrderByRecursion(bt->rchild);
}
}
void InOrderByRecursion(BTNode* bt) {
if (bt != NULL) {
InOrderByRecursion(bt->lchild);
printf("%c\t", bt->val);
InOrderByRecursion(bt->rchild);
}
}
void PostOrderByRecursion(BTNode* bt) {
if (bt != NULL) {
PostOrderByRecursion(bt->lchild);
PostOrderByRecursion(bt->rchild);
printf("%c\t", bt->val);
}
}
8、先序,中序,后续遍历(非递归)
void PreOrderByStack1(BTNode* bt) {
BTNode* tmp;
SqStack* st;
InitStack(&st);
Push(&st, bt);
while (!StackEmpty(st)) {
Pop(&st, &tmp);
printf("%c\t", tmp->val);
if (tmp->rchild != NULL)
Push(&st, tmp->rchild);
if (tmp->lchild != NULL)
Push(&st, tmp->lchild);
}
printf("\n");
DestroyStack(&st);
}
void PreOrderByStack2(BTNode* bt) {
BTNode* tmp = bt;
SqStack* st;
InitStack(&st);
while (!StackEmpty(st) || tmp != NULL) {
while (tmp != NULL) {
printf("%c\t", tmp->val);
Push(&st, tmp);
tmp = tmp->lchild;
}
if (!StackEmpty(st)) {
Pop(&st, &tmp);
tmp = tmp->rchild;
}
}
printf("\n");
DestroyStack(&st);
}
void InOrderByStack(BTNode* bt) {
BTNode* tmp;
SqStack* st;
InitStack(&st);
tmp = bt;
while (!StackEmpty(st) || tmp != NULL) {
while (tmp != NULL) {
Push(&st, tmp);
tmp = tmp->lchild;
}
if (!StackEmpty(st)) {
Pop(&st, &tmp);
printf("%c\t", tmp->val);
tmp = tmp->rchild;
}
}
printf("\n");
DestroyStack(&st);
}
void PostOrderByStack(BTNode* bt) {
BTNode* tmp, * visited;
bool flag;//flag和visited的设置有什么用?
SqStack* st;
InitStack(&st);
tmp = bt;
do {
while (tmp != NULL) {//处理左子树
Push(&st, tmp);
tmp = tmp->lchild;
}
visited = NULL;
flag = true;
while (!StackEmpty(st) && flag) {//为什么要连续判断栈顶是否可访问->不这样,会重复访问一些结点,会重复访问什么结点呢?
GetTop(st, &tmp);
if (tmp->rchild == visited) {//此处,有几种情况?2种
printf("%c\t", tmp->val);
Pop(&st, &tmp);
visited = tmp;
}
else {
tmp = tmp->rchild;
flag = false;
}
}
} while (!StackEmpty(st));//为什么循环条件不是"!StackEmpty(st) || tmp != NULL"?
printf("\n");
DestroyStack(&st);
}
9、层次遍历
void LevelOrder(BTNode* bt) {
BTNode* tmp;
SqQueue qu;
qu.front = qu.rear = -1;
if (bt != NULL)
qu.data[++(qu.rear)] = bt;
while (qu.front < qu.rear) {
tmp = qu.data[++(qu.front)];
printf("%c\t", tmp->val);
if (tmp->lchild != NULL)
qu.data[++(qu.rear)] = tmp->lchild;
if (tmp->rchild != NULL)
qu.data[++(qu.rear)] = tmp->rchild;
}
printf("\n");
}