【挑战程序设计竞赛】二叉搜索树

插入

题目

请编写一个程序,对二叉搜索树T执行以下命令:
insert k:插入键值key
print:分别用树的中序遍历和前序遍历算法输出键值

思路

insert以根为起点寻找结点z该插入的位置。设当前结点x,如果z的键值小于x则将当前结点的左子结点作为下一个x,反之则以右为下一个,不断向叶结点搜索。
程序将前一个节点保存在y里,用作z的候选父结点。当x抵达nil时搜索结束,此时y就是z的父结点。
如果搜索结束时y仍然为NIL,就代表插入前的二叉树为空,z即成为根结点。如果不为空,则z为y的左子结点或右子结点。

代码

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <string>
using namespace std; 

struct Node{
	int key;
	Node *parent,*right,*left;
};

Node *root,*NIL;

void insert(int k){
	Node *y=NIL;
	Node *x=root;
	Node *z;
	
	z=(Node*)malloc(sizeof(Node));
	z->key=k;
	z->left=NIL;
	z->right=NIL;
	
	while(x!=NIL){
		y=x;
		if(z->key<x->key){
			x=x->left;
		}
		if(z->key>x->key){
			x=x->right;
		}
	}
	
	z->parent=y;
	if(y==NIL){
		root=z;
	}else{
		if(z->key<y->key){
			y->left=z;
		}else{
			y->right=z;
		}
	}
}
void inorder(Node *u){
	if(u==NIL) return;
	inorder(u->left);
	printf(" %d",u->key);
	inorder(u->right);
}
void preorder(Node *u){
	if(u==NIL) return;
	printf(" %d",u->key);
	preorder(u->left);
	preorder(u->right);
}

int main(){
	int n,i,x;
	string com;
	
	scanf("%d",&n);
	
	for(i=0;i<n;i++){
		cin>>com;
		if(com == "insert"){
			scanf("%d",&x);
			insert(x);
		}else if(com == "print"){
			inorder(root);
			printf("\n");
			preorder(root);
			printf("\n");
		}
	}
	
	return 0;
}

搜索

题目

请编写一个程序,在Binary Search Tree的基础上添加find命令。

思路

以根为起点调用find,从根向叶搜索结点。如果给定键值小于当前结点x的键值,那么搜索目标移至左子结点继续搜搜,反之移动到右子结点。

代码

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <string>
using namespace std; 

struct Node{
	int key;
	Node *parent,*right,*left;
};

Node *root,*NIL;

Node *find(Node *u,int k){
	while(u!=NIL && k!=u->key){
		if(k<u->key) u=u->left;
		else u=u->right;
	}
	return u;
} 

void insert(int k){
	Node *y=NIL;
	Node *x=root;
	Node *z;
	
	z=(Node*)malloc(sizeof(Node));
	z->key=k;
	z->left=NIL;
	z->right=NIL;
	
	while(x!=NIL){
		y=x;
		if(z->key<x->key){
			x=x->left;
		}
		if(z->key>x->key){
			x=x->right;
		}
	}
	
	z->parent=y;
	if(y==NIL){
		root=z;
	}else{
		if(z->key<y->key){
			y->left=z;
		}else{
			y->right=z;
		}
	}
}
void inorder(Node *u){
	if(u==NIL) return;
	inorder(u->left);
	printf(" %d",u->key);
	inorder(u->right);
}
void preorder(Node *u){
	if(u==NIL) return;
	printf(" %d",u->key);
	preorder(u->left);
	preorder(u->right);
}

int main(){
	int n,i,x;
	string com;
	
	scanf("%d",&n);
	
	for(i=0;i<n;i++){
		cin>>com;
		if(com[0] == 'f'){
			scanf("%d",&x);
			Node *t=find(root,x);
			if(t!=NIL) printf("yes\n");
			else printf("no\n");
		}else if(com == "insert"){
			scanf("%d",&x);
			insert(x);
		}else if(com == "print"){
			inorder(root);
			printf("\n");
			preorder(root);
			printf("\n");
		}
	}
	
	return 0;
}

删除

题目

在上面的基础上,添加delete命令。

思路

在这里插入图片描述
在这里插入图片描述

代码

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <string>
using namespace std; 

struct Node{
	int key;
	Node *parent,*right,*left;
};

Node *root,*NIL;

Node* treeMinimum(Node *x){
	while(x->left != NIL) x=x->left;
	return x;
} 

Node *find(Node *u,int k){
	while(u!=NIL && k!=u->key){
		if(k<u->key) u=u->left;
		else u=u->right;
	}
	return u;
} 
/*
  当x存在右子结点时,右子树中键值最小的结点即为x的后一个结点,因此返回getMinimun(x->right)。不存在右
子结点,要向上查询父结点,第一个“以左子结点身份出现的父结点”就是x的后一个结点。 
*/
Node* treeSuccessor(Node *x){
	if(x->right != NIL) return treeMinimum(x->right);
	Node* y=x->parent;
	while(y != NIL && x==y->right){
		x=y;
		y=y->parent;
	}
	return y;
}

void treeDelete(Node *z){
	Node *y;//要删除的结点 
	Node *x;//y的子结点
	
	/*case1 & case2 */ 
    //确定要删除的结点
	//z没有子结点或者有一个子结点,y就是z。 
	if(z->left == NIL || z->right == NIL) y=z;
	else y=treeSuccessor(z);
	/*case3:*/ 
	//确定y的子结点x
	if(y->left!=NIL){
		x=y->left;
	}else{
		x=y->right;
	}
	//改变y父/子结点的指针,然后删除y。
	//首先让x的父结点指针指向y的父结点。 
	if(x!=NIL){
		x->parent=y->parent;
	}
	//随后改变该父结点原先指向y的指针,使x成为父结点的子结点。
	//这里在更改指针前,先要检查结点y是根节点,还是其父结点的左/右子结点 
	if(y->parent==NIL){
		root=x;
	}else{
		if(y==y->parent->left){
			y->parent->left=x;
		}else{
			y->parent->right=x;
		}
	}
	//最后,如果处理的是case3,则需要将y的键值赋值给z。 
	if(y!=z){
		z->key=y->key;
	}
	//删除y结点 
	free(y);
} 

void insert(int k){
	Node *y=NIL;
	Node *x=root;
	Node *z;
	
	z=(Node*)malloc(sizeof(Node));
	z->key=k;
	z->left=NIL;
	z->right=NIL;
	
	while(x!=NIL){
		y=x;
		if(z->key<x->key){
			x=x->left;
		}
		if(z->key>x->key){
			x=x->right;
		}
	}
	
	z->parent=y;
	if(y==NIL){
		root=z;
	}else{
		if(z->key<y->key){
			y->left=z;
		}else{
			y->right=z;
		}
	}
}
void inorder(Node *u){
	if(u==NIL) return;
	inorder(u->left);
	printf(" %d",u->key);
	inorder(u->right);
}
void preorder(Node *u){
	if(u==NIL) return;
	printf(" %d",u->key);
	preorder(u->left);
	preorder(u->right);
}

int main(){
	int n,i,x;
	string com;
	
	scanf("%d",&n);
	
	for(i=0;i<n;i++){
		cin>>com;
		if(com[0] == 'f'){
			scanf("%d",&x);
			Node *t=find(root,x);
			if(t!=NIL) printf("yes\n");
			else printf("no\n");
		}else if(com == "insert"){
			scanf("%d",&x);
			insert(x);
		}else if(com == "print"){
			inorder(root);
			printf("\n");
			preorder(root);
			printf("\n");
		}else if(com=="delete"){
			scanf("%d",&x);
			treeDelete(find(root,x));
		} 
	}
	
	return 0;
}

通过标准库管理集合

管理元素集合的STL容器大致分为两类。一类是有顺序的集合,称为序列式容器;另一类是经过排序的集合,称为关联式容器。
序列式容器会将新添加的元素置于特定位置,该位置由插入事件和地点决定,与元素本身的值无关。如:vector、list
关联式容器会依据特定的排序标准来决定要添加的元素的位置。如set、map、multiset、multimap容器。

set

使用实例

#include <iostream>
#include <set>
using namespace std;

void print(set<int> S){
	cout<<S.size()<<":";
	for(set<int>:: iterator it=S.begin();it!=S.end();it++){
		cout<<" "<<(*it);
	}
	cout<<endl;
}

int main(){
	set<int> S;
	
	S.insert(8);
	S.insert(1);
	S.insert(7);
	S.insert(4);
	S.insert(8);
	S.insert(4);
	
	print(S);//1 4 7 8 
	
	S.erase(7);
	
	print(S);
	
	S.insert(2);
	
	print(S);
	
	if(S.find(10)==S.end()) cout<<"not found"<<endl;
	
	return 0;
}

成员函数示例

函数名功能
size( )返回set中的元素数
clear( )清空set
begin( )返回指向set开头的迭代器
end( )返回指向set末尾的迭代器
insert(key)插入元素key
erase(key)删除含有key的元素
find(key)搜索与key一致的元素,并返回指向该元素的迭代器,没有与key一致的元素就返回末尾end()

map

map集合以键与值的组合为元素,每个元素拥有1个键和1个值,集合以键作为排序目标,集合中各元素的键唯一,不存在重复,可以看作是一种能使用任意类型下标的关联式容器。

使用示例

#include <iostream>
#include <map>
#include <string>
using namespace std;

void print(map<string, int> T){
	map<string, int>::iterator it;
	cout<<T.size()<<endl;
	for(it=T.begin();it!=T.end();it++){
		pair<string, int> item=*it;
		cout<<item.first<<"-->"<<item.second<<endl;
	}
}

int main(){
	map<string, int> T;
	
	T["red"]=32;
	T["blue"]=688;
	T["yellow"]=122;
	
	T["blue"]+=132;
	
	print(T);
	
	T.insert(make_pair("zebra",101010));
	T.insert(make_pair("white",0));
	T.erase("yellow");
	
	print(T);
	
	pair<string, int> target=*T.find("red");
	cout<<target.first<<"-->"<<target.second<<endl;
	
	return 0;
}

成员函数示例

函数名功能
size( )返回map中的元素数
clear( )清空map
begin( )返回指向map开头的迭代器
end( )返回指向map末尾的迭代器
insert((key,val))插入元素(key,val)
erase(key)删除含有key的元素
find(key)搜索与key一致的元素,并返回指向该元素的迭代器,没有与key一致的元素就返回末尾end()
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值