C语言 数据结构与算法---二叉排序(查找)树

一. 什么是二叉排序树

二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree),亦称二叉搜索树。它可以是一棵空树。

特点:

  • 若它的左子树不为空,则子树上所有结点的值均小于它的根结构的值。
  • 若它的右子树不为空,则子树上所有结点的值均大于它的根结构的值。
  • 它的左右子树也分别为二叉排序树。
  • 二叉搜索树支持对数时间的搜索,支持对数时间级别的插入和删除。
  • 一个无序序列可以通过构建一棵二叉排序树,从而变成一个有序序列。
    在这里插入图片描述

二. 二叉排序树的结构

与二叉树相同:

typedef struct BinNode
{
	int data;
	struct BinNode* lchild, * rchild;
}BinNode,*BinTree;

三. 二叉排序树的建立

void CreatTree(BinTree *T)
{
	char n;
	printf("请输入数据:\n");
	scanf("%c", &n);
	if (n == '#')
	{
		*T = NULL;
	}
	else
	{
		*T = (BinTree)malloc(sizeof(BinNode));
		if (!(*T))
		{
			return;
		}
		else
		{
			(*T)->data = n;
			CreatTree(&(*T)->lchild);
			CreatTree(&(*T)->rchild);
		}
	}
}

四. 二叉排序树的查找

在二叉排序树不为空树的前提下,首先将被查找值同树的根结点进行比较

  • 如果相等,查找成功;
  • 如果比较结果为根结点的关键字值较大,则说明该关键字可能存在其左子树中;
  • 如果比较结果为根结点的关键字值较小,则说明该关键字可能存在其右子树中;
BinTree SearchTree(BinTree T, int key)
{
	/*
	*  
	if (!T)
	{
		return NULL;
	}
	else if (T->data == key)
	{
		return T;
	}
	*/

	//简单写法如下
	//如果递归过程中 T 为空,则查找结果,返回NULL;或者查找成功,返回指向该关键字的指针
	if (!T || T->data == key)
	{
		return T;
	}
	else if (T->data > key)
	{
		return SearchTree(T->lchild,key);
	}
	else if (T->data < key)
	{
		return SearchTree(T->rchild, key);
	}
}

五. 二叉排序树的插入

当因为查找失败而需要插入数据元素时,该数据元素的插入位置一定位于二叉排序树的叶子结点,并且一定是查找失败时访问的最后一个结点的左孩子或者右孩子。

要完成插入操作,需要更改上述查找函数:

//指针 f 指向父结点,初始化为 NULL
bool SearchBST(BiTree T,int key, BiTree f, BiTree *p)
{
    // 如果 T 指针为空(找到尽头),说明查找失败,令 p 指针指向查找过程中最后一个叶子结点,并返回查找失败的信息
    if (!T)
   {
        *p = f;
        return false;
    }
    else if(key==T->data)
  {
     // 如果相等,令p指针指向该关键字,并返回查找成功信息
        *p = T;
        return true;
    }
    else if(key<T->data)
  {
        return SearchBST(T->lchild, key, T, p);
   }
  else
  {
        return SearchBST(T->rchild, key, T, p);
   }
}

插入:

bool InsertTree(BinTree T, int key)
{
	//查找成功,p指向查找到的结点
	//查找失败,p指向最后一个访问的结点
	BinTree p;

	//查找失败
	if (!SearchTree(T, key, NULL, &p))
	{
		BinTree s = (BinTree)malloc(sizeof(BinNode));
		s->data = key;
		s->lchild = s->rchild = NULL;

		//若 p 不存在,则为空树,新增结点为根结点
		if (!p)
		{
			T = s;
		}
		else if (key < p->data)
		{
			//插入s为左孩子
			p->lchild = s;
		}
		else {
			//插入s为右孩子
			p->rchild = s;
		}
		return true;
	}
	//数中已有关键字相同的结点,不在插入
	return false;
}

有了插入操作,就可以构建二叉排序树:

int i,n;
BinTree T = NULL;
printf("请输入你想要添加数据的数目:\n");
scanf("%d",&n);
int *a = (int*)malloc(n*sizeof(int));
printf("请依次输入你想要添加的数据:\n");
for(i=0;i<n;i++)
{
	scanf("%d",&a[i]);
	InsertTree(&T,a[i]);
}

也可以不依赖查找函数,直接添加数据:


void InsertTree2(BinTree T, int key)
{
	//若为空树,或者未找到
	if (!T) 
	{
		T = (BinTree)malloc(sizeof(BinNode));
		T->data = key;
		T->lchild = T->rchild = NULL;
		return;
	}

	//若找到则不添加
	if (T->data == key) 
	{
		return;
	}

	if (T->data > key) 
	{
		InsertTree2(T->lchild,key);
	}
	else 
	{
		InsertTree2(T->rchild, key);
	}
}

六. 二叉排序树的删除

  • 该结点只有左子树或者只有右子树,此时只需要将其左子树或者右子树直接变为结点 p 双亲结点的子树即可;

    • 左孩子为空,重接右孩子 ; 右孩子为空,重接左孩子
  • 该结点为叶子结点,此时只需要删除该结点,并修改其双亲结点的指针即可;

  • 该结点左右子树都不为空:

    • 需要找到这个要删除的结点的前驱或直接后继,把该前驱,换到该结点处。同时在二叉排序树中对其直接前驱(或直接后继)做删除操作。
      在这里插入图片描述
int Deletetree(BinTree* T, int key)
{
	//未找到key
	if (!*T)
	{
		return false;
	}
	else
	{
		if (key < (*T)->data)
		{
			Deletetree(&(* T)->lchild,key);
		}
		else if (key > (*T)->data)
		{
			Deletetree(&(*T)->rchild, key);
		}
		else//找到,开始删除操作
		{
			BinTree *p = T;
			BinTree q;
			//左孩子为空,重接右孩子
			if ((*p)->Left == NULL)
			{
				q = *p;
				*p = (*p)->Right;
				free(q);
			}
			//右孩子为空,重接左孩子
			else if ((*p)->Right == NULL)
			{
				q = *p;
				*p = (*p)->lchild;
				free(q);
			}
			//左右孩子都不为空
			else
			{
				q = *p;
				//寻找前驱,转左,向右到尽头
				BinTree s=(*p)->lchild;
				while (s->rchild)
				{
					q = s;
					s = s->rchild;
				}
				(*p)->data = s->data;
				//此时的s指向被删除结点直接前驱,此时的p是要删除的那个结点,此时的q是s的父结点
				if (q != *p)
				{
					q->rchild = s->lchild;
				}
				else
				{
					q->lchild = s->rchild;
				}
				free(s);
			}
		}
	}

  • 令结点的左子树为其双亲结点的左子树;结点 p 的右子树为其自身直接前驱结点的右子树
    在这里插入图片描述
// T 为待删除的结点
void kill(BinTree* T)
{
	BinTree s;
	//若无子树
	if (!(*T)->lchild && !(*T)->rchild)
	{
		*T = NULL;
	}
	//若无左子树,重接右子树
	else if (!(*T)->lchild)
	{
		s = *T;
		*T = (*T)->rchild;
		free(s);
	}
	//若无右子树,重接左子树
	else if (!(*T)->rchild)
	{
		s = *T;
		*T = (*T)->lchild;
		free(s);
	}
	//若左右子树都有
	else
	{
		s = *T;
		BinTree p = (*T)->lchild;
		//找到该结点前驱(该结点左子树的最右子树)
		while (p->rchild)
		{
			p = p->rchild;
		}
		//将该结点的右子树接到前驱的后面-->前驱的右子树
		p->rchild = (*T)->rchild;
		//将该结点的左子树变成该结点双亲的左子树
		*T = (*T)->lchild;
		free(s);
	}
}

//找出待删除的结点
bool Delete(BinTree* T, int m)
{
	//若找不到待删除的结点,或树为空
	if (!(*T))
	{
		return false;
	}

	//若找到待删除的结点
	else if ((*T)->data == m)
	{
		kill(T);
		return true;
	}
	else if ((*T)->data > m)
	{
		Delete(&(*T)->lchild,m);
	}
	else if ((*T)->data < m)
	{
		Delete(&(*T)->rchild, m);
	}
}

七. 二叉排序树的最值

最小值:

BinTree FindMin(BinTree T)
{
	if (T)
	{
		while (T->lchild)
		{
			T = T->lchild;
		}
	}
	return T;
}

最大值:


BinTree FindMax(BinTree T)
{
	if (T)
	{
		while (T->rchild)
		{
			T = T->rchild;
		}
	}
	return T;
}

八. 二叉排序树的打印

//中序打印
void printTree(BinTree T)
{
	if (T)
	{
		printTree(T->lchild);
		printf("%d",T->data);
		printTree(T->rchild);
	}
}
发布了37 篇原创文章 · 获赞 30 · 访问量 1118

猜你喜欢

转载自blog.csdn.net/myjess/article/details/104534566