二叉树,每个节点包含一个数据元素,一个左子树和一个右子树(地址)。
typedef int T;
struct BTNode{
T elem;
struct BTNode *lchild,*rchild;
};
typedef struct BTNode* BTree;
初始化一棵二叉树
注意这里传的参数是二叉树地址的地址,是个二级指针,因为我们需要将二叉树的地址(一级指针)置为NULL
void initBTree(BTree *ptree){
assert(ptree!=NULL);
*ptree=NULL;
}
是否为空
bool emptyBTree(BTree tree){
return tree==NULL;
}
获取一个二叉树的大小,递归,tree为null时返回0,代表这个子树大小为0,子树不为空时,调用时继续调用这颗二叉树两个子树的大小,加1表示这个子树当前节点不为空。
size_t sizeBTree(BTree tree){
if(tree==NULL) return 0;
return sizeBTree(tree->lchild)+sizeBTree(tree->rchild)+1;
}
获取一棵二叉树的高度,同样需要递归,树为空时返回0,不为空则分别调用此书两颗子树的高度,则此树的高度为两颗子树中最高的高度加1。
size_t heightBTree(BTree tree){
if(tree==NULL) return 0;
size_t lheight = sizeBTree(tree->lchild);
size_t rheight = sizeBTree(tree->rchild);
return lheight>rheight?lheight+1:rheight+1;
}
前序遍历,先判断是否为空,然后先遍历根节点(就是当前的树),再分别用递归的方式遍历左子树和右子树。
void foreachPrevBTree(BTree tree ,void(*callback)(T)){
if(tree!=NULL){
callback(tree->elem);
foreachPrevBTree(tree->lchild,callback);
foreachPrevBTree(tree->rchild,callback);
}
类似的,中序和后序的遍历
void foreachMidBTree(BTree tree ,void (*callback)(T)){
if(tree!=NULL){
foreachMidBTree(tree->lchild,callback);
callback(tree->elem);
foreachMidBTree(tree->rchild,callback);
}
}
void foreachBackBTree(BTree tree,void (*callback)(T)){
if(tree!=NULL){
foreachBackBTree(tree->lchild,callback);
foreachBackBTree(tree->rchild,callback);
callback(tree->elem);
}
}
查找操作(针对二叉查找树)
从当前的树开始,如果当前树的数据大于要找的数据,去这颗树的左子树继续找,反之去右子树,直到找到或者遇到空节点(找完了)。
int searchBTree(BTree tree,T elem){
struct BTNode *node = tree;
while(node!=NULL){
if (elem<node->elem)
{
node=node->lchild;
}
else if(elem>=node->elem){
node=node->rchild;
}
else{
return 1;
}
}
return 0;
}
递归版本的查找操作,原理大致相同,只不过是从节点的遍历变为了层层调用子树。
int searchBTreeCallself(BTree tree,T elem){
if (tree==NULL)
return 0;
if(elem<tree->elem){
return searchBTreeCallself(tree->lchild,elem);
}
else if (elem>tree->elem)
{
return searchBTreeCallself(tree->rchild,elem);
}
return 1;
}
创建一个二叉排序树的结点 ,设置为静态,因为只在本文件中调用。注意静态文件需要在调用之前定义。需要传递节点的数据、此节点的左右子树。
//创建一个二叉排序树的结点
static struct BTNode *createBTNode(T elem,struct BTNode *lchild,struct BTNode *rchild){
struct BTNode *node = (struct BTNode *)malloc(sizeof(struct BTNode));
if(node!=NULL){
node->elem = elem;
node->lchild = lchild;
node->rchild = rchild;
}
return node;
}
给一棵二叉查找树中插入数据。这里的参数为一个二级指针,树的地址的地址。采用节点遍历的思想,当要插入的数据小于当前节点的数据,去左子树,否则去右子树,循环完毕后,当前节点来到了一个合适的空的叶子节点的位置。调用创建节点的函数,左右子树为NULL,一个二叉查找树的新节点就创建好了。
注意:二级指针ptree所对应的节点数据应该用(*ptree)->elem 来访问。
int insertBTree(BTree *ptree,T elem){
assert(ptree!=NULL);
while(*ptree != NULL){
if(elem < (*ptree)->elem){//说明应该插入到(*ptree)左子树中
ptree = &(*ptree)->lchild;
}else if(elem > (*ptree)->elem){
ptree = &(*ptree)->rchild;
}else{//elem == (*ptree)->elem
return FAILURE;
}
}
*ptree = createBTNode(elem,NULL,NULL);
if(*ptree == NULL){
return FAILURE;
}
return SUCCESS;
}
递归版本的插入,判断当前节点是大于还是小于要查的数据,递归去对应的左子树或右子树,为空时结束递归,创建节点。
int insertBTreeCallSelf(BTree *ptree,T elem){
assert(ptree!=NULL);
if(*ptree == NULL){
*ptree = createBTNode(elem,NULL,NULL);
if(*ptree == NULL){
return FAILURE;
}
return SUCCESS;
}
if(elem < (*ptree)->elem){
return insertBTreeCallSelf(&(*ptree)->lchild,elem);
}else if(elem > (*ptree)->elem){
return insertBTreeCallSelf(&(*ptree)->rchild,elem);
}
//有相等元素
return FAILURE;
}
删除当前节点,需要去这颗树的右子树中找一个数据最大的节点替换当前节点,然后删除被用来替换的节点。如果当前节点没有子树就直接删除。
先判断是不是左右子树都存在,如果是,则用一个for循环,找最右边的子树,找到后替换里面的数据。
注意,在此之后,被替换的节点可能还有一棵左子树,右子树为NULL,如果有左子树,则用他的左子树代替他的位置,再释放他。
static int removeBTNode(BTree * pnode){
struct BTNode *node=*pnode;
if(node->lchild!=NULL&&node->rchild!=NULL){
pnode=&(node->rchild);
for(;(*pnode)->rchild!=NULL;pnode=&(*pnode)->rchild);
node->elem=(*pnode)->elem;
}
*pnode=node->lchild!=NULL?node->lchild:node->rchild;
free(node);
return SUCCESS;
}
删除指定的数据,首先要找到相同的数据,从根节点开始查找,和前面查找操作相同,找到后则调用删除该节点的函数。
int deleteBTree(BTree *ptree,T elem){
assert(ptree!=NULL);
while(*ptree!=NULL){
if((*ptree)->elem==elem){
return removeBTNode(ptree);
}
if(elem<(*ptree)->elem){
ptree=&(*ptree)->lchild;
}
else if (elem>(*ptree)->elem){
ptree=&(*ptree)->rchild;
}
}
return FAILURE;
}
递归版本删除
int deleteBTreeCallself(BTree *ptree,T elem){
assert(ptree!=NULL);
if(*ptree==NULL){
return FAILURE;
}
if(elem<(*ptree)->elem){
return deleteBTreeCallself(&(*ptree)->lchild,elem);
}
if(elem>(*ptree)->elem){
return deleteBTreeCallself(&(*ptree)->rchild,elem);
}
return removeBTNode(ptree);
}
清除一棵二叉树。递归分别清除自己的左右子树,再free此节点,记得free之后将此节点地址置NULL。
void clearBTree(BTree *ptree){
assert(ptree!=NULL);
if(*ptree!=NULL){
clearBTree(&(*ptree)->lchild);
clearBTree(&(*ptree)->rchild);
free(*ptree);
*ptree=NULL;
}
}
是否为二叉查抄树,在每个节点对左右子树分别调用查找最大和最小值的函数,来和当前的数据作比较,没有问题之后递归调用两颗子树,有问题则return false。
bool isBinSortTree(BTree tree){
if(tree==NULL)
return true;
if(tree->lchild!=NULL&&maxValueSortTree(tree->lchild)>tree->elem){
return false;
}
if(tree->rchild!=NULL&&minValueSortTree(tree->rchild)<tree->elem){
return false;
}
return isBinSortTree(tree->lchild)&&isBinSortTree(tree->rchild);
}
是不是平衡二叉树,获取左右子树的高度,判断左减右和右减左的值是不是超过绝对值1。没问题就对左右子树递归调用。
bool isBalanceBinTree(BTree tree){
if(tree==NULL){
return true;
}
size_t lheight=heightBTree(tree->lchild);
size_t rheight=heightBTree(tree->rchild);
if(lheight-rheight>1||lheight-rheight<-1){
return false;
}
return isBalanceBinTree(tree->lchild)&&isBalanceBinTree(tree->rchild);
}
是不是平衡二叉查找树,即同时满足平衡和二叉查找树的条件。
bool isAvlTree(BTree tree){
return isBalanceBinTree(tree)&&isBinSortTree(tree);
}
int maxValueSortTree(BTree tree){
while(tree->lchild!=NULL){
tree=tree->lchild;;
}
return tree->elem;
}
找一棵二叉查找树中最小的值,一直向右遍历即可,最右边的树就是最小的。
查找最大值也是同理。
int minValueSortTree(BTree tree){
while(tree->rchild!=NULL){
tree=tree->rchild;
}
return tree->elem;
}