查找二叉树:也叫排序二叉树,搜索二叉树。具有以下特点(百度百科)
二叉排序树或者是一棵空树,或者是具有下列性质的
二叉树: (1)若左子树不空,则左子树上所有结点的值均小于或等于它的
根结点的值; (2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值; (3)左、右子树也分别为二叉排序树;
查找二叉树的创建和基本二叉树的创建无差异
二叉树的清空,实际上就是后序遍历删除
// 想要改变指针的值还是需要将他作为返回值,不然就是传入二级指针
static SearchTree MakeEmpty(SearchTree tree)
{
if(tree){
MakeEmpty(tree->LeftNode);
MakeEmpty(tree->RightNode);
free(tree);
tree = NULL;
}
return tree;
}
查找二叉树的查找
1、基本二叉树的查找方法
// 实际上这样会将所有的节点都遍历一遍而忽略了查找二叉树的特点
// 以root为界限开始区分两者
static SearchTree FindNode(TreeElement element, SearchTree tree)
{
SearchTree ret_tree = NULL;
if (tree){
if(element==tree->element){
ret_tree = tree;
return ret_tree;
}
ret_tree = FindNode(element, tree->LeftNode);
ret_tree = FindNode(element, tree->RightNode);
}
return ret_tree;
}
2、二叉树的特点使用尾递归(递归在函数的尾部)
// 抓住了查找二叉树的特点,可以更加精确的进行查找
static SearchTree Find(TreeElement element, SearchTree tree)
{
if(tree){
if(tree->element==element){
return tree;
}else if(tree->element>element){
return Find(element, tree->LeftNode);
}else{
return Find(element, tree->RightNode);
}
}
return NULL;
}
3、将尾递归转换成赋值和goto语句
因为二叉树的平均深度是logN,所以使用的栈空间也只是logN,一般还是可以直接使用方法2,以下供参考
// 将尾递归转换成赋值和goto语句
static SearchTree FindNoLoop(TreeElement element, SearchTree tree)
{
next:
if(tree){
if(tree->element==element){
return tree;
}else{
if(tree->element>element)
tree = tree->LeftNode;
else
tree = tree->RightNode;
goto next;
}
}
return NULL;
}
查找二叉树查找最大、最小值
static SearchTree FindMax(SearchTree tree)
{
next:
if(tree){
if(tree->RightNode == NULL)
return tree;
tree = tree->RightNode;
goto next;
}
return tree;
}
static SearchTree FindMin(SearchTree tree)
{
if(tree){
if(tree->LeftNode == NULL)
return tree;
return FindMin(tree->LeftNode);
}
return NULL;
}
往二叉树中插入元素
如果插入的元素已经在二叉树中存在,则只是更新二叉树中记录发生频率的域(这里什么都不做)。否则插入到查找二叉树的尾部。
static SearchTree CreateTreeCell(TreeElement element)
{
SearchTree tree = NULL;
tree = (SearchTree)malloc(sizeof(*tree));
if(tree==NULL){
fprintf(stderr,"there is no space\n");
return NULL;
}
tree->element= element;
tree->RightNode = NULL;
tree->LeftNode = NULL;
return tree;
}
static SearchTree Insert(TreeElement element, SearchTree tree)
{
if(tree){
if(element>tree->element){
if(tree->RightNode==NULL){
tree->RightNode = CreateTreeCell(element);
}else{
Insert(element, tree->RightNode);
}
}else{
if(tree->LeftNode==NULL){
tree->LeftNode = CreateTreeCell(element);
}else{
Insert(element, tree->LeftNode);
}
}
return tree;
}
return NULL;
}
这是书上的解法,比我的思路好。
static SearchTree InsertNode(TreeElement element, SearchTree tree)
{
if(tree==NULL){
tree = (SearchTree)malloc(sizeof(*tree));
if(tree==NULL){
fprintf(stderr,"there is no space\n");
return NULL;
}
tree->element= element;
printf("tree->element is %d\n", tree->element);
tree->RightNode = NULL;
tree->LeftNode = NULL;
}else if(element>tree->element){
//需要将返回值指向tree->RightNode
tree->RightNode = InsertNode(element, tree->RightNode);
}else{
// 需要将返回值指向tree->LeftNode
tree->LeftNode = InsertNode(element, tree->LeftNode);
}
return tree;
}
节点的删除
1)删除的节点为树叶
2)删除的节点包含一个子节点
3)删除的节点包含两个子节点(在该节点的右子树中寻找最小,特换为该节点元素,然后删除右子树元素最小的节点)
值得注意的:当两个指针同时指向同一块内存,free掉一个指针会导致另外一个指针成为野指针,所以也需要将其置为NULL
static SearchTree Delete(TreeElement element, SearchTree tree)
{
if(tree){
if(element>tree->element){
// 无论是插入还是删除都需要建立指针的指向关系
tree->RightNode = Delete(element, tree->RightNode);
}else if(element<tree->element){
tree->LeftNode = Delete(element, tree->LeftNode);
}else{
if(tree->RightNode!=NULL&&tree->LeftNode!=NULL){
SearchTree tree_min = FindMin(tree->RightNode);
if(tree_min){
printf("tree->element is %d\n", tree->element);
tree->element = tree_min->element;
tree->RightNode = Delete(tree_min->element, tree->RightNode);
}
}else{
SearchTree tree_temp = tree;
if(tree->RightNode){
tree = tree->RightNode;
}else if(tree->LeftNode){
tree = tree->LeftNode;
}
if(tree==tree_temp){//两个指针同时指向同一块内存,需要将其置为NULL
tree = NULL;
}
free(tree_temp);
tree_temp = NULL;
}
}
return tree;
}
}