二叉搜索树(BST)和平衡二叉树(AVL树)的概念与理解

树的典型应用

二叉搜索树

静态查找:所查找集合的元素时不动的,在一个集合上主要做的便是find操作,而没有delete一个映射的操作

方法:二分查找,将要查找的数据事先的有序化

动态查找:所查找对象会发生变化,经常要发生插入,删除操作

书上的任意的一个结点他的值比左子树的值都要来的大,比右子树的值都要小,查找过程便成为了对当前结点的判断。

二叉搜索树(BST)—二叉排序树/二叉查找树

  • 一颗二叉树,可以为空,如果不为空需满足
    • 非空左子树的所有键值小于其根结点的键值
    • 非空右子树的所有键值大于其根结点的键值
    • 左,右子树都是二叉搜索树
操作函数
  • Position Find(elementtype x,bintree bst):从二叉搜索树BST中查找元素X,返回其所在结点的地址
  • Position Findmin(bintree bst):从二叉搜索树BST中查找并返回最小元素所在结点的地址
  • Position Findmax(bintree bst):从二叉搜索树BST中查找并返回最大元素所在结点的地址
  • bintree insert(elementtype x,bintree bst),插入
  • bintree delete(elementtype x,bintree bst),删除
查找操作
  • 查找从根结点开始,如果树为空,返回NULL;
  • 若搜索树非空,则根结点关键字和X进行比较,并进行不同处理
    • 若X小于根结点键值,只需在左子树中继续搜索
    • 若X大于根节点的键值,在右子树中进行继续搜索
    • 若两者比较结果是相等,搜索完成,返回指向此结点的指针
//尾递归
bintree find(ELementtype x, bintree bst)
{
	if (!bst)
		return NULL;//查找从根结点开始,如果树为空,返回NULL;
	if (x > bst->data)
	{
		return find(x, bst->right);//若X大于根节点的键值,在右子树中进行继续搜索
	}
	if (x < bst->data)
	{
		return find(x, bst->left);//若X小于根结点键值,只需在左子树中继续搜索
	}
	else
		return bst;//若两者比较结果是相等,搜索完成,返回指向此结点的指针
}

由于非递归函数的执行效率高,可将尾递归函数改为迭代函数

//递归实现版
bintree iterfind(ELementtype x, bintree bst)
{
	while (bst)
	{
		if (x > bst->data)
		{
			bst=bst->right;//若X大于根节点的键值,在右子树中进行继续搜索
		}
		if (x < bst->data)
		{
			bst = bst->left;//若X小于根结点键值,只需在左子树中继续搜索
		}
		else
			return bst;//若两者比较结果是相等,搜索完成,返回指向此结点的指针
	}
	return NULL;//查找从根结点开始,如果树为空,返回NULL;
}

查找的效率决定于树的高度

查找最大和最小元素
  • 最大元素一定是在树的最右分支的端结点上
  • 最小元素一定是在树的最左分支的端结点上
//递归
bintree findmin(bintree bst)
{
	if (!bst)
		return NULL;
	else if (!bst->left)
		return bst;//一直查找有没有左节点,左结点如果没有代表就是最小结点所在的位置
	else
		return findmin(bst->left);
}

//循环
bintree findmax(bintree bst)
{
	if (bst)
	{
		while (bst->right)
		{
			bst = bst->right;
		}
	}
	return bst;
}
二叉搜索树插入

分析:关键是要找到元素应该插入的位置

bintree insert(ELementtype x, bintree bst)
{//若原树为空,生成并返回一个结点的二叉搜索树
	if (!bst)
	{
		bst =(bintree)malloc(sizeof(struct treenode));
		bst->data = x;
		bst->left = bst->right = NULL;
	}
	else {//开始查找要插入元素的位置
		if (x < bst->data)
		{
			bst->left = insert(x, bst->left);
		}
		else if (x > bst->data)
		{
			bst->right = insert(x, bst->right);
		}
		//当找到了便将x赋给新建的空结点中
	}
	return bst;
}

二叉搜索树的删除

  • 要删除的是叶结点,直接删除,再修改其父结点指针,置为NULL
  • 要删除的结点只有一个孩子结点:将其父结点的指针指向要删除结点的孩子结点
  • 要删除的结点有左右两颗子树,用另一结点替代被删除结点,右子树的最小元素或则左子树的最大元素,原因,无论左子树的最小值或者右子树的最小值一定不会有两个结点
bintree deletetree(ELementtype x, bintree bst)
{
	bintree tmp;
	if (!bst)
	{
		cout << "we can`t finf the num" << endl;
	}
	else if (x > bst->data)
	{
		bst->right = deletetree(x, bst->right);//左子树递归删除
	}
	else if (x < bst->data)
	{
		bst->left = deletetree(x, bst->left);//右子树递归删除
	}
	else
	{
		if (bst->left && bst->right)//左右两边都不空
		{
			tmp = findmin(bst->right);//找到右子树的最小值进行替代
			bst->data = tmp->data;
			bst->right = deletetree(bst->data, bst->right);
		}
		else
		{//被删除结点有一个或无子结点
			tmp = bst;
			if (!bst->left)//有右子树或无子结点
			{
				bst = bst->right;
			}
			else if (!bst->right)//有左子树或无子结点
			{
				bst = bst->right;
			}
			free(tmp);
		}
	}
	return bst;
}

平衡二叉树

搜索树结点的不同插入次序,会导致不同的深度平均查找长度ASL不同

左右两边结点数差不多,左右两边的高度差不多

  • 平衡因子(BF),BF(T)=hL-hR
  • hL,hR分别是T的左右子树的高度

平衡二叉树(AVL树)

任一结点的左右子树高度差的绝对值不超过1,即|BF(T)|<=1

高度为h的最小结点数=高度为h-1时最小结点数+高度为h-2时最小结点数+1

类似于斐波那契数列,nh=f(h+2)-1

给定结点数为n的AVL树的最大高度为O(log2(n))

平衡二叉树的调整

右旋

无论如何调整,都必须保证搜索树的框架能够成立

RR旋转,RR插入,右单旋

LL旋转,LL插入,左单旋

LR旋转,LR插入,左右旋转

RL旋转,RL插入

怎么判别:便是看插入结点把谁给破坏了

小白专场

题目:判断是否为同一颗二叉搜索树

给定一个插入序列可以唯一确定一颗二叉搜索树,然而一颗给定的二叉搜索树可以由多种不同的插入序列得到

对于输入的各种个插入序列,需要判断他们是否可以生成一样的二叉搜索树

求解思路
  • 分别建两颗搜索树的判别方法
  • 不建树的判别方法
  • 建一颗树,再判别其他序列是否与该树一致
    • 搜索树表示
    • 建搜索树T
    • 判别一序列是否与搜索树T一致
程序框架搭建
主函数
  • 读取N和L
  • 根据第一行序列建树T
  • 依据树T分别判别后面的L个序列是否能与T形成同一搜索树并输出结果
  • 主要建立两个函数,读入数据建立搜索树T,判别序列是否与T构成一样的搜索树
判别方法

如何判别序列与树t是否一致

方法:再树t中顺序搜索序列中的每个数

  • 若每次搜索所经过的结点再前面均出现过,则一致
  • 否则(某次搜索中遇到前面你未出现过的结点),则不一致
#include <stdio.h>
#include <stdlib.h>
#include<iostream>

using namespace std;

typedef struct Treenode* tree;

//建立搜索树
struct Treenode
{
	int v;
	tree left, right;
	int flag;//用来判别一个序列是不是与树一致,作为结点有无被访问过的标记
};

int main()
{
	int N,L,i;
	tree T;
	cin >> N;
	while (N)
	{
		cin >> L;
		T = maketree(N);
		for (i = 0; i < L; i++)
		{
			if (judge(T,N))
			{
				cout << "yes" << endl;
			}
			else
			{
				cout << "no" << endl;
			}
			resetT(T);//清除T中的标记flag
		}
		freetree(T);
		cin >> N;
	}
	return 0;


}

tree maketree(int n)
{
	tree t;
	int i, v;
	cin >> v;
	t = newnode(v);
	for (i = 1; i < n; i++)
	{
		cin >> v;
		t = insert(t, v);
	}
	return t;
}

tree newnode(int v)
{
	tree t = (tree)malloc(sizeof(struct Treenode));
	t->v = v;
	t->left = t->right = NULL;
	t->flag = 0;
	return t;
}

tree insert(tree t, int v)
{
	if (!t)
		t = newnode(v);
	else {
		if (v > t->v)
		{
			t->right = insert(t->right, v);
		}
		else
			t->left = insert(t->left, v);
	}
	return t;
}


int check(tree t, int v)
{
	if (t->flag)
	{
		if (v < t->v)
			return check(t->left, v);
		else if (v > t->v)
			return check(t->right, v);
		else
			return 0;
	}
	else
	{
		if (v == t->v)
		{
			t->flag = 1;
			return 1;
		}
		else
			return 0;
	}
}

//当发现序列中的某个数与T不一致时,必须把该序列后面的数全部都读完
int judge(tree t, int n)
{
	int i, v,flag = 0;
	//flag=0,代表目前还是一致的,1代表已经不一致了
	cin >> v;
	if (v != t->v)
		flag = 1;
	else
		t->flag = 1;
	for (i = 1; i < n; i++)
	{
		cin >> v;
		if (!flag && !check(t, v))
			flag = 1;
	}
	if (flag)
		return 0;
	else
		return 0;
}

void resetT(tree t)//清除t中的每个结点的flag标记
{
	if (t->left)
		resetT(t->left);
	if (t->right)
		resetT(t->right);
	t->flag = 0;
}

void freetree(tree t)//释放结点
{
	if (t->left)
		freetree(t->left);
	if (t->right)
		freetree(t->right);
	free(t);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值