《算法笔记》学习记录 Part 3 树(中)

第九章    数据结构专题 —— 树(中)

9.4 二叉查找树(BST)

9.4.1 BST的递归定义:

1)要么二叉查找树是一颗空数

2)要么二叉查找树由根节点、左子树、右子树组成,其中左子树和右子树都是二叉查找树,且左子树上所有结点的数据域均小于或等于根节点的数据域,右子树上所有结点的数据域均大于根结点的数据域。

9.4.2 BST的基本操作
查找操作
void search(node* root,int x){
	if(root==NULL){
		printf("search failed");
	}
	if(x==root->data){
		printf("%d\n",root->data);
	}else if(x<root->data){	//如果x比根结点的数据域小,说明x在左子树
		search(root->lchild,x);
	}else{					//如果x比根结点的数据域大,说明x在右子树
		search(root->rchild,x);
	}
}
插入操作
void insert(node* &root,int x){
	if(root==NULL){	//空树,说明查找失败,即插入位置
		root==newNode(x);
		return;
	}
	if(x==root->data){	//查找成功,说明结点已存在,直接返回
		return;
	}else if(x<root->data){		
		insert(root->lchild,x);
	}else{
		insert(root->rchild,x);
	}
}
二叉树的建立,但是同一组相同的数字,如果插入顺序不同,最后生成的二叉树也可能不同
node* create(int data[],int n){
	node* root = NULL;
	for(int i=0;i<n;i++){
		insert(root,data[i]);
	}
}
二叉查找树的删除

把以二叉查找树中比结点权值小的最大结点称为该结点的前驱,而把比结点权值大的最小结点称为该结点的后继。显然,结点的前驱是该结点左子树的最右结点,右子树同理

//寻找以root为根节点的树中的最大权值
node* findMax(node* root){
	while(root->rchild!=NULL){
		root=root->rchild;
	}
	return root;
}
//寻找以root为根结点的树种的最小权值结点
node* findMin(node* root){
	while(root->lchild!=NULL){
		root=root->lchild;
	}
	return root;
}
//假设决定用结点N的前驱P来替换N,于是就把问题转化为在N的左子树中删除结点P,就可以递归下去了
//直到递归到一个叶子结点,就可以直接把他删除了
void deleteNode(node* &root,int x){
	if(root==NULL) return;
	if(root->data == x){
		if(root->lchild==NULL&&root->rchild==NULL){
			root=NULL;	//叶子结点直接删除,把root地址设为NULL,父结点就找不到它了
		}else if(root->lchild != NULL){
			node* pre = findMax(root->lchild);	//找root前驱
			root->data = pre->data;				//用前驱覆盖root
			deleteNode(root->lchild,pre->data);
		}else{
			node* next = findMin(root->rchild);	//找root后继
			root->data = next->data;				//用后继覆盖root
			deleteNode(root->rchild,next->data);	//在右子树中删除结点next
		}
	}else if(root->data > x){
		deleteNode(root->lchild,x);
	}else{
		deleteNode(root->rchild,x);
	}
}
9.4.3 二叉查找树的性质

对于二叉查找树进行中序遍历,遍历的结果是有序的


9.5 平衡二叉树(AVL)

9.5.1 平衡二叉树的定义

对AVL树的任意结点来说,其左子树和右子树的高度之差的绝对值不超过1,其中左右子树的高度之差称为该结点的平衡因子  

struct node{
	int v,height;	//v为结点权值,height为当前子树高度
	node *lchild,*rchild;
};

node* newNode(int v){
	node* Node = new node;
	Node->v = v;
	Node->height = 1;	//结点初始高度为1
	Node->lchild=Node->rchild = NULL;
	return Node;
}

//获取以root为根结点的子树当前height
int getHeight(node* root){
	if(root==NULL) return 0;
	return root->height;
}

//计算结点root的平衡因子
int getBalanceFactor(node* root){
	return getHeight(root->lchild) - getHeight(root->rchild);
}

//更新结点root的height
void updateHeight(node* root){
	root->height = max(getHeight(root->lchild),getHeight(root->rchild))+1;
}
9.5.2 平衡二叉树的基本操作

查找操作,时间复杂度为O(logn)

void search(node* root,int x){
	if(root==NULL){
		cout<<"search failed"<<endl;
		return;
	}
	if(x==root->data){
		cout<<root->data<<endl;	//查找成功,访问
	}else if(x<root->data){
		search(root->lchild,x);
	}else{
		search(root->rchild,x);
	}
}

插入操作 

void insert(node* &root,int v){
	if(root == NULL){
		root = newNode(v);
		return;
	}
	if(v < root->data){ 	//v比根结点的权值小
		insert(root->lchild,v);	//  往左子树插入
		updateHeight(root); 		// 	更新树高
		if(getBalanceFactor(root) == 2){
			if(getBalanceFactor(root->lchild) == 1){		//LL型
				R(root);
			}else if(getBalanceFactor(root->lchild) == -1){	//LR型
				L(root->lchild);
				R(root);
			}
		}
	}else{			//v比根结点的权值大
		insert(root->rchild,v);
		updateHeight(root);	//更新树高
		if(getBalanceFactor(root) == -2){
			if(getBalanceFactor(root->rchild) == -1){	//RR型
				L(root);
			}else if(getBalanceFactor(root->rchild) == 1){ //RL型
				R(root->rchild);
				L(root);
			}
		}
	}
}

AVL树的建立

node* create(int data[],int n){
	node* root = NULL;
	for(int i=0;i<n;i++){
		insert(root,data[i]);
	}
	return root;
}

9.6 并查集

并查集(Union Find Set),用一个数组 int father[N] ,father[i]表示元素i的父结点,例如father[1]=2就表示元素1的父结点是元素2,以这种父亲关系来表示元素所属的集合。另外,如果father[i] == i,则说明元素i是该集合的根节点,但对同一个集合来说只存在一个根结点,且将其作为所属集合的标识。

9.6.1 并查集的操作

初始化

//1.初始化,一开始,每个元素都是独立的一个集合,因此需要令所有father[i]等于i
for(int i=1;i<=N;i++){
	father[i]=i;
}

查找

由于规定同一个集合中只存在一个根结点,因此查找操作就是对给定的结点寻找其根结点的过程。可以用递归或者递推实现

//递推,返回元素x所在集合的根结点
int findFather(int x){
	while(x != father[x]){
		x = father[x];
	}
	return x;
}

//递归
int find_father(int x){
	if(x == father[x]) return x;	  //如果找到根结点,则返回根结点编号x
	else return find_father(father[x]);	//否则,递归判断x的父亲结点是否是根结点
}

合并

合并是指把两个集合合并成一个集合,题目中一般给出两个元素,要求把这两个元素所在的集合合并。

void Union(int a,int b){
	int faA = findFather(a);
	int faB = findFather(b);
	if(faA!=faB){	//如果不属于一个集合
		father[faA] = faB;	//合并他们
	}
}

在合并过程中,只对两个不同的集合进行合并,如果两个元素在相同的集合中,那么就不会对它们进行操作。这就保证了在同一个集合中一定不会产生环,即并查集产生的每一个集合都是一颗树

9.6.2 路径压缩

把当前查询结点的路径上的所有结点的父亲都指向根结点,查找的时候就不需要一直回溯去找父亲了,查询的复杂度可以降为O(1)

步骤:1.按原先写法获得x的根结点r。  2.重新从x开始走一遍寻找根结点的过程,把路径上经过的所有结点的父亲全部改为根结点r

//压缩路径
int find_Father(int x){
	int a = x;	//由于x在下面的while中会变成根结点,因此先把原先的x保存一下
	while(x != father[x]){
		x = father[x];
	}
	//到这里,x存放的是根结点。下面把路径上的所有结点的father改成根结点
	while(a != father[a]){
		int z = a;	//因为a要被father[a]覆盖,所以先保存a的值,以修改father[a]
		a = father[a];	//a回溯父亲结点
		father[z] = x;	//将原先的结点a的父亲改为根结点
	}
	return x;	//返回根结点
}

//压缩路径,递归
int Find_father(int x){
	if(x == father[x]) return x;		//找到根结点
	else{
		int F = Find_father(father[x]);	//递归寻找father[x]的根结点F
		father[x] = F;	//将根节点F赋给father[x]
		return F;	//返回根结点F
	}
}





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猪突猛进!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值