二叉搜索树

时间:2022-06-22
本文章向大家介绍二叉搜索树,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

版权声明:本文为博主原创文章,转载请注明博客地址: https://blog.csdn.net/zy010101/article/details/83033069

二叉搜索树(Binary Search Tree)也叫做二叉排序树或者二叉查找树。顾名思义,它是一种对排序和查找都很有用的特殊二叉树

二叉查找树满足以下性质:(假设二叉查找树中每个节点元素都是不同的,它也可以为空)

  1. 非空左子树的所有键值小于其根节点的键值;
  2. 非空右子树的所有键值大于其根节点的键值;
  3. 左,右两棵子树都是二叉搜索树

二叉搜索树本质上还是一棵二叉树,只不过对它做了限定,就行队列和栈一样,它们是对线性表做了限制。对二叉搜索树的遍历和创建操作与普通二叉树一致。但是二叉搜索树的特点使得对它的查找,插入,删除变得有些不同。

二叉搜索树的平均深度是O(logn)的,一般不会造成爆栈的。

二叉搜索树的对于查找问题的解决,本质上还是二分法的使用。但是不同于我们对一个有序数组使用的二分查找法。有序数组上施加的二分查找是元素个数恒定不变的(不进行插入和删除操作),称之为静态查找。二叉搜索树则可以支持插入和删除操作,它使得查找的范围可以动态变化,称之为动态查找

如果按照查找操作是如何进行的来分类,那么二叉搜索树和二分查找都是基于比较实现的;另外一种实现查找的方式是基于映射实现的,即:散列表,或者称之为哈希表。

二叉搜索树的ATD和操作集

#ifndef BST
#define BST

#include<iostream>
#include<stack>

using std::cout;
using std::cin;
using std::stack;

typedef struct TreeNode Tree;
typedef Tree * PTree;
typedef PTree Position;
typedef int ElementType;

struct TreeNode
{
	ElementType data;
	PTree left;
	PTree right;
};
Position Find(ElementType k, PTree T);		 //递归版查找
Position Find(ElementType k, PTree T,int a); //迭代版查找
Position FindMax(PTree T);					 //返回最大元素
Position FindMin(PTree T);					 //返回最小元素
void InorderTraversal(PTree T);				 //递归版中序遍历
void InorderTraversal(PTree T,int a);		 //迭代版中序遍历
PTree Insert(ElementType x, PTree T);		 //插入操作
PTree Delete(ElementType x, PTree T);		 //删除操作
int PostinorderGetHeight(PTree T);			 //二叉树的高度

#endif // !BST

二叉搜索树操作集的C++实现代码:

#include "searchtree.h"

//递归版本实现的查找函数,二叉树的平均深度是O(log n),可以递归的
Position Find(ElementType k, PTree T)
{
	if (NULL == T)
	{
		return NULL;			//空树
	}
	if (k < T->data)			//k小于data,去该节点的左子树查找。
	{
		return Find(k,T->left);
	}
	else if (k > T->data)		//k大于data,去该节点的右子树查找。
	{
		return Find(k, T->right);
	}
	else if (k == T->data)		//k等于data,直接返回该节点。
	{
		return T;
	}
}

Position Find(ElementType k, PTree T, int a)
{
	while (T)		//树不空执行循环
	{
		if (k > T->data)
		{
			T = T->right;		//转右子树查找
		}
		else if (k < T->data)
		{
			T = T->left;		//转左子树查找
		}
		else
		{
			return T;			//找到了该元素
		}
	}
	return NULL;	//没有找到该元素
}

Position FindMax(PTree T)
{
	if (NULL == T)
	{
		return NULL;	//空树
	}
	else
	{
		while (T->right)
		{
			T = T->right;		//最大值在其右子树上,且该右子树无右子树
		}
		return T;			//返回最大值所在节点
	}
}

Position FindMin(PTree T)
{
	if (NULL == T)
	{
		return NULL;	//空树
	}
	else
	{
		while (T->left)
		{
			T = T->left;		//最小值在其左子树上,且该左子树无左子树
		}
		return T;			//返回最小值所在节点
	}
}

void InorderTraversal(PTree T)
{
	if (T)
	{
		InorderTraversal(T->left);
		cout << T->data << ' ';
		InorderTraversal(T->right);
	}
}

void InorderTraversal(PTree T, int a)
{
	if (NULL == T)
	{
		return;
	}
	else
	{
		stack<PTree> S;			//创建空栈
		while (T || !S.empty())
		{
			while (T)
			{
				S.push(T);				//将T压入栈中
				T = T->left;			//转左子树
			}
			if(!S.empty())
			{
				/*T = (PTree)malloc(sizeof(Tree));
				T->data = S.top()->data;
				T->left = S.top()->left;
				T->right = S.top()->right;*/
				T = S.top();
				cout << T->data << ' ';
				T = T->right;            //转右子树
				S.pop();				//删除栈顶元素
			}
		}
	}
}

PTree Insert(ElementType x, PTree T)
{
	PTree temp = NULL;
	PTree BT = T;
	if (!T)
	{
		T = (PTree)malloc(sizeof(Tree));
		T->data = x;
		T->left = T->right = NULL;
		return T;
	}
	else
	{
		while (T)
		{
			if (x < T->data)
			{
				if (!T->left)	//若左子树为空,插入到这里
				{
					temp = (PTree)malloc(sizeof(Tree));
					temp->data = x;
					temp->left = NULL;
					temp->right = NULL;
					T->left = temp;
					break;
				}
				else			//若不空,转左子树
				{
					T = T->left;
				}
			}
			if (x > T->data)
			{
				if (!T->right)		//若右子树为空,插入到这里
				{
					temp = (PTree)malloc(sizeof(Tree));
					temp->data = x;
					temp->left = NULL;
					temp->right = NULL;
					T->right = temp;
					break;
				}
				else		//若不空,转右子树
				{
					T = T->right;
				}
			}
			if (x == T->data)	//找到这个元素
			{
				break;
				//如果要插入的元素已经在该树中存在,那么一般有两种做法
				//1.什么操作也不做,适合在不追求重复元素的场合使用
				//2.向树的ADT中增加一个域,用来保存该元素的出现次数。这样使得树
				//增加了一下附加空间,但是,却比将重复信息放入树中好的多,这样避免
				//了树深度的增加。
				//在这里我们采用第一种方式,什么也不做。
			}
		}
		return BT;
	}
}

PTree Delete(ElementType x, PTree T)
{
	PTree temp;
	if (!T)
	{
		cout << "删除元素未找到!n";
	}
	else
	{
		if (x < T->data)
		{
			T->left = Delete(x, T->left);		//递归删除左子树
		}
		else if (x > T->data)
		{	
			T->right = Delete(x, T->right);		//递归删除右子树
		}
		else
		{
			if (T->left && T->right)	//左右子树都存在
			{
				temp = FindMin(T->right);
				//temp = FindMax(T->left);
				T->data = temp->data;
				T->right = Delete(T->data, T->right);
			}
			else            
			{
				temp = T;
				if (!T->left)        //右子树存在或者左右子树都不存在
				{
					T = T->right;
				}
				else if (!T->right)    //左子树存在或者左右子树都不存在
				{
					T = T->left;
				}
				free(temp);
			}
			return T;
		}
	}
}

int PostinorderGetHeight(PTree T)
{
	if (T)
	{
		int l, r, max;
		l = PostinorderGetHeight(T->left);
		r = PostinorderGetHeight(T->right);
		max = l > r ? l : r;
		return max + 1;
	}
	else
	{
		return 0;
	}
}

main.cpp文件

#include"searchtree.h"

int main()
{
	PTree BT = NULL;
	for (int i = 5; i < 10; i++)	//把根节点设置为5,插入5-9;
	{
		BT = Insert(i, BT);
	}
	for (int i = 0; i < 5; i++)		//插入0-4.
	{
		BT = Insert(i, BT);
	}
	Insert(5, BT);
	//理论上中序遍历输出应该是0 1 2 3 4 5 6 7 8 9
	cout << "中序遍历输出:";
	InorderTraversal(BT,1);
	cout << "n树的深度是:";
	cout << PostinorderGetHeight(BT);
	cout << "n树中最大元素是:" << FindMax(BT)->data;
	cout << "n树中最小元素是:" << FindMin(BT)->data;
	cout << 'n' << "删除元素5后,中序遍历结果:";
	BT = Delete(5, BT);
	InorderTraversal(BT);
	cout << 'n';
	PTree temp;
	cout << "查找5这个元素的结果:";
	temp = Find(5, BT);
	if (NULL == temp)
	{
		cout << "未找到!n";
	}
	else
	{
		cout << temp->data << 'n';
	}
	cout << "查找3这个元素的结果:";
	temp = Find(3, BT);
	if (NULL == temp)
	{
		cout << "未找到!n";
	}
	else
	{
		cout << temp->data << 'n';
	}

	system("pause");
	return 0;
}

运行结果: