【数据结构】二叉树

二叉树,每个节点包含一个数据元素,一个左子树和一个右子树(地址)。

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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值