【数据结构】实验十二:图 查找

news2024/11/17 4:23:52

实验十二  图+查找

一、实验目的与要求

1)掌握拓扑排序的应用;

2)掌握查找的概念和算法;

3)掌握查找的基本原理以及各种算法的实现;

4)掌握查找的应用。

二、实验内容

1.  用邻接表建立一个有向图的存储结构。利用拓扑排序算法输出该图的拓扑排序序列。

2. 0n-1中缺失的数字

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0n-1之内。在范围0n-1内的n个数字中有且只有一个数字不在该数组中,请使用折半查找算法找出这个数字。

示例 1:

输入: [0,1,3]

输出: 2

示例 2:

输入: [0,1,2,3,4,5,6,7,9]

输出: 8

三、实验结果

1)请将调试通过的主要源代码、输出结果粘贴在下面(必要的注释、Times New Roman 5号,行间距1.5倍)

2)简述算法步骤(选画技术路线图),格式如下:

S1:

S2:

3)请分析算法的时间复杂度。

4)请将源代码(必要的注释)cpp文件压缩上传


题目1

1.1)实验源代码

#include <iostream>
using namespace std;
//拓扑排序 
#define MVNum 100
typedef char VerTexType;

//图的邻接表存储表示
typedef struct ArcNode{
    int adjvex;  
    struct ArcNode *nextarc;	
}ArcNode; 

typedef struct VNode{ 
    VerTexType data;
    ArcNode *firstarc;//指向第一条依附该顶点的边的指针 
}VNode, AdjList[MVNum];

typedef struct{ 
    AdjList vertices;//邻接表 
	AdjList converse_vertices;//逆邻接表
    int vexnum, arcnum;//图的当前顶点数和边数 
}ALGraph;

//定义顺序栈
typedef struct{
	int *base;
	int *top;
	int stacksize;
}spStack;

int indegree[MVNum];//存放个顶点的入度
spStack S;

//初始化栈
void InitStack(spStack &S){
	S.base = new int[MVNum];
	if(!S.base)
		exit(1);
	S.top = S.base;
	S.stacksize = MVNum;
}

//进栈 
void Push(spStack &S , int i){
	if(S.top - S.base == S.stacksize){
		return;
	}
	*S.top++ = i;
}

//出栈 
void Pop(spStack &S , int &i){
	if(S.top == S.base){
		return;
	}
	i = *--S.top;
}

//判断空栈 
bool StackEmpty(spStack S){
	if(S.top == S.base){
		return true;
	}
	return false;
}

//确定点v在G中的位置
int LocateVex(ALGraph G , VerTexType v){
	for(int i = 0; i < G.vexnum; ++i){
		if(G.vertices[i].data == v){
			return i;
		}
	}
	return -1;
}

//创建有向图G的邻接表、逆邻接表
int CreateUDG(ALGraph &G){
	int i , k;
	cout <<"请输入总顶点数,总边数:";
	cin >> G.vexnum >> G.arcnum;
    cout << endl;
	cout << "输入点的名称:" << endl;
	for(i = 0; i < G.vexnum; ++i){
		cout << "请输入第" << (i+1) << "个点的名称:";
		cin >> G.vertices[i].data;
		G.converse_vertices[i].data = G.vertices[i].data;
		G.vertices[i].firstarc=NULL;//初始化表头结点的指针域为NULL 
		G.converse_vertices[i].firstarc=NULL;
    }
	cout << endl;
	cout << "输入边依附的顶点:" << endl;
	for(k = 0; k < G.arcnum;++k){
		VerTexType v1 , v2;
		int i , j;
		cout << "请输入第" << (k + 1) << "条边依附的顶点:";
		cin >> v1 >> v2;
		i = LocateVex(G, v1);  
		j = LocateVex(G, v2);//确定v1和v2在G中位置,即顶点在G.vertices中的序号 
		ArcNode *p1=new ArcNode;//生成一个新的边结点*p1 
		p1->adjvex=j;//邻接点序号为j
		p1->nextarc = G.vertices[i].firstarc;
		G.vertices[i].firstarc=p1;//将新结点*p1插入顶点vi的边表头部
		ArcNode *p2=new ArcNode;
		p2->adjvex=i;
		p2->nextarc = G.converse_vertices[j].firstarc;
		G.converse_vertices[j].firstarc=p2;
    }
    return 1; 
}

//求入度
void FindInDegree(ALGraph G){
	int i , count;
	for(i = 0 ; i < G.vexnum ; i++){
		count = 0;
		ArcNode *p = G.converse_vertices[i].firstarc;
		if(p){
			while(p){
				p = p->nextarc;
				count++;
			}
		}
		indegree[i] = count;
	}
}

//有向图G采用邻接表存储结构,若G无回路,则生成G的一个拓扑序列topo[]并,否则ERROR 
int TopologicalSort(ALGraph G , int topo[]){
	int i , m;
    FindInDegree(G);
    InitStack(S);
    for(i = 0; i < G.vexnum; ++i){
    	if(!indegree[i]){
    		Push(S, i); //入度为0者进栈
		}
	}
	m = 0;//对输出顶点计数,初始为0 
	while(!StackEmpty(S)){
		Pop(S, i);//将栈顶顶点vi出栈
		topo[m]=i;//将vi保存在拓扑序列数组topo中 
		++m;//对输出顶点计数 
		ArcNode *p = G.vertices[i].firstarc;//p指向vi的第一个邻接点 
		while(p){
			int k = p->adjvex;//vk为vi的邻接点   
			--indegree[k];//vi的每个邻接点的入度减1 
			if(indegree[k] ==0){
				Push(S, k); //若入度减为0,则入栈
			}  
			p = p->nextarc;//p指向顶点vi下一个邻接结点 
		}
	}
	if(m < G.vexnum){
		return 0;
	}
	return 1;
}

int main(){
	ALGraph G;
	CreateUDG(G);
	int *topo = new int [G.vexnum];
	cout << endl;
	cout << "有向图的邻接表、逆邻接表创建完成" << endl<< endl;
	if(TopologicalSort(G , topo)){
		cout << "该有向图的拓扑有序序列为:";
		for(int j = 0 ; j < G.vexnum; j++){
			if(j != G.vexnum - 1){
				cout << G.vertices[topo[j]].data << " , ";
			}
			else{
				cout << G.vertices[topo[j]].data << endl << endl;
			}
		}
	}
	else{
		cout << "网中存在环,无法进行拓扑排序!" <<endl;
	}
	return 0;
}

1.2)测试用例及其输出结果

测试用例1

 

程序运行结果1

 

测试用例2

程序运行结果2

 

(2)简述算法步骤

S1:构建邻接表结构。

 

S2:构建栈,并定义初始化栈、进栈、出栈、判断空栈这四个函数体。初始化栈时,创建一个新的数组存放数据,并令顶指针为初始位置,栈空间大小为最大数。进栈时,先判断是否栈满,如果栈已满则直接return,如果栈未满则顶指针存入数据后后置++。出栈时,先判断是否栈空,如果栈空则直接return,如果栈非空则顶指针下移一位后弹出存放的数据。判断空栈时,如果顶指针和底指针相等,则当前栈为空栈,否则当前栈非空。

S3:构建“确定节点v在邻接表中的位置”函数。主要通过for循环遍历G,如果当前节点数据与目标节点v匹配,则返回当前节点的位置。如果循环结束后依然未匹配,则返回-1数值,表示节点v不存在于有向图G中。

S4:构建“创建有向图G的邻接表和逆邻接表”函数。首先需要输入总顶点数和总边数、各节点的名称、各边依附的节点信息。通过输入各节点的名称时生成邻接表和逆邻接表,并将指向第一条依附该节点的边的指针置空。接着通过输入各边依附的节点信息时,确定两个节点在有向图G中的位置,并生成两个个临时新边界点p1和p2,令p1、p2的邻接点序号分别为该边第二个节点的位置和第一个节点的位置,然后令p1、p2的下一条边为另外一个节点的第一条边指针。

S4:构建“求入度”函数。通过遍历节点,设置临时边指针p为逆邻接表中当前遍历节点的第一条边,当p非空的时候,p指向其下一条边,计数变量加一,然后输出当前节点的入度数。

S5:构建“邻接表存储结构”函数。先求出有向图G中各节点的入度,然后初始化一个栈,通过for循环遍历有向图G,并将入度为0的节点进行进栈操作。当存入的栈非空时,将顶指针指向的节点进行出栈操作,并保存在拓扑序列数组中,计数输出节点个数。接着,再临时新建一个p指针表示当前节点的第一条边指针。当p非空时,vk是vi的邻接点,vi的每个邻接点入度减去1。如果vk的入度为0,则将vk入栈,并令p指向vi的下一个邻接节点。最后通过if条件语句判断是否有回路,如果有回路则返回0,否则返回1。

S6:构造主函数。主要通过调用上述函数,得出拓扑序列。

(3)分析算法的时间复杂度

拓扑排序算法的主要思想为:

第一步,栈S初始化,累加器count初始化。

第二步,扫描顶点表,将没有前驱(即入度为0)的顶点压栈。

第三步,当栈S非空时循环,(1)vj=退出栈顶元素;输出vj;累加器加1;(2)将顶点vj的各个邻接点的入度减1;(3)将新的入度为0的顶点入栈。

第四步,if (count<vertexNum) 输出有回路信息。

如果AOV网络有n个顶点,e条边,在拓扑排序的过程中,搜索入度为零的顶点所需的时间是O(n)。在正常情况下,每个顶点进一次栈,出一次栈,所需时间O(n)。每个顶点入度减1的运算共执行了e次。所以总的时间复杂为O(n+e)。

 


题目2

1.1)实验源代码

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

void half(int *a,int n){
	int start=0,end=n-1;
    while(start<=end){
        int mid = start+((end-start)>>1);   //折半查找 
        //a[mid]!=mid时,向前判断,因为肯定是之前的不等引发当前a[mid]!=mid
        if(a[mid] != mid){
            if(mid==0 || a[mid-1]==mid-1){
                cout<<endl<<"缺失的数字是:"<<mid<<endl;//确定mid并输出 
            }
            end = mid-1;
        }
		else{
            start = mid+1;
        }
    }
}

int main(){
	int n,i,a[10000];
	cout<<"请输入你需要输入的数字数目:";
	cin>>n;
	cout<<endl<<"请输入一个递增序列:";
	for(i=0;i<n;i++){
		cin>>a[i];
	}
	half(a,n);
	return 0;
}

(1.2)测试用例及其输出结果

测试用例1:

n=3

array=【0,1,3】

程序运行结果1:

测试用例2:

n=9

array=【0,1,2,3,4,5,6,7,9】

程序运行结果2:

(2)简述算法步骤

S1:构造“折半查找”函数。首先设置下标值start、end和mid,mid即为缺失的数字的下标,之后利用while循环进行查找循环。每次折半查找都要更新min的值,由于该序列是隔1递增的,因此查找的终止条件为a[mid]!=mid。

S2:构造主函数。输入需要查找的数字和已知的递增序列,再通过调用half函数输出运行结果。

 

(3)分析算法的时间复杂度

折半查找判定树的构造:如果当前low和high之前有奇数个元素,则mid分隔后,左右两部分元素个数相等。如果当前low和high之间有偶数个元素,则mid分隔后,左半部分比右半部分少一个元素。

在折半查找的判定树中,若mid=(low+high)/2,向下取整,则对于任何一个结点,因此必有:右子树结点-左子树结点数=0或1。当有奇数个元素时等于0,有偶数个元素时等于1。所以折半查找判定数一定是平衡二叉树,树的深度为[log2n]+1。查找成功时所进行的关键码比较次数至多为[log2n]+1,查找不成功时和给定值进行比较的次数最多不超过树的深度。

综上所述,折半查找的时间复杂度为O(log2n)。

 


补充:请使用顺序查找算法进行时间复杂度对比分析。

【选作:】使用索引顺序表查找算法进行时间复杂度、空间复杂度的对比分析。

三、实验结果

顺序查找算法:

(1)源代码

//顺序查找算法

#include <cstdio>

#include <iostream>

using namespace std;



int main(){

       int n,a[1000],i;

       cout<<"请输入n:";

       cin>>n;

       cout<<"请输入排序好的数组:";

       for(i=0;i<n;i++){

              scanf("%d",&a[i]);

       }

       int j=0,loss=0,flag=0;

       while(j<n){

              if(a[j]!=j){

                     loss=j;

                     flag++;

                     break;

              }

              j++;

       }

       if(!flag){

              loss=n;

       }

       cout<<"缺失值为:"<<loss;

       return 0;

}

(2)测试用例及其运行结果

(3)时间复杂度分析

最好情况:一次比较就找到所要查找的元素,时间复杂度为O(1)。最差情况:比较n次(共有n个元素),时间复杂度为O(n)。平均情况:综合两种情况,顺序查找的时间复杂度为O(n)。

索引顺序表查找算法:

(1)源代码

#include <stdio.h>
#define BLOCK_NUMBER 3
#define BLOCK_LENGTH 6

typedef struct IndexNode
{
	int data;	//数据
	int link;	//指针
}IndexNode;

//索引表
IndexNode indextable[BLOCK_NUMBER];

//块间查找
int IndexSequlSearch(int s[], int l, IndexNode it[], int key){
	int i = 0;
	while (key > it[i].data && i < BLOCK_NUMBER){
		i++;
	}
	if (i > BLOCK_NUMBER){
		return -1;
	}
	else{//在块间顺序查找
		int j = it[i].link - 1;
		while (key != s[j] && j < l) {
			j++;
		}
		if (key == s[j]){
			return j + 1;
		}
		else{
			return -1;
		}
	}
}

//建立索引表
void IndexTable(int s[], int l, IndexNode indextable[]){
	int j = 0;
	for (int i = 0; i < BLOCK_NUMBER; i++){
		indextable[i].data = s[j];
		indextable[i].link = j;
		for (j; j < indextable[i].link + BLOCK_LENGTH && j < l; j++){
			if (s[j] > indextable[i].data){
				indextable[i].data = s[j];
			}
		}
	}
	for (int i = 0; i < BLOCK_NUMBER; i++){
		indextable[i].link++;
	}
}

//打印查找的数据元素以及其在线索表中的位置
void print(int searchNumber, int index){
	if (index == -1){
		printf("查找元素:%d\n", searchNumber);
		printf("查找失败!\n");
		return;
	}
	else{
		printf("查找元素:%d\n", searchNumber);
		printf("元素位置:%d\n", index);
		return;
	}
}

int main(){
	int s[18] = {22,48,60,58,74,49,86,53,13,8,9,20,33,42,44,38,12,24};
	IndexTable(s, 18, indextable);
	printf("线索表:");
	for (int i = 0; i < 18; i++){
		printf("%d ",s[i]);
	}
	printf("\n");
	int indexNumber1 = 38;
	int index1 = IndexSequlSearch(s, 18, indextable, indexNumber1);
	print(indexNumber1, index1);
	int indexNumber2 = 28;
	int index2 = IndexSequlSearch(s, 18, indextable, indexNumber2);
	print(indexNumber2, index2);
	return 0;
}

(2)测试用例及其运行结果

(3)时间、空间复杂度分析

算法思想:

首先,将n个数据元素"按块有序"划分为m块(m ≤ n)。每一块中的结点不必有序,但块与块之间必须"按块有序";即后一块中任一元素都必须大于前一块中最大的元素;然后,块之间进行二分或者顺序查找,块内进行顺序查找。

时间复杂度:

整体算法是对顺序查找的改进,介于顺序查找和二分查找之间,时间复杂度为O(log2m+n/m)。

空间复杂度:

一般情况下,为进行分块查找,可以将长度为n的表均匀分成b块,每块还有s个元素,即b = [n/s]。索引表采用顺序查找的方式,则查找成功时的平均查找长度为:(s+b+2)/2。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/804529.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

安卓:百度地图开发(超详细)

一、百度地图介绍 百度地图SDK是一套供开发者使用的软件开发工具包&#xff08;SDK&#xff09;&#xff0c;用于在Android应用程序中集成和使用百度地图功能。通过使用百度地图SDK&#xff0c;开发者可以实现在自己的应用中显示地图、获取定位信息、进行搜索、导航等功能。 百…

机器视觉初步14:相机标定原理及应用

相机标定是指通过已知的相机参数&#xff0c;解算相机内部参数矩阵和外部参数矩阵。 文章目录 1.为什么要标定&#xff1f;2.工业场景中常见的标定方法2.1. 使用棋盘格标定板&#xff08;Checkerboard Markers&#xff09;2.2 使用相机自标定2.3. 使用三维物体标定2.4.九孔标…

MATLAB与ROS联合仿真——控制类功能模块介绍

1、Keyboard Control &#xff08;1&#xff09;输入参数&#xff1a;无 &#xff08;2&#xff09;输出参数&#xff1a;Speed Factor为输出的速度系数&#xff08;1代表前行&#xff0c;0停止&#xff0c;-1代表后退&#xff09;&#xff0c;Turn Factor为输出的舵机系数&am…

excel绘制折线图或者散点图

一、背景 假如现在通过代码处理了一批数据&#xff0c;想看数据的波动情况&#xff0c;是不是还需要写个pyhon代码&#xff0c;读取文件&#xff0c;绘制曲线&#xff0c;看起来也简单&#xff0c;但是还有更简单的方法&#xff0c;就是直接生成csv文件&#xff0c;csv文件就是…

windows11打不开任务管理器,

目录 第一章、win11系统任务管理器打不开&#xff1f;第二章、解决方式修改注册表 友情提醒&#xff1a; 先看文章目录&#xff0c;大致了解文章知识点结构&#xff0c;点击文章目录可直接跳转到文章指定位置。 第一章、win11系统任务管理器打不开&#xff1f; Win11任务管理…

达梦数据库DEM监控部署

1、安装达梦8数据库 在192.168.1.253 windows机器上安装达梦8数据库 解压dm8_setup_win64_ent_8.1.1.56_20200115.iso安装包运行安装程序setup.exe 数据库安装信息&#xff1a; 数据库名:CBDM 实例名:CBDM 数据库目录:D:\dmdbms\data 端口:5236 控制文件: D:\dmdbms\data\CBDM\…

技术复盘(5)--git

技术复盘--git 资料地址原理图安装配置基本命令分支命令对接gitee练习:远程仓库操作 资料地址 学习地址-B站黑马&#xff1a;https://www.bilibili.com/video/BV1MU4y1Y7h5 git官方&#xff1a;https://git-scm.com/ gitee官网&#xff1a;https://gitee.com/ 原理图 说明&am…

Vue-Router基本使用

1 安装&#xff1a; vue2项目要安装vue-router3版本 npm i vue-router3 2 src下创建router目录&#xff0c;router文件夹下创建index.js 在vue.config.js中 配置src路径别名 3 在main.js中引入 4 在app.vue中配置 5 即可看到内容

十分钟配置好Neovim go开发环境(其他语言一样)

文章目录 前言仓库地址用法快捷键问题反馈 前言 这篇文章的目的是为了分享下我自己的Neovim配置。 本人是Golang程序员&#xff0c;最开始使用的IDE是JetBrains Goland。有一说一这个ide适配度很高&#xff0c;认识的很多人都使用这个。但是它也有几个对我来说的缺点&#xf…

JSP 结构和指令

JSP 结构 网络服务器需要一个 JSP 引擎&#xff0c;也就是一个容器来处理 JSP 页面。容器负责截获对 JSP 页面的请求。本教程使用内嵌 JSP 容器的 Apache 来支持 JSP 开发。 JSP 容器与 Web 服务器协同合作&#xff0c;为JSP的正常运行提供必要的运行环境和其他服务&#xff…

低代码:告别繁琐,提速软件开发

一、前言 数字化进程加速&#xff0c;对于软件开发效率和成本的要求在不断提高。与此同时&#xff0c;低代码技术的出现为这项高成本的人力开发提供了便捷。目前&#xff0c;低代码已广泛应用于各行各业&#xff0c;帮助企业提高应用开发效率、降低开发成本、提高软件质量&…

Vue 3:玩一下web前端技术(四)

前言 本章内容为VUE开发环境的使用与相关使用讨论。 上一篇文章地址&#xff1a; Vue 3&#xff1a;玩一下web前端技术&#xff08;三&#xff09;_Lion King的博客-CSDN博客 下一篇文章地址&#xff1a; &#xff08;暂无&#xff09; 一、开发环境的使用 1、汉化VScod…

提升设计技能,教你玩转CAD中的辅助命令

大家好&#xff0c;今天先来了解一下CAD软件中的视图操作。 CAD设计中的辅助命令是实现精准和高效设计的关键要素。熟练掌握并正确运用CAD中的各类辅助命令&#xff0c;对于设计师们来说至关重要。本文将为您提供如何正确使用CAD中的辅助命令的实用指南&#xff0c;帮助您优化…

动手学深度学习——实战Kaggle比赛:预测房价(代码详解+调参优化)

目录 1. 下载和缓存数据集2. Kaggle3. 访问和读取数据集4. 数据预处理5. 训练6. K折交叉验证7. 模型选择8. 提交Kaggle预测9. 调参优化 1. 下载和缓存数据集 数据集百度云&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/14CVZBjmlKA_c3MYNFLSvbg?pwdpysi 提取码&…

脚手架(vue-cli)的安装详细教程

首先要下载node.js 下载 | Node.js 中文网 (nodejs.cn)https://nodejs.cn/download/ 大家根据自己的系统来选择哪个&#xff0c;我是Windows系统&#xff0c;所以选择红色箭头所指的安装包去安装&#xff01;&#xff01;&#xff01; 接下来双击安装&#xff01;&#xff01;…

Docker 如何助您成为数据科学家

一、说明 在过去的 5 年里&#xff0c;我听到了很多关于 docker 容器的嗡嗡声。似乎我所有的软件工程朋友都在使用它们来开发应用程序。我想弄清楚这项技术如何使我更有效率&#xff0c;但我发现网上的教程要么太详细&#xff1a;阐明我作为数据科学家永远不会使用的功能&#…

英伟达 H100 vs. 苹果M2,大模型训练,哪款性价比更高?

M1芯片 | Uitra | AMD | A100 M2芯片 | ARM | A800 | H100 关键词&#xff1a;M2芯片&#xff1b;Ultra&#xff1b;M1芯片&#xff1b;UltraFusion&#xff1b;ULTRAMAN&#xff1b;RTX4090、A800;A100&#xff1b;H100&#xff1b;LLAMA、LM、AIGC、CHATGLM、LLVM、LLM、LLM…

C语言每日一题之整数求二进制1的个数

今天分享一道题目&#xff0c;用三种方法来求解 二进制1的个数 方法1 我们的十进制除10和取余数就可以得到我们每一位的数字&#xff0c;那我们的二进制也可 以 #include<stdio.h> int num_find_1(unsigned int n) {int count 0;while (n){if (1 n % 2){count;}n / 2…

python实现某品牌数据采集

某品牌数据采集 采集需求 地址&#xff1a;http://www.winshangdata.com/brandList 需求&#xff1a;用scrapy框架采集本站数据&#xff0c;至少抓取5个分类&#xff0c;数据量要求5000以上 采集字段&#xff1a;标题、创建时间、开店方式、合作期限、面积要求 网页分析 …