数据结构上机实验6.22

这篇博客介绍了二叉链表存储的二叉树创建与遍历,包括前序、中序和后序遍历。同时,讲解了图的深度优先遍历,用于遍历无向图的连通分量和生成树。此外,还涵盖了有序表的折半查找算法以及二叉排序树的构建和查找操作。通过实例代码展示了这些数据结构和算法的实现。
摘要由CSDN通过智能技术生成

目录

问题 A: 算法6-1~6-4:二叉链表存储的二叉树

问题 B: 图的遍历

代码

问题 C: 算法7-7,7-8:无向图的连通分量和生成树

代码

问题 D: 算法9-2:有序表的折半查找

代码

问题 E: 算法9-5~9-8:二叉排序树的基本操作

代码

问题 F: 二叉排序树

代码


问题 A: 算法6-1~6-4:二叉链表存储的二叉树

这一题同上一次作业的最后一题,直接copy过来

#include<iostream>
using namespace std;
struct TreeNode{
    struct TreeNode * lchild, *rchild;
    char val;
    TreeNode(char x): val(x), lchild(NULL), rchild(NULL){}
};
void create(TreeNode* &root){   	
	   char x;
	   if(scanf("%c",&x)!=EOF){	   
	   if(x == ' ')
             root = NULL;
         else{
                 root = new TreeNode(x);
                 create(root->lchild);
                 create(root->rchild);
		  }	}
}
void preorder(TreeNode* root){
    if(root){
        cout << root->val << " ";
        preorder(root->lchild);
        preorder(root->rchild);
    }
}
void inorder(TreeNode* root){
    if(root){
        
        inorder(root->lchild);
		cout << root->val << " ";
        inorder(root->rchild);
    }
}
void postorder(TreeNode* root){
    if(root){       
        postorder(root->lchild);		
        postorder(root->rchild);
		cout << root->val << " ";
    }
}
int main(){	
    TreeNode * root;//,* btree
    create(root);
    preorder(root);
    cout<<endl;
    inorder(root);
    cout<<endl;
    inorder(root);
    cout<<endl;
    return 0;
}

问题 B: 图的遍历

题目描述

深度优先搜索遍历类似于树的先根遍历,是树的先根遍历的推广。其过程为:假设初始状态是图中所有顶点未曾被访问,则深度优先搜索可以从图中的某个顶点v出发,访问此顶点,然后依次从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到;若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作为起始点,重复上述过程,直至图中所有顶点都被访问到为止。
其算法可以描述如下:

在本题中,读入一个无向图的邻接矩阵(即数组表示),建立无向图并按照以上描述中的算法遍历所有顶点,输出遍历顶点的顺序。
输入
输入的第一行包含一个正整数n,表示图中共有n个顶点。其中n不超过50。
以后的n行中每行有n个用空格隔开的整数0或1,对于第i行的第j个0或1,1表示第i个顶点和第j个顶点有直接连接,0表示没有直接连接。当i和j相等的时候,保证对应的整数为0。
输入保证邻接矩阵为对称矩阵,即输入的图一定是无向图。

输出

只有一行,包含n个整数,表示按照题目描述中的深度优先遍历算法遍历整个图的访问顶点顺序。每个整数后输出一个空格,并请注意行尾输出换行。

样例输入以及输出

4
0 1 0 1
1 0 0 0
0 0 0 1
1 0 1 0
0 1 3 2 

提示

在本题中,需要熟练掌握图的邻接矩阵存储方式。在建立完成无向图之后,需要严格按照题目描述的遍历顺序对图进行遍历。另外,算法中描述的FirstAdjVex函数和NextAdjVex函数,需要认真的自行探索并完成。

通过这道题目,应该能够对图的深度优先搜索建立更加直观和清晰的概念。
思路

考虑代码的简洁性(懒得写类,老师也没有发代码,于是就自己写了个邻接表用来简单的实现)

根据书上的类模板,找到设置二维数组的方法

在深度优先遍历的时候,注意邻接查询导致的回溯或者已经被查询过怎么处理数据

1.因为是无向图对称矩阵,在找邻接顶点的时候,注意对称的标识也应该清零以防重复访问

2.根据迷宫代码我们可知,可以设置一个visited数组来记录这个顶点我们访问过没

代码

#include<iostream>
using namespace std;
struct dfs{
	bool **edge;
};
void createdfs(int n,dfs &d){//注意这里是引用 
	d.edge=new bool*[n];
	for(int i=0;i<n;i++){
		d.edge[i]=new bool[n];
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++)
		{
		//cout<<i<<" "<<j;
			cin>>d.edge[i][j];
			}//cout<<d.edge[i][j];
	}
	//cout<<d.edge[0][0];
}
void outputdfs(int n,dfs d){
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++)
		{
			//cout<<"Sf"<<endl;
			cout<<d.edge[i][j]<<" ";
		}
			cout<<endl;
	}
}
int getfirstver(int n,dfs& d,int ptr,int*visited){
	for(int i=0;i<n;i++){
		if(d.edge[ptr][i]==1&&visited[i]==0){
			d.edge[ptr][i]=0;
			d.edge[i][ptr]=0;return i;
		}
		
	}
	return -1;
}
int getnextver(int n,dfs &d,int w,int *visited){
	for(int i=0;i<n;i++){
		if(d.edge[w][i]==1&&visited[i]==0)
		{
		//cout<<w<<" "<<i<<" "<<d.edge[w][i]<<endl;
		d.edge[w][i]=0;
		d.edge[i][w]=0;
		return i;}//循环的时候这个节点就过去了 
	}
	return -1;
} 
void sdfs(int n,dfs &d,int *&visited,int &ptr){
	int w=0;
	visited[ptr]=1;
	cout<<ptr<<" ";
	for(w=getfirstver(n,d,ptr,visited);w!=-1;w=getnextver(n,d,ptr,visited)){
	//cout<<"eaf"<<w<<endl; 	
		if(!visited[w]){
		
		sdfs(n,d,visited,w);}
	}
}
void dfstra(int n,dfs &d,int *&visited ,int ptr){
	for(ptr=0;ptr<n;ptr++){
		if(!visited[ptr]){		
		sdfs(n,d,visited,ptr);}
	}
} 
int main(){
	int n=0;
	cin>>n;
	dfs d;
	createdfs(n,d);
	//cout<<d.edge[1][0];
	//outputdfs(n,d);
	int *visited=new int[n];
	for(int i=0;i<n;i++){
		visited[i]=0;
	}
	int ptr=0;
	dfstra(n,d,visited,ptr);
	cout<<endl;
	/*int c=getfirstver(n,d,1);
	cout<<c<<endl;*///测试找第一个邻接结点
	/*int a=getnextver(n,d,1);
	for(;a!=-1;a=getnextver(n,d,1))
	cout<<"a"<<a<<endl;*///测试找下一个邻接结点 
	return 0;
}

问题 C: 算法7-7,7-8:无向图的连通分量和生成树

题目描述

在对无向图进行遍历时,对于连通图,仅需从图中任一顶点出发,进行深度优先搜索或广度优先搜索,便可访问到图中所有顶点。对于非连通图,则需从多个顶点出发进行搜索,而每一次从一个新的起始点出发进行搜索的过程中得到的顶点访问序列恰为其各个连通分量中的顶点集。

对于非连通图,每个连通分量中的顶点集,和遍历时走过的边一起构成若干棵生成树,这些连通分量的生成树组成非连通图的生成森林。

假设以孩子兄弟链表作为生成森林的存储结构,则生成非连通图的深度优先生成森林的算法可以描述如下

 而建立以p为根的深度优先生成树的算法可以描述如下

在本题中,读入一个无向图的邻接矩阵(即数组表示),建立无向图并按照以上描述中的算法建立无向图的生成森林。对于森林中的每一棵生成树,遍历所有顶点,并输出遍历顶点的顺序。

输入

输入的第一行包含一个正整数n,表示图中共有n个顶点。其中n不超过50。

以后的n行中每行有n个用空格隔开的整数0或1,对于第i行的第j个0或1,1表示第i个顶点和第j个顶点有直接连接,0表示没有直接连接。当i和j相等的时候,保证对应的整数为0。

输入保证邻接矩阵为对称矩阵,即输入的图一定是无向图。

输出

每一行输出无向图中的一棵生成树,表示按照题目描述中的深度优先遍历算法遍历相应的连通分量的访问顶点顺序。每个整数后输出一个空格,并请注意行尾输出换行。

输入样例

6
0 0 0 1 0 0
0 0 1 1 0 0
0 1 0 1 0 0
1 1 1 0 0 0
0 0 0 0 0 1
0 0 0 0 1 0

输出样例

0 3 1 2 
4 5 

提示

在本题中,需要掌握图的深度优先遍历的方法,并需要掌握无向图的连通性问题的本质。通过求出无向图的连通分量和对应的生成树,应该能够对图的连通性建立更加直观和清晰的概念。

思路

参考到上一题我们有一个深度优先遍历,只要在外层的函数的循环中做一个换行就好(依旧是没有用类的一题,有空会补的)

代码

#include<iostream>
using namespace std;
struct dfs{
	bool **edge;
};
void createdfs(int n,dfs &d){//注意这里是引用 
	d.edge=new bool*[n];
	for(int i=0;i<n;i++){
		d.edge[i]=new bool[n];
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++)
		{
		//cout<<i<<" "<<j;
			cin>>d.edge[i][j];
			}//cout<<d.edge[i][j];
	}
	//cout<<d.edge[0][0];
}
void outputdfs(int n,dfs d){
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++)
		{
			//cout<<"Sf"<<endl;
			cout<<d.edge[i][j]<<" ";
		}
			cout<<endl;
	}
}
int getfirstver(int n,dfs& d,int ptr,int*visited){
	for(int i=0;i<n;i++){
		if(d.edge[ptr][i]==1&&visited[i]==0){
			d.edge[ptr][i]=0;
			d.edge[i][ptr]=0;return i;
		}
		
	}
	return -1;
}
int getnextver(int n,dfs &d,int w,int*visited){
	for(int i=0;i<n;i++){
		if(d.edge[w][i]==1&&visited[i]==0)
		{
		//cout<<w<<" "<<i<<" "<<d.edge[w][i]<<endl;
		d.edge[w][i]=0;
		d.edge[i][w]=0;
		return i;}//循环的时候这个节点就过去了 
	}
	return -1;
} 
void sdfs(int n,dfs &d,int *&visited,int &ptr){
	int w=0;
	visited[ptr]=1;
	cout<<ptr<<" ";//<<"ptr"
	for(w=getfirstver(n,d,ptr,visited);w!=-1;w=getnextver(n,d,ptr,visited)){
	//cout<<"eaf"<<w<<endl; 	
		if(!visited[w]){
		//outputdfs(n,d);
		//cout<<endl;
		sdfs(n,d,visited,w);}
	}
}
void dfstra(int n,dfs &d,int *&visited ,int ptr){
	for(ptr=0;ptr<n;ptr++){
		if(!visited[ptr]){		
		sdfs(n,d,visited,ptr);
		//cout<<"ptr"<<ptr;
		cout<<endl;}
		
	}
} 
int main(){
	int n=0;
	cin>>n;
	dfs d;
	createdfs(n,d);
	//cout<<d.edge[1][0];
	//outputdfs(n,d);
	int *visited=new int[n];
	for(int i=0;i<n;i++){
		visited[i]=0;
	}
	int ptr=0;
	dfstra(n,d,visited,ptr);
	cout<<endl;
	/*int c=getfirstver(n,d,1);
	cout<<c<<endl;*///测试找第一个邻接结点
	/*int a=getnextver(n,d,1);
	for(;a!=-1;a=getnextver(n,d,1))
	cout<<"a"<<a<<endl;*///测试找下一个邻接结点 
	return 0;
}

问题 D: 算法9-2:有序表的折半查找

题目描述

用有序表表示静态查找表时,通常检索函数可以用折半查找来实现。

折半查找的查找过程是:首先确定待查记录所在的范围,然后逐步缩小范围直到找到或者确定找不到相应的记录为止。而每次需要缩小的范围均为上一次的一半,这样的查找过程可以被称为折半查找。

其查找过程可以描述如下:

在本题中,读入一串有序的整数,另外给定多次查询,判断每一次查询是否找到了相应的整数,如果找到则输出整数相应的位置。

输入

输入的第一行包含2个正整数n和k,分别表示共有n个整数和k次查询。其中n不超过1000,k同样不超过1000。

第二行包含n个用空格隔开的正整数,表示n个有序的整数。输入保证这n个整数是从小到大递增的。

第三行包含k个用空格隔开的正整数,表示k次查询的目标。

输出

只有1行,包含k个整数,分别表示每一次的查询结果。如果在查询中找到了对应的整数,则输出其相应的位置,否则输出-1。

请在每个整数后输出一个空格,并请注意行尾输出换行。

样例输入

8 3
1 3 5 7 8 9 10 15
9 2 5

输出

5 -1 2 

提示

在本题中,需要按照题目描述中的算法完成折半查找过程。通过将需要查询的值与当前区间中央的整数进行比较,不断缩小区间的范围,直到确定被查询的值是否存在。

通过课本中的性能分析部分,不难发现折半查找的时间复杂度为O(log2n),这是一种非常高效的查找方法。

思路

传统的二分查找法,oj上要注意多个同样的值要找第一次出现的时候

代码

#include<iostream>
using namespace std;
void search(int num[],int n,int x){
	int low=0;
	int hig=n-1;
	int mid=n/2;
	while(low<=hig){
		mid=(low+hig)/2;
		//cout<<"mid"<<mid<<endl;
		if(num[mid]>x) hig=mid-1;
		else if(num[mid]<x) low=mid+1;
		else if(num[mid]==x){
			if(num[mid-1]==x){
				hig=mid;
				continue;
			}
			cout<<mid<<" ";
			return;
		}
	}
	cout<<-1<<" "; 
	return ;
}
int main(){
	int num[1000],ki[1000];
	int n=0,k=0;
	cin>>n>>k;
	for(int i=0;i<n;i++){
		cin>>num[i];
	}
	int x=0;
	while(k){
		k--;
		cin>>x;
		search(num,n,x);
	}
	cout<<endl;
	return 0; 
}

问题 E: 算法9-5~9-8:二叉排序树的基本操作

题目描述

二叉排序树或者是一棵空树,或者是具有以下几条性质的二叉树:

1.       若它的左子树不空,则左子树上所有结点的值均小于它的根节点的值;

2.       若它的右子树不空,则右子树上所有结点的值均大于它的根节点的值;

3.       它的左右子树也分别为二叉排序树。

二叉排序树又可以被称为二叉查找树,根据上述定义的结构不难知道,它的查找过程十分简单,只需要通过不断的将当前结点的值与需要查找的值进行比较,如果相等则直接输出,如果要查找的值更小则深入至左子树进行比较,否则就深入右子树进行比较,直到找到相应的值或者进入了一棵不存在的子树为止。

其查找过程可以描述如下:

而其插入过程同样也十分简洁,可以描述如下

而删除操作可以描述为如下的两个算法

在本题中,读入一串整数,首先利用这些整数构造一棵二叉排序树。另外给定多次查询,利用构造出的二叉排序树,判断每一次查询是否成功。

输入

输入的第一行包含2个正整数n和k,分别表示共有n个整数和k次查询。其中n不超过500,k同样不超过500。

第二行包含n个用空格隔开的正整数,表示n个整数。

第三行包含k个用空格隔开的正整数,表示k次查询的目标。

输出

只有1行,包含k个整数,分别表示每一次的查询结果。如果在查询中找到了对应的整数,则输出1,否则输出0。

请在每个整数后输出一个空格,并请注意行尾输出换行。

样例输入

8 3
1 3 5 7 8 9 10 15
9 2 5

输出

1 0 1 

提示

在本题中,首先需要按照题目描述中的算法完成二叉排序树的构造过程,之后需要通过在二叉排序树中的不断向下查找,将需要查询的值与当前节点的值进行比较,直到确定被查询的值是否存在。

 

通过课本中的性能分析部分,不难发现二叉排序树的平均查找长度是和logn同数量级的,但是,在某些特殊情况下二叉排序树将会退化,使查找的效率大大降低,这时就需要引入二叉排序树的平衡操作,利用平衡二叉树来保证查找的效率始终维持在logn的数量级上。

思路

偷个懒,比根小的放根的左边,比根大的放根的右边

代码

#include<iostream>
using namespace std;
struct node{
	int data;
	node*uplink;
	node*llink;
	node*rlink;
	node(int x=0,node*u=NULL,node*l=NULL,node*r=NULL):data(x),uplink(u),llink(l),rlink(r){}
};
void insertnode(node*&root,int x){
	node*newnode=new node(x);
	if(root->data<x){
		newnode->rlink=root->rlink;
		newnode->uplink=root;
		if(root->rlink!=NULL)
		root->rlink->uplink=newnode;
		root->rlink=newnode; 
		
	}
	else if(root->data>x){
		newnode->llink=root->llink;
		newnode->uplink=root;
		if(root->llink!=NULL)
		root->llink->uplink=newnode;
		root->llink=newnode;
	}
}
void search(node*root,int x){
	node*ptr=root;
	if(root->data==x) cout<<1<<" ";
	else if(ptr->data>x){
		ptr=ptr->llink;
		while(ptr!=NULL){
			
			if(ptr->data==x)
			{
			cout<<1<<" ";
			break;
		}ptr=ptr->llink;
		}
		
		if(ptr==NULL) cout<<0<<" ";
	}
	else if(ptr->data<x){ptr=ptr->rlink;
		while(ptr!=NULL){
		
			if(ptr->data==x)
			{
			cout<<1<<" ";
			break;}	ptr=ptr->rlink;
		}//cout<<"aef"<<endl;
		if(ptr==NULL) cout<<0<<" ";
	}
}
void printnode(node*root){
	node*ptr=root;
	while(ptr!=NULL){
		cout<<ptr->data;
		ptr=ptr->llink;
	}
	ptr=root->rlink;
	while(ptr!=NULL){
		cout<<ptr->data;
		ptr=ptr->rlink;
	}
}
int main(){
	int n=0,k=0;
	cin>>n>>k;
	int x;
	cin>>x;
	node* root=new node(x);
	int m=n;
	//cin>>x;cout<<root->data;
	//insertnode(root,x);
	
	while(m-1){
		m--;
		cin>>x;
		insertnode(root,x);
	}
	//printnode(root);
	while(k){
		k--;
		cin>>x;
		search(root,x);
	}
	return 0;
}

问题 F: 二叉排序树

题目描述

输入一系列整数,建立二叉排序数,并进行前序,中序,后序遍历。

输入

输入第一行包括一个整数n(1<=n<=100)。接下来的一行包括n个整数。

输出

可能有多组测试数据,对于每组数据,将题目所给数据建立一个二叉排序树,并对二叉排序树进行前序、中序和后序遍历。每种遍历结果输出一行。每行最后一个数据之后有一个空格。

样例输入

1
2 
2
8 15 
4
21 10 5 39 

样例输出

2 
2 
2 
8 15 
8 15 
15 8 
21 10 5 39 
5 10 21 39 
5 10 39 21 

思路

摆脱类,整个根节点,然后照搬书上代码,爽啦

遇到过输出超限,想了想,在while判断那里一定要加!=EOF,至于原理,大佬们可以自行分析一下555菜鸡不会

代码

#include<iostream>
using namespace std;
struct node{
	int data;
	node*uplink;
	node*llink;
	node*rlink;
	node(int x=0,node*u=NULL,node*l=NULL,node*r=NULL):data(x),uplink(u),llink(l),rlink(r){}
};
void insert(node*&root,int x){
	if(root==NULL){
		root=new node(x);
	}
	else if(root->data>x) insert(root->llink,x);
	else if(root->data<x) insert(root->rlink,x);
}
void createnodetree(node*&root,int n){
	int x=0;
	cin>>x;
	root=new node(x);
	node*newnode;
	while(n-1){
		n--;
		cin>>x;
		if(root->data>x){
			insert(root->llink,x);
		}
		else if(root->data<x){
			insert(root->rlink,x);
		}
	}
}
void preorder(node*root){
	
    if(root!=NULL){
		cout<<root->data<<" ";
	preorder(root->llink);
	preorder(root->rlink);
	}
}
void inorder(node*root){
	if(root!=NULL){
		inorder(root->llink);
		cout<<root->data<<" ";
		inorder(root->rlink);
	}
} 
void postorder(node*root){
	if(root!=NULL){
		postorder(root->llink);
		postorder(root->rlink);
		cout<<root->data<<" ";
	}
}
int main(){
	int n=0;
	while(scanf("%d",&n)!=EOF){	
	node*root;
	createnodetree(root,n);
	preorder(root);
	cout<<endl;
	inorder(root);
	cout<<endl;
	postorder(root);
	cout<<endl;}
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值