树的类型有很多,这里我们只讲二叉树。

一、二叉树的基本概念

1、什么是二叉树

在计算机科中,二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”和“右子树”,左子树和右子树同时也是二叉树。二叉树的子树有左右之分,并且次序不能任意颠倒。其中,起始的节点叫做根节点,整棵树只有一个根节点除了根节点之外的每个节点有且仅有一个父节点;其中没有任何子节点的节点叫做叶子节点,叶子节点有父节点但是没有子节点;除了根节点和叶子节点之外,剩下的节点叫做枝节点,枝节点有父节点也有子节点。
如果该二叉树中每层节点数均达到最大值,且所有的枝节点都有两个子节点,那么该二叉树就叫做满二叉树。
如果除了最后一层之外,各层节点数均达到最大值,并且最后一层的节点都连续集中在左边,则叫做完全二叉树。
下面使用 ProcessOn 画的图形
(1)二叉树的一般形式:
根节点、枝节点和叶节点
父节点和子节点
左子节点和右子节点
左子树和右子树
大小和高度(深度)
(2)满二叉树
每层节点数均达到最大值,所有枝节点均有左右子树
(3)完全二叉树
除最下层外,各层节点数均达到最大值,最下层的节点都连续集中在左边。

二、基本特征

二叉树具有递归嵌套式的空间结构特征,因此采用递归的方法处理二叉树问题,可以使得算法变得更加简洁。
递归我们之前有总结过的,参看:数据结构与算法 -- 再论递归
处理的一般形式:
处理(二叉树){if(二叉树是否为空,如果为空) { 直接处理;}else{处理根节点;处理左子树; => 使用递归的方法处理 小二叉树处理右子树; => 使用递归的方法处理 小二叉树}}    

三、二叉树的存储结构

(1)采用顺序结构进行存储
一般情况下,从上到下、从左到右,依次存放所有的节点,对于非完全二叉树来说,需要采用虚节点补成完全二叉树。


代码实现说明:
创建二叉树,将字符放到数组下标,首元素存储字符个数。左子树插入根节点下标的2n,右子树插入根节点下标的2n+1,然后使用递归。前/中/后遍历,对应的DLR/LDR/LRD。
示例一:
参看:顺序存储二叉树
#include <stdio.h>  #define MAX_NODE_SIZE 100         //二叉树的最大节点数
#define SqBiTree_TYPE char
SqBiTree_TYPE t [MAX_NODE_SIZE+1];        //0号单元节点个数  //创建二叉树
void creat_tree(void)
{  int i=0;  char ch;  while((ch=getchar())!='#')  {  i++;
//      printf ("i = %d\n", i);t[i]=ch;  }  t[0]=i;
}  //获取给定结点(位置)的左孩子的结点位置
int LeftChild_locate(int node)
{  if ((2 * node) > t[0])  return -1;  else   return 2 * node;
}
//获取给定结点(位置)的右孩子的结点位置
int RightChild_locate(int node)
{  if ((2 * node+1) > t[0])  return -1;  else   return 2 * node+1;
}  //层序遍历
void level_order(void)
{  int i = 0;for(i=1;i<=t[0];i++)  if(t[i]!='#')  printf ("%c ", t[i]);
}
//前序遍历
void pre_order(int i)
{  if(t[0]<=0)  printf ("空树!\n");else  {  if(t[i]!='#')  printf ("%c ", t[i]);if(LeftChild_locate(i)!=-1)    //如果左子结点存在,递归  pre_order(LeftChild_locate(i));  if(RightChild_locate(i)!=-1)    //如果右子结点存在,递归  pre_order(RightChild_locate(i));  }
}
//中序遍历
void mid_order(int i)
{  if(t[0]<=0)  printf ("空树!\n");else  {  if(LeftChild_locate(i)!=-1)    //如果左子结点存在,递归  mid_order(LeftChild_locate(i));  if(t[i]!='#')  printf ("%c ", t[i]);if(RightChild_locate(i)!=-1)    //如果右子结点存在,递归  mid_order(RightChild_locate(i));  }
}//后序遍历
void back_order(int i)
{  if(t[0]<=0)  printf ("空树!\n");else  {  if(LeftChild_locate(i)!=-1)    //如果左子结点存在,递归  back_order(LeftChild_locate(i));  if(RightChild_locate(i)!=-1)    //如果右子结点存在,递归  back_order(RightChild_locate(i));  if(t[i]!='#')  printf ("%c ", t[i]);}
}
int main (void)
{  printf ("创建二叉树\n");//创建顺序二叉树  creat_tree();  //层序遍历  printf ("层序遍历\n");level_order();   printf ("\n");//前序遍历  printf ("前序遍历\n");pre_order(1);  printf ("\n");//中序遍历  printf ("中序遍历\n");mid_order(1);  printf ("\n");//后续遍历  printf ("后序遍历\n");back_order(1);  printf ("\n");return 0;
}
输出结果:
创建二叉树
12345#
层序遍历
1 2 3 4 5
前序遍历
1 2 4 5 3
中序遍历
4 2 5 1 3
后序遍历
4 5 2 3 1 

示例二:

参看:二叉树顺序存储的实现
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100/*存储空间初始分配量*/
#define MAX_TREE_SIZE 100/*定义树的最大节点数*/typedef int status;
typedef int elemtype;
typedef struct position {int level;int order;
} position;
typedef elemtype sqbitree[MAXSIZE];
elemtype null=0;/*设0表示空*/status visit(elemtype c) {printf("%d ",c);return OK;
}status initbitree(sqbitree T) {int i=0;for(i=0; i<MAX_TREE_SIZE; i++)T[i]=null;/*初始化时将所有节点置为空*/return OK;
}/*创建二叉树*/
status createbitree(sqbitree T) {int i=0;printf("请按层序输入结点的值(整型),0表示空结点,输999结束。结点数≤%d:\n",MAX_TREE_SIZE);while(i<10) {T[i]=i+1;/*T[(i+1)/2-1]是T[i]的双亲节点*/ if(i&&!T[(i+1)/2-1]&&T[i]) {printf("产生了没有双亲节点的新节点\n");return ERROR;}i++;}/*其余节点置空*/ while(i<MAX_TREE_SIZE) {T[i]=null;i++;}return OK;
}/*判断二叉树为空*/
status emptybitree(sqbitree T) {if(!T[0])return TRUE;elsereturn FALSE;
}/*求二叉树深度*/
status depthbitree(sqbitree T) {int i,j=-1;/*从最后一个节点开始,找到第一个不为空的节点*/for(i=MAX_TREE_SIZE-1; i>=0; i--)if(T[i])break;i++;do {j++;} while(i>=pow(2,j));return j;
}/*返回二叉树的根节点*/
status root(sqbitree T,elemtype *e) {if(emptybitree(T)) {printf("二叉树为空\n");return ERROR;} else {*e=T[0];return OK;}
}/*给二叉树中第e层序对节点赋新值value*/
status refresh(sqbitree T,position e,elemtype value) {/*将e转换成数组下标,这个公式画出示意图很容易就能理解*/int i=(int)(pow(2,e.level-1)+e.order-2);/*情况一:给叶子赋空但双亲为空,返回ERROR*/if(!value&&!T[(i+1)/2-1])return ERROR;/*情况二:给双亲赋空但叶子不为空,返回ERROR*/if(!value&&T[i*2+1]||T[i*2+2])return ERROR;T[i]=value;return OK;
}/*返回一个节点的左孩子*/
elemtype leftchild(sqbitree T,elemtype e){int i=0;if(!T[0])return ERROR;for(i=0;i<MAX_TREE_SIZE;i++){if(T[i]==e)return T[i*2+1];}return ERROR;
}/*返回一个节点的右孩子*/
elemtype rightchild(sqbitree T,elemtype e) {int i=0;if(!T[0]) {printf("空树\n");return null;}for(i=0; i<MAX_TREE_SIZE; i++) {if(T[i]==e)return T[i*2+2];}return ERROR;/*没找到*/
}/*返回一个节点的左兄弟,若e是T的左孩子或无左兄弟,则返回"空"*/
elemtype leftslibing(sqbitree T,elemtype e) {int i=0;if(!T[0]) {printf("空树\n");return null;}for(i=0; i<MAX_TREE_SIZE; i++)if(T[i]==e&&i%2==0)/*i为偶数,*则为右子孙,减一即可得到左兄弟*//*此外,这个地方写i%2==0h和!i%2的结果不一样,正在思考原因*/ return T[i-1];return null;
}/*回一个节点的右兄弟,若e是T的左孩子或无左兄弟,则返回"空"*/
elemtype rightslibing(sqbitree T,elemtype e) {int i=0;if(!T[0]) {printf("空树\n");return null;}for(i=0; i<MAX_TREE_SIZE; i++) {if(T[i]==e&&i%2)/*i为奇数,*则为左子孙,减一即可得到右兄弟*/return T[i+1];}return null;
}/*返回双亲*/
elemtype parent(sqbitree T,elemtype e){int i=0;for(i=0;i<MAX_TREE_SIZE;i++){if(T[i]==e)return T[(i+1)/2-1];}
}/*遍历二叉树*//*前序遍历*/
status preorder(sqbitree T,int i){visit(T[i]);/*先访问根节点,再递归访问左子树和右子树*/ if(T[2*i+1])preorder(T,2*i+1);if(T[2*i+2])preorder(T,2*i+2);
}status pretraverse(sqbitree T){if(emptybitree(T))return ERROR;else{preorder(T,0);/*从0开始递归*/ printf("\n");}return OK;
}/*中序遍历二叉树*/
void InTraverse(sqbitree T,int e)
{ if(T[2*e+1]!=null) /* 左子树不空 */InTraverse(T,2*e+1);visit(T[e]);if(T[2*e+2]!=null) /* 右子树不空 */InTraverse(T,2*e+2);
}status InOrderTraverse(sqbitree T)
{ if(!emptybitree(T)) /* 树不空 */InTraverse(T,0);printf("\n");return OK;
}/*后序遍历二叉树*/
status lastorder(sqbitree T,int i){if(T[2*i+1])lastorder(T,2*i+1);if(T[2*i+2])lastorder(T,2*i+2);visit(T[i]);
} status lasttraverse(sqbitree T){if(emptybitree(T))return ERROR;else{lastorder(T,0);}printf("\n");
}int main(void) {status i;position p;elemtype e;sqbitree T;initbitree(T);createbitree(T);printf("建立二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n",emptybitree(T),depthbitree(T));printf("对二叉树做前序遍历:");pretraverse(T); printf("对二叉树做中序遍历:");InOrderTraverse(T);printf("对二叉树做后序遍历:"); lasttraverse(T);e=50;printf("修改层序为3,本层序号为2的节点的值,输入新值为%d\n",e);p.level=3;p.order=2;refresh(T,p,50);printf("对二叉树做前序遍历:");pretraverse(T);printf("节点50的双亲为%d,左右孩子分别为%d %d,左右兄弟为%d %d\n",parent(T,e),leftchild(T,e),rightchild(T,e),leftslibing(T,e),rightslibing(T,e));initbitree(T);printf("清空二叉树后,树空否?%d(1:是 0:否) 树的深度=%d\n",emptybitree(T),depthbitree(T));root(T,&e);
}
编译:gcc test.c -lm
输出结果:
请按层序输入结点的值(整型),0表示空结点,输999结束。结点数≤100:
建立二叉树后,树空否?0(1:是 0:否) 树的深度=4
对二叉树做前序遍历:1 2 4 8 9 5 10 3 6 7
对二叉树做中序遍历:8 4 9 2 10 5 1 6 3 7
对二叉树做后序遍历:8 9 4 10 5 2 6 7 3 1
修改层序为3,本层序号为2的节点的值,输入新值为50
对二叉树做前序遍历:1 2 4 8 9 50 10 3 6 7
节点50的双亲为2,左右孩子分别为10 0,左右兄弟为4 0
清空二叉树后,树空否?1(1:是 0:否) 树的深度=0
二叉树为空


(2)采用链式结构进行存储
一般情况下,每个节点包括三个部分,其中一个是存储数据的内存空间,另外两个则用于存储左右子节点的地址。

二、遍历方式

(1)前序遍历(DLR - Data Left Right)
对于从根开始的每一棵子树,先处理根节点中的数据,再处理它的左子树,最后处理它的右子树,又叫做先根遍历
(2)中序遍历(LDR - Left Data Right)=> 重点掌握
对于从根开始的每一棵子树,先处理它的左子树,再处理它的根节点中数据,最后处理它的右子树,又叫做中根遍历
(3)后序遍历(LRD - Left Right Data)

对于从根开始的每一棵子树,先处理它的左子树,再处理它的右子树,最后处理它的根节点数据,又叫做后根遍历

三、有序二叉树

1、有序二叉树介绍

BST 是 Binary Search Tree 的缩写,翻译为二叉搜索树,或有序二叉树,是二叉树的一种,它的定义如下:
(1)或者是一颗空树
(2)或者是具有下列性质的二叉树:
若左子树非空,则左子树上所有节点的值均小于它的根节点的值
若右子树非空,则右子树上所有节点的值均大于它的根节点的值
左右子树也分别为有序二叉树
基于有序二叉树的排序和查找,可获得O(logN)级的平均时间复杂度。
BST 在查找一个节点或者插入一个节点时,具有极大的优势,速度非常快,是一种基础性数据结构,广泛应用于更加抽象的集合,关联数组等数据结构。

2、有序二叉树用途

(1)排序
无论以何种顺序构建有序二叉树,其中序遍历的结果一定是一个有序序列。
(2)搜索
若搜索目标与根节点的值相等,则搜索成功,否则用搜索目标和根节点的值比较大小。
若搜索目标小于根节点的值,则在根节点的左子树中继续搜索,否则在根节点的右子树中继续搜索。
以递归的方式重复以上过程,直到搜索成功,或因子树不存在而宣告失败。
基于有序二叉树的搜索,可达到对数级的平均时间复杂度。

3、链式有序二叉树实现

代码实现说明:
插入节点时,判断二叉树是否为空,如果为空就直接插入即可,如果二叉树不为空,则使用根节点与新节点比较大小,如果新节点小于根节点,则插入到左子树中,如果新节点大于根节点,则插入到右子树中。插入节点的函数为递归函数。如下图所示:
删除节点时,先将左子树合并到右子树中,将要删除的节点地址单独保存,将连接目标节点的指针指向合并出来的右子树,删除目标节点。
中序遍历,先遍历左子树、遍历根节点,遍历右子树
(1)链式有序二叉树(示例一)
//编程实现有序二叉数的基本操作
#include <stdio.h>
#include <stdlib.h>
//定义节点的数据类型
typedef struct Node
{int data;//存储的具体内容struct Node* left;//左子树的地址struct Node* right;//右子树的地址
}Node;
//定义二叉数的数据类型
typedef struct
{int cnt;//记录节点的个数Node* root;//指向跟节点的指针
}Tree;
//插入新节点后,组成有序二叉数
void insertData(Tree* pt,int data);
//创建新节点
Node* create_node(int data);//指针
//插入节点的递归函数
void insert(Node** pRoot,Node* pn);
//采用中序方法遍历二叉数
void travelData(Tree* pt);
//递归遍历函数
void travel(Node* pn);
//清空二叉数中所有的节点
void clearData(Tree* pt);
//清空的递归函数
void clear(Node** pRoot);
//查找指定的元素所在的地址
Node** findData(Tree* pt,int data);
//查找的递归函数
Node** find(Node** pRoot,int data);
//删除指定的元素
void delData(Tree* pt,int data);
//修改二叉数指定元素的值
void modifyData(Tree* pt,int data,int newData);
//判断二叉数是否为空
int empty(Tree* pt);
//判断二叉数是否为满
int full(Tree* pt);
//计算节点的个数
int size(Tree* pt);
//获取根节点元素值
int get_root(Tree* pt);
int main()
{//创建二叉数,并且初始化Tree tree;tree.root=NULL;tree.cnt=0;insertData(&tree,50);//50travelData(&tree);insertData(&tree,10);//10 50travelData(&tree);insertData(&tree,30);//10 30 50travelData(&tree);insertData(&tree,40);//10 30 40 50travelData(&tree);insertData(&tree,20);//10 20 30 40 50travelData(&tree);insertData(&tree,70);//10 20 30 40 50 70travelData(&tree);insertData(&tree,60);//10 20 30 40 50 60 70travelData(&tree);printf("----------------------\n");delData(&tree,30);//10 20 40 50 60 70travelData(&tree);delData(&tree,50);//10 20 40 60 70travelData(&tree);delData(&tree,80);//10 20 40 60 70travelData(&tree);printf("-----------------------\n");modifyData(&tree,20,80);travelData(&tree);printf("二叉数中根节点元素是:%d\n",get_root(&tree));printf("二叉数中节点个数是:%d\n",size(&tree));printf("%s\n",empty(&tree)?"二叉树为空":"二叉树未空");printf("%s\n",full(&tree)?"二叉树为满":"二叉树未满");printf("--------------------------------\n");printf("二叉数已清空\n");clearData(&tree);travelData(&tree);return 0;
}
//修改二叉数指定元素的值
void modifyData(Tree* pt,int data,int newData)
{//1.删除指定元素的节点delData(pt,data);//插入新元素值insertData(pt,newData);
}
//判断二叉数是否为空
int empty(Tree* pt)
{return NULL==pt->root;
}
//判断二叉数是否为满
int full(Tree* pt)
{return 0;
}
//计算节点的个数
int size(Tree* pt)
{return pt->cnt;
}
//获取根节点元素值
int get_root(Tree* pt)
{return empty(pt)?-1:pt->root->data;
}
//清空二叉数中所有的节点
void clearData(Tree* pt)
{//调用递归函数进行清空 址传递clear(&pt->root);pt->cnt=0;
}
//清空的递归函数
void clear(Node** pRoot)
{if(*pRoot!=NULL){//1.清空左子树clear(&(*pRoot)->left);//2.清空右子树clear(&(*pRoot)->right);//3.清空根节点free(*pRoot);*pRoot=NULL;}
}
//查找指定的元素所在的地址
Node** findData(Tree* pt,int data)
{//调用递归函数实现查找return find(&pt->root,data);
}
//查找的递归函数
Node** find(Node** pRoot,int data)
{//1.判断二叉数是否为空if(NULL==*pRoot){return pRoot;//查找失败}//2.判断根节点是否和目标元素相等else if(data==(*pRoot)->data){return pRoot;//查找成功}//3.如果目标元素小于根节点,则去左子树中进行查找else if(data < (*pRoot)->data){return find(&(*pRoot)->left,data);}//4.如果目标元素大于根节点,则去右子数中进行查找else{return find(&(*pRoot)->right,data);}
}
//删除指定的元素
void delData(Tree* pt,int data)
{//1.查找目标元素所在的地址Node** p=findData(pt,data);//2.根据返回值进行判断,如果查找失败,则删除失败,函数结束if(NULL==*p){printf("元素%d不存在,删除失败\n",data);return;}//3.如果查找成功,先将左子树合并到右子树中if((*p)->left != NULL){insert(&(*p)->right,(*p)->left);}//4.将要删除的节点地址单独保存Node* q=*p;//5.将连接目标节点的指针指向合并出来的右子树*p=(*p)->right;//6.删除目标节点,个数减1free(q);q=NULL;pt->cnt--;
}
//采用中序方法遍历二叉数
void travelData(Tree* pt)
{//调用递归函数进行遍历travel(pt->root);printf("\n");
}
//递归遍历函数  中序遍历
void travel(Node* pn)
{if(pn!=NULL){//1.遍历左子树travel(pn->left);//2.遍历根节点printf("%d ",pn->data);//3.遍历右子树travel(pn->right);}
}
//插入节点的递归函数
void insert(Node** pRoot,Node* pn)//二级指针
{//1.判断二叉数是否为空,如果为空,直接插入即可if(NULL==*pRoot){//Node** pRoot=&root;//*pRoot=root;*pRoot=pn;return;}//2. 如果二叉数不为空,则使用根节点与新节点比较大小//2.1如果新节点小于根节点,则插入到左子树中if(pn->data < (*pRoot)->data){insert(&(*pRoot)->left,pn);}//2.2如果新节点大于根节点,则插入到右子树中else{insert(&(*pRoot)->right,pn);}}
//创建新节点
Node* create_node(int data)//指针
{Node* pn=(Node*)malloc(sizeof(Node));pn->data=data;pn->left=NULL;pn->right=NULL;return pn;
}
//插入新节点后,组成有序二叉数
void insertData(Tree* pt,int data)
{//1.创建新建点//2.插入新节点到二叉数中insert(&pt->root,create_node(data));//3.节点的个数加1pt->cnt++;
}
输出结果:
50
10 50
10 30 50
10 30 40 50
10 20 30 40 50
10 20 30 40 50 70
10 20 30 40 50 60 70
----------------------
10 20 40 50 60 70
10 20 40 60 70
元素80不存在,删除失败
10 20 40 60 70
-----------------------
10 40 60 70 80
二叉数中根节点元素是:70
二叉数中节点个数是:5
二叉树未空
二叉树未满
--------------------------------
二叉数已清空
(2)链式有序二叉树(示例二)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>  /* 节点 */
typedef struct BsTreeNode {  int                data;  /* 数据 */  struct BsTreeNode* left;  /* 左子树 */  struct BsTreeNode* right; /* 右子树 */
}   BSTREE_NODE;
/* 二叉树 */
typedef struct BsTree {  BSTREE_NODE* root; /* 树根 */  size_t       size; /* 大小 */
}   BSTREE;
/* 初始化为空树 */
void bstree_init (BSTREE* bstree);
/* 释放剩余节点并恢复到初始状态 */
void bstree_deinit (BSTREE* bstree);
/* 插入 */
void bstree_insert (BSTREE* bstree, int data);
/* 删除 */
int bstree_erase (BSTREE* bstree, int data);
/* 删除所有匹配数据 */
void bstree_remove (BSTREE* bstree, int data);
/* 清空 */
void bstree_clear (BSTREE* bstree);
/* 更新 */
void bstree_update (BSTREE* bstree, int old,  int new);
/* 判断是否存在 */
int bstree_exist (BSTREE* bstree, int data);
/* 中序遍历 */
void bstree_travel (BSTREE* bstree);
/* 大小 */
size_t bstree_size (BSTREE* bstree);
/* 高度 */
size_t bstree_height (BSTREE* bstree);  /* 测试用例 */
int main (void) {  BSTREE bstree;  bstree_init (&bstree);  bstree_insert (&bstree, 50);  bstree_insert (&bstree, 70);  bstree_insert (&bstree, 20);  bstree_insert (&bstree, 60);  bstree_insert (&bstree, 40);  bstree_insert (&bstree, 30);  bstree_insert (&bstree, 10);  bstree_insert (&bstree, 90);  bstree_insert (&bstree, 80);  /* srand (time (NULL)); int i; for (i = 0; i < 20; ++i) bstree_insert (&bstree, rand () % 1000); */  bstree_travel (&bstree);  printf ("%u, %u\n", bstree_size (&bstree),  bstree_height (&bstree));  bstree_erase (&bstree, 60);  bstree_travel (&bstree);  bstree_insert (&bstree, 50);  bstree_insert (&bstree, 50);  bstree_travel (&bstree);  bstree_remove (&bstree, 50);  bstree_travel (&bstree);  bstree_insert (&bstree, 40);  bstree_insert (&bstree, 40);  bstree_travel (&bstree);  bstree_update (&bstree, 40, 85);  bstree_travel (&bstree);  printf ("%d, %d\n", bstree_exist (&bstree, 40),  bstree_exist (&bstree, 85));  bstree_deinit (&bstree);  return 0;
}  /* 创建节点 */
static BSTREE_NODE* create_node (int data) {  BSTREE_NODE* node = malloc (  sizeof (BSTREE_NODE));  node->data = data;  node->left = NULL;  node->right = NULL;  return node;
}
/* 销毁节点 */
static void destroy_node (BSTREE_NODE* node) {  free (node);
}
/* 将参数node的目标节点插入到以参数root的目标节点为 根的子树中 */
static void insert (BSTREE_NODE* node,  BSTREE_NODE** root) {  if (! *root)  *root = node;  else if (node)  if (node->data < (*root)->data)  insert (node, &(*root)->left);  else  insert (node, &(*root)->right);
}
/* 返回以参数root的目标所指向的节点为根的子树中, 数值与参数data相匹配的节点的父节点中,指向该 节点的指针型成员变量的地址 */
static BSTREE_NODE** find (int data,  BSTREE_NODE** root) {  if (! *root)  return root;  if (data < (*root)->data)  return find (data, &(*root)->left);  if ((*root)->data < data)  return find (data, &(*root)->right);  return root;
}
/* 销毁以参数root的目标节点为根的子树 */
static void clear (BSTREE_NODE** root) {  if (*root) {  clear (&(*root)->left);  clear (&(*root)->right);  destroy_node (*root);  *root = NULL;  }
}
/* 中序遍历以参数root的目标节点为根的子树 */
static void travel (BSTREE_NODE* root) {  if (root) {  travel (root->left);  printf ("%d ", root->data);  travel (root->right);  }
}
/* 返回以参数root的目标节点为根的子树的高度 */
static size_t height (BSTREE_NODE* root) {  if (root) {  size_t lh = height (root->left);  size_t rh = height (root->right);  return (lh > rh ? lh : rh) + 1;  }  return 0;
}
/* 初始化为空树 */
void bstree_init (BSTREE* bstree) {  bstree->root = NULL;  bstree->size = 0;
}
/* 释放剩余节点并恢复到初始状态 */
void bstree_deinit (BSTREE* bstree) {  clear (&bstree->root);  bstree->size = 0;
}
/* 插入 */
void bstree_insert (BSTREE* bstree, int data) {  insert (create_node (data), &bstree->root);  ++bstree->size;
}
/* 删除 */
int bstree_erase (BSTREE* bstree, int data) {  BSTREE_NODE** node = find (data, &bstree->root);  if (*node) {  /* 将匹配节点的左子树插入其右子树 */  insert ((*node)->left, &(*node)->right);  BSTREE_NODE* temp = *node;  /* 用匹配节点的右子树的根节点取代匹配节点 */  *node = (*node)->right;  /* 删除匹配节点 */  destroy_node (temp);  --bstree->size;  return 1;  }  return 0;
}
/* 删除所有匹配数据 */
void bstree_remove (BSTREE* bstree, int data) {  while (bstree_erase (bstree, data));
}
/* 清空 */
void bstree_clear (BSTREE* bstree) {  bstree_deinit (bstree);
}
/* 更新 */
void bstree_update (BSTREE* bstree, int old,  int new) {  while (bstree_erase (bstree, old))  bstree_insert (bstree, new);
}
/* 判断是否存在 */
int bstree_exist (BSTREE* bstree, int data) {  return *find (data, &bstree->root) != NULL;
}
/* 中序遍历 */
void bstree_travel (BSTREE* bstree) {  travel (bstree->root);  printf ("\n");
}
/* 大小 */
size_t bstree_size (BSTREE* bstree) {  return bstree->size;
}
/* 高度 */
size_t bstree_height (BSTREE* bstree) {  return height (bstree->root);
}
输出结果:
10 20 30 40 50 60 70 80 90
9, 4
10 20 30 40 50 70 80 90
10 20 30 40 50 50 50 70 80 90
10 20 30 40 70 80 90
10 20 30 40 40 40 70 80 90
10 20 30 70 80 85 85 85 90
0, 1

数据结构与算法 -- 二叉树 ADT相关推荐

  1. 数据结构与算法--二叉树第k个大的节点

    二叉树第k个大的节点 二叉树文章列表: 数据结构与算法–面试必问AVL树原理及实现 数据结构与算法–二叉树的深度问题 数据结构与算法–二叉堆(最大堆,最小堆)实现及原理 数据结构与算法–二叉查找树转顺 ...

  2. 数据结构与算法-- 二叉树中和为某一值的路径

    二叉树中和为某一值的路径 题目:输入一颗二叉树和一个整数,打印出二叉树中节点值的和为给定值的所有路径.从树的根节点开始往下一只到叶子节点所经过的节点形成一条路径. 我们用二叉树节点的定义沿用之前文章中 ...

  3. 数据结构与算法-- 二叉树后续遍历序列校验

    二叉树后续遍历序列校验 题目:输入一个整数数组,判断改数组是否是某个二叉搜索树的后续遍历结果,如果是返回true否则false,假设输入数组的任意两个数字不相同. 例如输入{5,7,6,9,11,10 ...

  4. javascript数据结构与算法--二叉树遍历(中序)

    javascript数据结构与算法--二叉树遍历(中序) 中序遍历按照节点上的键值,以升序访问BST上的所有节点 代码如下: /**二叉树中,相对较小的值保存在左节点上,较大的值保存在右节点中*** ...

  5. 数据结构与算法 -- 栈 ADT

    这两天翻了下数据结构与算法分析.严蔚敏的数据结构.C和指针.C Primer Plus这些本书,受益很多.不过大多的示例不够完整,需要自己动手编写程序.又看了遍培训时的笔记,虽然很糙但是精华的部分还是 ...

  6. 数据结构与算法--二叉树的深度问题

    二叉树的深度 题目:输入一颗二叉树的根,求该树的深度.从根节点到叶子节点一次进过的节点形成的一条路径,最长的路径的长度为树的深度. 如下图中二叉树的额深度4,因为从根节点A到叶子节点的路径中有4个节点 ...

  7. 数据结构与算法--二叉树实现原理

    二叉树 二叉树(binary tree)是一棵树,其中每个节点都不能有多于两个的子节点 二叉树的一个性质是一颗平均二叉树的深度要比节点个数N小得多(重点),对二叉树的分析得出其平均深度为O(N\sqr ...

  8. 数据结构与算法-二叉树(java描述)

    一.概述 1.1.树的概念 树状图是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合.把它叫做"树"是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而 ...

  9. 数据结构与算法——二叉树、堆、优先队列

    *************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 七 ...

最新文章

  1. LeetCode Remove Duplicates from Sorted List
  2. cvtColor函数
  3. 滴滴算法大赛算法解决过程 - 拟合算法
  4. 美国夫妇用数学算法买彩票赢1.74亿元——网友:现在学数学还来得及吗?
  5. 爱奇艺PPS如何登陆账号
  6. iFit—Smart Cardio Equipment 简介与下载
  7. 【华为推荐论文】如何学习未知样本?基于反事实学习的推荐系统技术研究(附论文下载链接)...
  8. vue中使用this遇到的坑
  9. ubuntu20.04+vtd环境搭建
  10. BP神经网络预测matlab程序销售量预测
  11. L' Hospital(洛必达)法则
  12. spss多元线性回归散点图_如何通过残差散点图检验SPSS线性回归是否存在异方差等问题?...
  13. 为什么博客图片不显示?
  14. nodejs高速公路收费管理系统vue
  15. python2 if写法_python 中if-else的多种简洁的写法
  16. 苹果电脑和Windows怎么切换 苹果电脑安装双系统的利弊
  17. Whitelabel Error Page访问
  18. 1375. 至少K个不同字符的子串
  19. linux每日命令,Linux日常命令整理
  20. 全球电容生产厂商排名一览表

热门文章

  1. Spring入门hello world常见问题及解决办法
  2. tp论坛 分页(三)
  3. HTTP的 Basic 验证
  4. Python学习笔记:异步IO(1)
  5. Tips:重装系统后Anaconda目录在开始菜单消失问题
  6. 【算法】交叉熵损失和KL散度
  7. 【流量】一觉醒来发现CSDN博客访问量增加十倍!原来是这个原因
  8. 小科知道20211202
  9. Jupyter notebook入门教程(上)
  10. swift. 扩展类添加属性_Swift中用到extension的一些基本的扩展功能讲解