认识树
- 树的结构
树的部分基本定义
- 节点:树中的一个独立单元,包含一个数据元素以及若干指向其他子树的分支
- 节点的度:结点拥有的子树数量成为结点的度
- 树的度:树内根结点度的最大值
- 叶子:度为零的结点称为叶子或终端结点
- 层次:结点的层次从根开始定义,根为第一层,根的孩子为第二层,树中任一层次等于其双亲结点的层次+1
- 节点的高度:节点到叶子节点的最大路径(边数)
- 节点的深度:根节点到这个节点的所经历的边的个数
- 节点的层数:节点的深度-1
- 树的高度:根节点的高度
二叉树
二叉树相关定义与特性
- 定义:有且仅有一个根节点,除了根节点以外,其余节点分为两个互不相交的子集T1、T2,分别称为树T的左子树、右子树,且T1、T2都是二叉树
二叉树的特性:
- 二叉树每个节点最多有两个子树(二叉树中不存在度大于2的节点),所以二叉树中节点的度不大于2
- 二叉树的子树有左右之分,次序不能颠倒
- 即使只有一棵树也需要区分左子树还是右子树
- 二叉树的五中形态:
满二叉树:除了叶子节点外,每个节点均有左右两个子树
- 完全二叉树:对⼀颗具有n个结点的⼆叉树按层序编号,如果编号为i(1=< i <= n)的结点与同样深度的满⼆叉树中编号为i的结点⼆叉树中位置完全相同. 则这颗⼆叉树称为完全⼆叉树.
- 满二叉树与完全二叉树的区别
- ⾸先"完全" 和 "满" 的差异, 满⼆叉树⼀定是⼀个完全⼆叉树不⼀定是满的.
- 完全⼆叉树的所有结点和同样深度的满⼆叉树,它们按照层序编号相同的结点⼀⼀对应. 这⾥有⼀个关键词是按层序编号
- 完全二叉树的特性
- 叶⼦结点只能出现在最下两层
- 最下层的叶⼦⼀定集中在左部连接
- 倒数第⼆层,若有叶⼦节点, ⼀定都在右部连续位置
- 如果结点度为1, 则该结点只有左孩⼦, 既不存在只有右⼦树的情况
- 同样结点数的⼆叉树, 完全⼆叉树的深度最⼩;
- 二叉树的性质:
二叉树的顺序存储
二叉树按照结点编号依次存入数组中即可,结点不存在时,数组对应节点下标存储为空,缺点是浪费存储空间
二叉树的遍历
⼆叉树的遍(Traversing binary tree) 是指的从根结点出发,按照某种次序依次访问
⼆叉树中所有结点,使得每个结点被访问⼀次且仅被访问⼀次.
- 前序遍历:若⼆叉树为空,则空操作返回; 否则先访问根结点,然后前序遍历左⼦树,在前序遍历右⼦树
#pragma mark - 前序遍历(root->left->right) void PreTraversal(SqBiTree T, int index){ if (EmptyBiTree(T)) { printf("空树\n"); }else{ printf("%3d",T[index]); //先遍历左子树 if (T[2*index + 1] != Nil) { PreTraversal(T, 2*index + 1); } //再遍历右子树 if (T[2*index + 2] != Nil) { PreTraversal(T, 2*index + 2); } } } - 中序遍历:若⼆叉树为空,则空操作返回; 否则从根结点开始(注意并不是先访问根结点),中序遍历根结点的左⼦树,然后是访问根结点,最后中序遍历右⼦树.
#pragma mark - 中序遍历(left->root->right) void MidTraversal(SqBiTree T, int index){ if (EmptyBiTree(T)) { printf("空树\n"); }else{ //先遍历左子树 if (T[2*index + 1] != Nil) { MidTraversal(T, 2*index + 1); } printf("%3d",T[index]); //再遍历右子树 if (T[2*index + 2] != Nil) { MidTraversal(T, 2*index + 2); } } } - 后序遍历:若⼆叉树为空,则空操作返回; 否则从左到右先叶⼦后结点的⽅式遍历左右⼦树,最后访问根结点
#pragma mark - 后序遍历(left->right->root) void AfterTraversal(SqBiTree T, int index){ if (EmptyBiTree(T)) { printf("空树\n"); }else{ //先遍历左子树 if (T[2*index + 1] != Nil) { AfterTraversal(T, 2*index + 1); } //再遍历右子树 if (T[2*index + 2] != Nil) { AfterTraversal(T, 2*index + 2); } printf("%3d",T[index]); } } - 层序遍历:就是按照二叉树的排序进行遍历
#pragma mark - 按序号遍历 void LevelOrderTraversal(SqBiTree T){ if (EmptyBiTree(T)) { printf("\n空树\n"); }else{ int i = 0; while (T[i] != Nil) { printf("%3d",T[i]); i++; } } }
二叉树顺序存储完整代码
#include <stdio.h>
#include "stdlib.h"#include "math.h"
#include "time.h"#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100 /* 存储空间初始分配量 */
#define MAX_TREE_SIZE 100 /* 二叉树的最大结点数 */
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int CElemType; /* 树结点的数据类型,目前暂定为整型 */
typedef CElemType SqBiTree[MAX_TREE_SIZE]; /* 0号单元存储根结点 */
CElemType Nil = 0; /*设整型以0为空 或者以 INT_MAX(65535)*/
#pragma mark 节点位置信息
typedef struct {
//节点层
int level;
//本层的序号(按照满二叉树的顺序)
int order;
}Position;
#pragma mark - 初始化二叉树
Status InitBiTree(SqBiTree T){
for (int i = 0; i<MAXSIZE; i++) {
//初始化二叉树只需把指定位置置空即可
T[i] = Nil;
}
return OK;
}
#pragma mark - 打印节点值
void Visit(CElemType elem){
printf("%2d",elem);
}
#pragma mark - 判断是否是空树
Status EmptyBiTree(SqBiTree T){
if (T[0] == Nil) {
return TRUE;
}
return FALSE;
}
#pragma mark - 按照层序次序输入二叉树的值,构造顺序存储二叉树
Status CreatBiTree(SqBiTree T){
int i = 0;
//本次只设置10个节点
while (i<10) {
T[i] = i + 1;
printf("%3d",T[i]);
//结点不为空,且无双亲结点的异常处理,其中T[(i+1)/2-1]是双亲结点
if (i != 0 && T[(i+1)/2-1] == Nil && T[i] != Nil) {
printf("出现无双亲的非根结点%d\n",T[i]);
exit(ERROR);
}
i++;
}
//将数组T10个节点后的值设置成Nil
while (i<MAXSIZE) {
T[i] = Nil;
i++;
}
return OK;
}
#pragma mark - 清空二叉树(相当于初始化二叉树)
#define ClearBiTree(x) InitBiTree(x)
#pragma mark - 获取二叉树的深度
int GetBiTreeDepth(SqBiTree T){
if (EmptyBiTree(T)) {
return -1;
}
//定义深度
int depth = -1;
int i = MAXSIZE - 1;
//先找到最后的结点
while (T[i] == Nil && i>=0) {
i--;
}
do {
depth++;
} while (pow(2, depth) <= i);//求depth的2次幂,当depth的2次幂大于i时等到了深度,结束循环
return depth;
}
#pragma mark - 获得处于e位置(层、序号)的节点值
void GetCelemType(SqBiTree T, Position e){
if (EmptyBiTree(T)) {
printf("空树");
return;
}
//先找层序,再找在本层中序号
CElemType levelNum = pow(2, e.level - 1);//level从1开始,所以要减1
printf("第%d层第%d个结点值为:%d\n",e.level,e.order,T[levelNum + e.order - 2]);
}
#pragma mark - 获取二叉树的根节点
Status Root(SqBiTree T,CElemType *e){
if (EmptyBiTree(T)) {
return ERROR;
}
printf("\n根节点是:%d\n",T[0]);
*e = T[0];
return OK;
}
#pragma mark - 给处于e结点位置的结点赋值
Status SetValue(SqBiTree T, Position e, CElemType type){
//先找到e位置的结点
int index = pow(2, e.level - 1) + e.order - 2;
if (T[(index + 1)/2 - 1] == Nil && type != Nil) {//双亲结点不存在
return ERROR;
}
if (type == Nil && (T[2*index + 1] != Nil || T[2*index + 2] != Nil)) {//赋值的结点有叶子节点并且该节点赋值为空,则直接返回
return ERROR;
}
T[index] = type;
return OK;
}
#pragma mark - 获取结点的双亲
CElemType GetParent(SqBiTree T, Position e){
if (EmptyBiTree(T)) {
return Nil;
}
//获得该节点的值
int index = pow(2, e.level - 1) + e.order - 2;
CElemType type = T[index];
for (int i = 0; i<MAXSIZE; i++) {
if (T[i] == type) {
return T[(i+1)/2 - 1];
}
}
return Nil;
}
#pragma mark - 获取结点的左孩子
CElemType GetLeftChird(SqBiTree T, Position e){
if (EmptyBiTree(T)) {
return Nil;
}
//获得该节点的值
int index = pow(2, e.level - 1) + e.order - 2;
CElemType type = T[index];
for (int i = 0; i<MAXSIZE-1; i++) {
if (T[i] == type) {
return T[2*i + 1];
}
}
return Nil;
}
#pragma mark - 获取结点的右孩子
CElemType GetRightChird(SqBiTree T, Position e){
if (EmptyBiTree(T)) {
return Nil;
}
//获得该节点的值
int index = pow(2, e.level - 1) + e.order - 2;
CElemType type = T[index];
for (int i = 0; i<MAXSIZE-1; i++) {
if (T[i] == type) {
return T[2*i + 2];
}
}
return Nil;
}
#pragma mark - 获取结点的左兄弟
CElemType GetLeftBrother(SqBiTree T, Position e){
if (EmptyBiTree(T)) {
return Nil;
}
//获得该节点的值
int index = pow(2, e.level - 1) + e.order - 2;
CElemType type = T[index];
for (int i = 0; i<MAXSIZE-1; i++) {
if (T[i] == type && i%2 == 0) {//相等并且是偶数才会有左兄弟,该节点是右节点才有左兄弟
return T[i - 1];
}
}
return Nil;
}
#pragma mark - 获取结点的右兄弟
CElemType GetRightBrother(SqBiTree T, Position e){
if (EmptyBiTree(T)) {
return Nil;
}
//获得该节点的值
int index = pow(2, e.level - 1) + e.order - 2;
CElemType type = T[index];
for (int i = 0; i<MAXSIZE-1; i++) {
if (T[i] == type && i%2 == 1) {//相等并且是奇数才会有右兄弟,该节点是左节点才有右兄弟
return T[i + 1];
}
}
return Nil;
}
#pragma mark - 二叉树的遍历
#pragma mark - 按序号遍历
void LevelOrderTraversal(SqBiTree T){
if (EmptyBiTree(T)) {
printf("\n空树\n");
}else{
int i = 0;
while (T[i] != Nil) {
printf("%3d",T[i]);
i++;
}
}
}
#pragma mark - 前序遍历(root->left->right)
void PreTraversal(SqBiTree T, int index){
if (EmptyBiTree(T)) {
printf("空树\n");
}else{
printf("%3d",T[index]);
//先遍历左子树
if (T[2*index + 1] != Nil) {
PreTraversal(T, 2*index + 1);
}
//再遍历右子树
if (T[2*index + 2] != Nil) {
PreTraversal(T, 2*index + 2);
}
}
}
#pragma mark - 中序遍历(left->root->right)
void MidTraversal(SqBiTree T, int index){
if (EmptyBiTree(T)) {
printf("空树\n");
}else{
//先遍历左子树
if (T[2*index + 1] != Nil) {
MidTraversal(T, 2*index + 1);
}
printf("%3d",T[index]);
//再遍历右子树
if (T[2*index + 2] != Nil) {
MidTraversal(T, 2*index + 2);
}
}
}
#pragma mark - 后序遍历(left->right->root)
void AfterTraversal(SqBiTree T, int index){
if (EmptyBiTree(T)) {
printf("空树\n");
}else{
//先遍历左子树
if (T[2*index + 1] != Nil) {
AfterTraversal(T, 2*index + 1);
}
//再遍历右子树
if (T[2*index + 2] != Nil) {
AfterTraversal(T, 2*index + 2);
}
printf("%3d",T[index]);
}
}
int main(int argc, const char * argv[]) {
// insert code here...
printf("Hello, World!\n");
Status iStatus;
Position p;
CElemType e;
SqBiTree T;
InitBiTree(T);
CreatBiTree(T);
printf("建立二叉树后,树空否?%d(1:是 0:否) \n",EmptyBiTree(T));
printf("树的深度=%d\n",GetBiTreeDepth(T));
p.level=3;
p.order=2;
GetCelemType(T, p);
iStatus = Root(T, &e);
if (iStatus) {
printf("二叉树的根为:%d\n",e);
}else
{
printf("树为空,无根!\n");
}
//向树中3层第2个结点位置上结点赋值99
e = 99;
SetValue(T, p, e);
//获取树中3层第2个结点位置结点的值是多少:
GetCelemType(T, p);
//找到e这个结点的双亲;
int index = pow(2, p.level - 1) + p.order - 2;
printf("结点%d的双亲为%d_",T[index],GetParent(T, p));
//找到p这个结点的左右孩子;
printf("左右孩子分别为:%d,%d\n",GetLeftChird(T, p),GetRightChird(T, p));
//找到e这个结点的左右兄弟;
printf("结点%d的左右兄弟:%d,%d\n",e,GetLeftBrother(T, p),GetRightBrother(T, p));
SetValue(T, p, 5);
printf("\n二叉树T层序遍历:");
LevelOrderTraversal(T);
printf("\n二叉树T先序遍历:");
PreTraversal(T, 0);
printf("\n二叉树T中序遍历:");
MidTraversal(T, 0);
printf("\n二叉树T后序遍历:");
AfterTraversal(T, 0);
printf("\n");
return 0;
}
二叉树的链式存储
使用链表存储二叉树,可以有效减少二叉树顺序存储造成的空间浪费问题
#include <stdio.h>
#include "stdlib.h"
#include "math.h"
#include "time.h"
#include "string.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100 /* 存储空间初始分配量 */
#define MAX_TREE_SIZE 100 /* 二叉树的最大结点数 */
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef char CElemType; /* 树结点的数据类型,目前暂定为char类型 */
typedef char String[24]; /* 0号单元存放串的长度 */
String str;
Status StrAssign(String T,char *chars)
{
int i;
if(strlen(chars)>MAXSIZE)
return ERROR;
else
{
T[0]=strlen(chars);
for(i=1;i<=T[0];i++)
T[i]=*(chars+i-1);
return OK;
}
}
#pragma mark - 二叉树相关
int indexs = 1;
CElemType Nil = '\0';//'\0'为空
typedef struct BiNode{
CElemType data;
struct BiNode *leftChild,*rightChild;
}BiNode,*BiTree;
#pragma mark - 构造二叉树
Status InitBiTree(BiTree *T){
*T = NULL;
return OK;
}
#pragma mark - 销毁二叉树
Status DestroyBiTree(BiTree *T){
if (*T) {
if ((*T)->leftChild) {
DestroyBiTree(&(*T)->leftChild);
}
if ((*T)->rightChild) {
DestroyBiTree(&(*T)->rightChild);
}
free(*T);
*T = NULL;
}
return OK;
}
#define ClearBiTree DestroyBiTree
#pragma mark - 创建二叉树
void CreatBiTree(BiTree *T){
CElemType data;
data = str[indexs++];
if (data == '#') {
*T = NULL;
}else{
*T = (BiTree)malloc(sizeof(BiNode));
if (!*T) {
exit(ERROR);
}
(*T)->data = data;
//生成左子树
CreatBiTree(&(*T)->leftChild);
//生成右子树
CreatBiTree(&(*T)->rightChild);
}
}
#pragma mark - 是否是空二叉树
Status EmptyBiTree(BiTree T) {
if(T)
return FALSE;
else
return TRUE;
}
#pragma mark - 获得二叉树的深度
int GetDepthBiTree(BiTree T){
if (!T) {
return 0;
}
int leftDepth,rightDepth;
if (T->leftChild) {//获取左子树的深度
leftDepth = GetDepthBiTree(T->leftChild);
}else{
leftDepth = 0;
}
if (T->rightChild) {//获得右子树的深度
rightDepth = GetDepthBiTree(T->rightChild);
}else{
rightDepth = 0;
}
return leftDepth>rightDepth ? leftDepth + 1 : rightDepth + 1;
}
#pragma mark - 获取二叉树的根
CElemType GetRootBiTree(BiTree T){
if (!T) {
return Nil;
}
return T->data;
}
#pragma mark - 返回p所指向的结点
CElemType GetValueBiTree(BiTree P){
if (!P) {
return Nil;
}
return P->data;
}
#pragma mark - 给p结点赋值
void SetValueBiTree(BiTree p, CElemType data){
if (p) {
p->data = data;
}
}
#pragma mark - 遍历二叉树
#pragma mark - 前序遍历(root->left->right)
void PreTraversalBiTree(BiTree T){
if (T) {
printf("%c ",T->data);
//遍历左子树
if (T->leftChild) {
PreTraversalBiTree(T->leftChild);
}
//遍历右子树
if (T->rightChild) {
PreTraversalBiTree(T->rightChild);
}
}
}
#pragma mark - 中序遍历(left->root->right)
void MidTraversalBiTree(BiTree T){
if (T) {
//遍历左子树
if (T->leftChild) {
MidTraversalBiTree(T->leftChild);
}
printf("%c ",T->data);
//遍历右子树
if (T->rightChild) {
MidTraversalBiTree(T->rightChild);
}
}
}
#pragma mark - 后序遍历(left->right->root)
void AfterTraversalBiTree(BiTree T){
if (T) {
//遍历左子树
if (T->leftChild) {
AfterTraversalBiTree(T->leftChild);
}
//遍历右子树
if (T->rightChild) {
AfterTraversalBiTree(T->rightChild);
}
printf("%c ",T->data);
}
}
int main(int argc, const char * argv[]) {
// insert code here...
printf("Hello, World!\n");
BiTree T;
CElemType e1;
InitBiTree(&T);
StrAssign(str,"ABDH#K###E##CFI###G#J##");
CreatBiTree(&T);
printf("二叉树是否为空%d(1:是 0:否),树的深度=%d\n",EmptyBiTree(T),GetDepthBiTree(T));
e1= GetRootBiTree(T);
printf("二叉树的根为: %c\n",e1);
printf("\n前序遍历二叉树:");
PreTraversalBiTree(T);
printf("\n中序遍历二叉树:");
MidTraversalBiTree(T);
printf("\n后序遍历二叉树:");
AfterTraversalBiTree(T);
printf("\n");
return 0;
}