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

实验十二  图+查找

一、实验目的与要求

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;//邻接点序号为jp1->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]!=midif(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 6typedef 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.mzph.cn/news/12944.shtml

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

相关文章

WIZnet W51000S-EVB-PICO 入门教程(一)

概述 W5100S-EVB-Pico是基于树莓派RP2040和全硬件TCP/IP协议栈控制器W5100S的微控制器开发板-基本上与树莓派Pico板相同&#xff0c;但通过W5100S芯片增加了以太网功能。 W5100S-EVB-Pico特点 RP2040规格参数 双核Arm Cortex-M0 133MHz264KB 高速SRAM和2MB板载内存通过…

JAVA基础-多线程入门(详解)

目录 引言 一&#xff0c;线程概念 二&#xff0c;创建线程 2.1&#xff0c;继承Thread类&#xff0c;重写run方法 2.2&#xff0c;实现Runnable接口&#xff0c;重写run方法&#xff0c;实现Runnable接口的实现类的实例对象作为Thread构造函 数的target 2.3&#xff0c;通…

RCU 使用及机制源码的一些分析

》内核新视界文章汇总《 文章目录 1 介绍2 使用方法2.1 经典 RCU2.2 不可抢占RCU2.3 加速版不可抢占RCU2.4 链表操作的RCU版本2.5 slab 缓存支持RCU 3 源码与实现机制的简单分析3.1 数据结构3.2 不可抢占RCU3.3 加速版不可抢占RCU3.4 可抢占RCU3.5 报告禁止状态3.6 宽限期的开…

教雅川学缠论03-分型

原著作者将K线走势分成四中类型&#xff0c;这四中类型&#xff0c;就叫做分型&#xff0c;注意&#xff0c;分型是K线的组合&#xff08;至少3个K线&#xff09;&#xff0c;如下 下面我们以2023年7月武汉控股日K示例 四个分型用图来表示的话&#xff0c;还是很简单的&…

spring5源码篇(13)——spring mvc无xml整合tomcat与父子容器的启动

spring-framework 版本&#xff1a;v5.3.19 文章目录 整合步骤实现原理ServletContainerInitializer与WebApplicationInitializer父容器的启动子容器的启动 相关面试题 整合步骤 试想这么一个场景。只用 spring mvc&#xff08;确切来说是spring-framework&#xff09;&#x…

Flink集群运行模式--Standalone运行模式

Flink集群运行模式--Standalone运行模式 一、实验目的二、实验内容三、实验原理四、实验环境五、实验步骤5.1 部署模式5.1.1 会话模式&#xff08;Session Mode&#xff09;5.1.2 单作业模式&#xff08;Per-Job Mode&#xff09;5.1.3 应用模式&#xff08;Application Mode&a…

从头开始:数据结构和算法入门(时间复杂度、空间复杂度)

目录 文章目录 前言 1.算法效率 1.1 如何衡量一个算法的好坏 1.2 算法的复杂度 2.时间复杂度 2.1 时间复杂度的概念 2.2 大O的渐进表示法 2.3常见时间复杂度计算 3.空间复杂度 4.常见复杂度对比 总结 前言 C语言的学习篇已经结束&#xff0c;今天开启新的篇章——数据结构和算…

gitee 配置ssh 公钥(私钥)

步骤1&#xff1a;添加/生成SSH公钥&#xff0c;码云提供了基于SSH协议的Git服务&#xff0c;在使用SSH协议访问项目仓库之前&#xff0c;需要先配置好账户/项目的SSH公钥。 绑定账户邮箱&#xff1a; git config --global user.name "Your Name" git config --glob…

【图像去噪】基于进化算法——自组织迁移算法(SOMA)的图像去噪研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

HTML基础知识点总结

目录 1.HTML简介 2.HTML基础结构 主要字符&#xff1a; 3.基础知识 &#xff08;一&#xff09;p标签 &#xff08;二&#xff09;hr标签 &#xff08;三&#xff09;尖角号 &#xff08;四&#xff09;版权号 (五)div和span div span (六)列表 &#xff08;1&…

【Docker】Docker相关基础命令

目录 一、Docker服务相关命令 1、启动docker服务 2、停止docker服务 3、重启docker服务 4、查看docker服务状态 5、开机自启动docker服务 二、Images镜像相关命令 1、查看镜像 2、拉取镜像 3、搜索镜像 4、删除镜像 三、Container容器相关命令 1、创建容器 2、查…

pytorch实现梯度下降算法例子

如题&#xff0c;利用pytorch&#xff0c;通过代码实现机器学习中的梯度下降算法&#xff0c;求解如下方程&#xff1a; f ′ ( x , y ) x 2 20 y 2 {f}(x,y) x^2 20 y^2 f′(x,y)x220y2 的最小值。 Latex语法参考&#xff1a;https://blog.csdn.net/ViatorSun/article/d…

RabbitMQ 集群部署

RabbiMQ 是用 Erlang 开发的,集群非常方便,因为 Erlang 天生就是一门分布式语言,但其本身并不支持负载均衡。 RabbitMQ 的集群节点包括内存节点、磁盘节点。RabbitMQ 支持消息的持久化,也就是数据写在磁盘上,最合适的方案就是既有内存节点,又有磁盘节点。 RabbitMQ 模式大…

【动态规划part12】| 309.买卖股票的最佳时机含冷冻期、714.买卖股票的最佳时机含手续费

目录 &#x1f388;LeetCode309.最佳买卖股票时机含冷冻期 &#x1f388;LeetCode714.买卖股票的最佳时机含手续费 &#x1f388;LeetCode309.最佳买卖股票时机含冷冻期 给定一个整数数组prices&#xff0c;其中第 prices[i] 表示第 i 天的股票价格 。​ 设计一个算法计…

Jenkins从配置到实战(二) - Jenkins的Master-Slave分布式构建

前言 Jenkins的Master-Slave分布式构建&#xff0c;就是通过将构建过程分配到从属Slave节点上&#xff0c;从而减轻Master节点的压力&#xff0c;而且可以同时构建多个&#xff0c;有点类似负载均衡的概念。简单理解就是&#xff0c;将Jenkins服务器上的构建任务分配到其他机器…

【Spring Boot】实战:实现优雅的数据返回

实战&#xff1a;实现优雅的数据返回 本节介绍如何让前后台优雅地进行数据交互&#xff0c;正常的数据如何统一数据格式&#xff0c;以及异常情况如何统一处理并返回统一格式的数据。 1.为什么要统一返回值 在项目开发过程中经常会涉及服务端、客户端接口数据传输或前后台分…

多分类问题-Softmax Classifier分类器

概率分布&#xff1a;属于每一个类别的概率总和为0&#xff0c;且都>0&#xff0c;n组类别需要n-1个参数就能算出结果 数据预处理 loss函数 crossentropyloss()函数 CrossEntropyLoss <> LogSoftmax NLLLoss。也就是说使用CrossEntropyLoss最后一层(线性层)是不需要做…

Fiddler Everywhere(TTP调试抓包工具) for Mac苹果电脑版

Fiddler Everywhere for Mac版是Mac电脑上的一款跨平台的HTTP调试抓包工具&#xff0c;Fiddler Everywhere for Mac能够记录客户端与服务器之间的所有HTTP&#xff08;S&#xff09;通信&#xff0c;支持对包进行监视、分析、设置断点、甚至修改请求/响应数据等操作。 适用于任…

基于量子同态加密的安全多方凸包协议

摘要安全多方计算几何(SMCG)是安全多方计算的一个分支。该协议是为SMCG中安全的多方凸包计算而设计的。首先&#xff0c;提出了一种基于量子同态加密的安全双方值比较协议。由于量子同态加密的性质&#xff0c;该协议可以很好地保护量子电路执行过程中数据的安全性和各方之间的…

【广州华锐互动】AR智慧机房设备巡检系统

AR智慧机房设备巡检系统是一种新型的机房巡检方式&#xff0c;它通过使用增强现实技术将机房设备、环境等信息实时呈现在用户面前&#xff0c;让巡检人员可以更加高效地完成巡检任务。 首先&#xff0c;AR智慧机房设备巡检系统具有极高的智能化程度。该系统可以根据用户设定的…