深入理解数据结构——图

2023-11-04

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


struct Graph {
	int** data;
	int vertex_num;//顶点数量
	int edge_num;//边数
};

//基于邻接矩阵表示的有向带权图的建立算法

void CreateGraph(Graph &g) {
	int n, first, second;
	cout << "请输入边的数量";
	cin >> g.edge_num;

	cout << "请输入dingdian的数量";
	cin >> g.vertex_num;

	g.data = new int* [g.vertex_num];

	for (n = 0; n < g.vertex_num; n++)
		g.data[n] = new int[g.vertex_num];

	for (first = 0; first < g.vertex_num; first++)
		for (second = 0; second < g.vertex_num; second++)
			g.data[first][second] = INFINITY;

	for ( n = 0; n < g.edge_num; n++)
	{
		cout << "请输入第" << n + 1 << "条弧的弧头顶点的序号";
		cin >> first;
		cout << "请输入第" << n + 1 << "条弧的弧尾顶点的序号";
		cin >> second;
		cout << "请输入权";
		cin >> g.data[first][second];

	}
}


//邻接表的实现

typedef int InfoType;

struct AdjNode {
	int another_vertex;//该条边另一个顶点在表头节点数组中的下标
	InfoType info;//
	AdjNode* next;//下一个节点地址
};

typedef int EType;

struct AdjList {
	EType data;
	AdjNode* head;
};

struct AdjGraph {
	AdjList * head_list;
	int vertex_num;//顶点数量
	int edge_num;//边数
};

//基于邻接表表示的图的建立算法

void CreateGraph(AdjGraph &g) {

	
	int n, first, second, weight;

	AdjNode* p;

	cout << "请输入边的数量";
	cin >> g.edge_num;

	cout << "请输入dingdian的数量";
	cin >> g.vertex_num;

	g.head_list = new AdjList[g.vertex_num];
	for (n = 0; n < g.vertex_num; n++)
		g.head_list[n].head = NULL;

	for (n = 0; n < g.edge_num; n++)
	{
		cout << "请输入第" << n + 1 << "条弧的弧头顶点的序号";
		cin >> first;
		cout << "请输入第" << n + 1 << "条弧的弧尾顶点的序号";
		cin >> second;
		cout << "请输入权";
		cin >> weight;

		p = new AdjNode;
		p->info = weight;
		p->another_vertex = second;
		p->next = g.head_list[first].head;
		g.head_list[first].head = p;
	}
}



//有向图的十字链表结构
#define MAX_VERTEX_NUM 20

struct ArcBox
{
	int tailvex, headvex;//该弧的尾和头节点的位置
	struct ArcBox* hlink, * tlink;//分别为弧头相同和弧尾相同的弧的链接域
	InfoType info;
};

typedef char VertexType;
struct VexNode {
	VertexType vertex;
	ArcBox fisrin, firstout;//分别指向该顶点第一条入弧和出弧
};

struct OLGraph {
	VexNode xlist[MAX_VERTEX_NUM];//表头向量
	int vexnum,     //有向图的定点数
		arcnum;		//有向图的弧数
};

#define MAX_VERTEX_NUM 20
typedef enum{ 
	unvisited,visited 
}VisitIf;

struct EBox {
	VisitIf mark;  //访问标记
	int ivex, jvex;//该边衣服的两个顶点的位置
	EBox *ilink;//分别指向依附这两顶点的下一条边
	EBox *jlink;

	InfoType info;
};


struct VexBox {
	VertexType data;
	EBox fistedge;//指向第一条依附该顶点的边
};

struct AMLGraph {
	VexBox adjmulist[MAX_VERTEX_NUM];
	int vexnu, edgenum;//无向图的当前顶点数和边数
};

//深度优先搜索的递归实现算法
//基于邻接表表示的有向图的深度优先搜索递归算法
void DFS(AdjGraph g, int v, int* visited) {
	AdjNode* w;
	visited[v] = 1;

	cout << v << "->";

	for (w = g.head_list[v].head; w; w = w->next) {
		if (!visited[w->another_vertex])
			DFS(g,w->another_vertex,visited);
	}
}

void DFSTrans(AdjGraph g) {
	int *visited, i;

	visited = new int[g.vertex_num];

	memset(visited, 0, sizeof(int) * g.vertex_num);
	for (i = 0; i < g.vertex_num; i++) {
		if (!visited[i]) {
			DFS(g, i, visited);
		}
	}
	delete[]visited;
}



//基于邻接表表示的有向图的深度优先搜索非递归算法
//算法复杂度为O(n^2)
void DFS(AdjGraph g, int v, int* visited) {
	
	int stack[1000];
	int top;//栈顶指针
	int i;
	AdjNode* pt;
	cout << v << "->";
	top = 0;

	visited[v] = 1;//已经访问标志
	stack[top] = v;//栈顶元素
	
	while (top >= 0) {

		pt = g.head_list[stack[top]].head;//访问元素的头部
		while ((pt != NULL) && visited[pt->another_vertex])
			pt = pt->next;//找到一个未被访问的邻接顶点
		if (!pt)
			top--;//没有找到,返回到前一次访问过的顶点
		else {
			i = pt->another_vertex;//进行访问
			cout << i << "->";
			visited[i] = 1;//访问完成标志
			top++;
			stack[top] = i;
		}
	}
}

void DFSTrans(AdjGraph g) {
	int* visited, i;

	visited = new int[g.vertex_num];

	memset(visited, 0, sizeof(int) * g.vertex_num);
	for (i = 0; i < g.vertex_num; i++) {
		if (!visited[i]) {
			DFS(g, i, visited);
		}
	}
	delete[]visited;
}

//基于邻接表表示的有向图的宽度优先搜索非递归算法
#define MAXIMUM 1000
void BFS(AdjGraph g, int v, int* visited) {
	int queue[MAXIMUM];
	
	int head, tail, t; 
	AdjNode* p;
	head = 0;
	tail = 0;
	cout << v << "->";

	visited[v] = 1;
	queue[head] = v;
	while (head<=tail) {
		p = g.head_list[queue[head]].head;//从队首取顶点

		while (!p) {
			t = p->another_vertex;
			if (!visited[t]) {
				cout << t << "->";
				visited[t] = 1;
				tail++;
				queue[tail] = t;//入队
			}
			p = p->next;
		}//出队
		head++;
	}

}

void DFSTrans(AdjGraph g) {
	int* visited, i;

	visited = new int[g.vertex_num];

	memset(visited, 0, sizeof(int) * g.vertex_num);
	for (i = 0; i < g.vertex_num; i++) {
		if (!visited[i]) {
			DFS(g, i, visited);
		}
	}
	delete[]visited;
}

//无向图的连通性
void DFSTrans(AdjGraph g) {
	int* visited, i;
	int count = 0;//计数变量
	visited = new int[g.vertex_num];

	memset(visited, 0, sizeof(int) * g.vertex_num);
	for (i = 0; i < g.vertex_num; i++) {
		if (!visited[i]) {
			count++;//每调用一次dfs,count+1
			DFS(g, i, visited);
		}
	}
	delete[]visited;
}


//基于邻接矩阵表示的图的prim算法实现——连通网络的最小代价生成树
//时间复杂度O(n^3)
void Prim(Graph g, int v)
{
	int* flag;
	int i, m, n, min, temp_m, temp_n;
	memset(flag, 0, sizeof(int) * g.vertex_num);

	flag[v] = 1;//把顶点v放入集合U

	for (i = 0; i < g.vertex_num; i++) {
		min = INFINITY;
		for (m = 0; m < g.vertex_num; m++) {
			for (n = 0; n < g.vertex_num; n++) {
				if ((flag[m] + flag[n]) == 1 &&
					g.data[m][n] < min) {//(flag[m]+flag[n])==1表示连个顶点只有一个在集合U中
					min = g.data[m][n];
					temp_m = m;
					temp_n = n;
				}
			}
		}
		cout << temp_m << "-" << temp_m << endl;
		g.data[temp_m][temp_n] = INFINITY;//不考虑此边
		flag[temp_m] = 1;
		flag[temp_n] = 1;
	}
	delete[]flag;
}

//基于邻接矩阵表示的图的kruakal算法实现——连通网络的最小代价生成树

void Kruskal(Graph g, int v) {
	int* set_number;
	int i, j, m, n, min, temp_m, temp_n;

	set_number = new int[g.vertex_num];
	for (i = 0; i < g.vertex_num; i++)
		set_number[i] = i;//设置联通分量号

	for (i = 0; i < g.vertex_num; i++) {
		min = INFINITY;
		for (m = 0; m < g.vertex_num; m++) {
			for (n = 0; n < g.vertex_num; n++) {
				if ((set_number[m] != set_number[n]) &&
					g.data[m][n] < min) {
					min = g.data[m][n];
					temp_m = m;
					temp_n = n;
				}
			}
		}
		cout << temp_m << "-" << temp_m << endl;
		g.data[temp_m][temp_n] = INFINITY;//不考虑此边

		for (j = 0; j < g.vertex_num; j++) {
			if (set_number[j] == set_number[temp_n])
				set_number[j] == set_number[temp_m];
		}
	}
	delete[]set_number;
}

//单源最短路径
void Dijkstra(Graph g, int v) {
	int* dist;//顶点v到各顶点的当前最短距离
	int* S;

	char** path, temp[20];
	int i, j, k, min;
	dist = new int[g.vertex_num];
	S = new int[g.vertex_num];
	path = new char* [g.vertex_num];

	for (i = 0; i < g.vertex_num; i++)
		path[i] = new char[100];

	for (i = 0; i < g.vertex_num; i++) {//初始化
		dist[i] = g.data[v][i];//v到i是路劲长短
		if (dist[i] < INFINITY)
			sprintf(path[i], "%d->%d", v, i);
		else
			strcpy(path[i], "");
		S[i] = 0;
	}

	S[v] = 1;//把源点放入集合S中
	for (i = 1; i < g.vertex_num; i++) {
		min = INFINITY;
		j = 0;
		for (k = 0; k < g.vertex_num; k++) {//在V—S中找到dist[]值最小的顶点
			if (!S[k] && dist[k] < min) {
				//S[k]不为零,且dist[k]小于最小值
				min = dist[k];//最小值就是dist[k]
				j = k;
			}
		}
		S[j] = 1;//把找到的顶点放入S集合中
		
		for (k = 0; k < g.vertex_num; k++) {//修改V—S各顶点的dist[]值
			if (!S[k] && dist[j]+g.data[j][k] < dist[k]) {
				//S[k]不为零,且dist[j]+g.data[j][k]小于dist[k]
				dist[k] = dist[j] + g.data[j][k];
				sprintf(temp, "->%d", k);
				strcpy(path[k],path[j]);
				strcpy(path[k],temp);
				
			}
		}	
	}	
	
	for (i = 1; i < g.vertex_num; i++) {
		cout << dist[i] << "  " << path[i] << endl;
	}

	delete[]dist;
	delete[]S;
	for (i = 1; i < g.vertex_num; i++) {
		delete[]path[i];
	}
	delete[]path;
}


//任意两顶点之间的路径
//时间复杂度O(n^3)
void AllShortPath(Graph g) {
	int** m;
	char*** path;

	int n, i, j, k;

	m = new int*[g.vertex_num];
	path = new char** [g.vertex_num];

	for (n = 0; n < g.vertex_num; n++) {
		m[n] = new int[g.vertex_num];
		path[n]= new char* [g.vertex_num];
		for (i = 0; i < g.vertex_num; i++) {
			path[n][i] = new char [100];
		}
	}

	for (i = 0; i < g.vertex_num; i++) {
		for ( j= 0; j < g.vertex_num; j++) {
			m[i][j] = g.data[i][j];
			if (i != j && m[i][n] < INFINITY) {
				sprintf(path[i][j], "%d->%d", i, j);
			}
			else {
				path[i][j][0] = 0;
			}
		}
	}

	for (k = 0; k < g.vertex_num; k++) {
		for (i = 0; i < g.vertex_num; i++) {
			for (j = 0; j < g.vertex_num; j++) {
				if (i != j && m[i][k] + m[k][j] < m[i][j]) {
					m[i][j] = m[i][k] + m[k][j];

					strcpy(path[i][j], path[i][k]);
					strcpy(path[i][j], ",");
					strcpy(path[i][j], path[k][j]);
				}
			}
		}
	}


	for (i = 0; i < g.vertex_num; i++) {
		for (j = 0; j < g.vertex_num; j++) {
		
			printf("  %d",m[i][j]);
		}
		cout << endl;
	}

	for (i = 0; i < g.vertex_num; i++) {
		for (j = 0; j < g.vertex_num; j++) {

			printf("  %s", path[i][j]);
		}
		cout << endl;
	}


	for (i = 0; i < g.vertex_num; i++)
		for (j = 0; j < g.vertex_num; j++)
			delete[]path[i][j];

	for (i = 0; i < g.vertex_num; i++) {
		delete []m[i];
		delete[]path[i];
	}

	delete[]m;
	delete[]path;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深入理解数据结构——图 的相关文章

  • c和java语言中的换行符

    现在行分隔符取决于系统 但在 C 程序中我使用 n 作为行分隔符 无论我在 Windows 还是 Linux 中运行它都可以正常工作 为什么 在java中 我们必须使用 n 因为它与系统相关 那么为什么我们在c中使用 n 作为新行 而不管我
  • 如何在 C++ 中的文件末尾添加数据?

    我已按照网上的说明进行操作 此代码应该将输入添加到文件 数据库 的末尾 但当我检查时 数据会覆盖现有数据 请帮忙 这是我的代码 int main string name string address string handphone cou
  • 使用 Unity 在构造函数中使用属性依赖注入

    好的 我在基类中定义了一个依赖属性 我尝试在其派生类的构造函数内部使用它 但这不起作用 该属性显示为 null Unity 在使用 container Resolve 解析实例后解析依赖属性 我的另一种选择是将 IUnityContaine
  • 如何读取扩展文件属性/文件元数据

    因此 我按照教程使用 ASP net core 将文件 上传 到本地路径 这是代码 public IActionResult About IList
  • 为 Visual Studio 2013 编译 Tesseract

    我正在尝试使用tesseract在 Visual Studio 2013 中 我在链接器 gt 输入 不是 libtesseract302 static lib 中使用 libtesseract302 lib 一切都正常 并且已编译并运行
  • 如何修复此错误“GDI+ 中发生一般错误”?

    从默认名称打开图像并以默认名称保存 覆盖它 我需要从 Image Default jpg 制作图形 将其放在 picturebox1 image 上并在 picurebox1 上绘制一些图形 它有效 这不是我的问题 但我无法保存 pictu
  • 将内置类型转换为向量

    我的 TcpClient 类接受vector
  • 单元测试一起运行时失败,单独运行时通过

    所以我的单元测试遇到了一些问题 我不能只是将它们复制并粘贴到这里 但我会尽力而为 问题似乎是 如果我一项一项地运行测试 一切都会按预期进行 但如果我告诉它一起运行测试 则 1 5 将通过 TestMethod public void Obj
  • 如何从 .resx 文件条目获取注释

    资源文件中的字符串有名称 值和注释 The ResXResourceReader类让我可以访问名称和值 有办法看评论吗 你应该能够得到Comment via ResXDataNode class http msdn microsoft co
  • 在 C# 中循环遍历文件文件夹的最简单方法是什么?

    我尝试编写一个程序 使用包含相关文件路径的配置文件来导航本地文件系统 我的问题是 在 C 中执行文件 I O 这将是从桌面应用程序到服务器并返回 和文件系统导航时使用的最佳实践是什么 我知道如何谷歌 并且找到了几种解决方案 但我想知道各种功
  • 如何在 C# 中定义文本框数组?

    您好 当我在 Windows 申请表上创建文本框时 我无法将其命名为 box 0 box 1 等 我这样做的目的是因为我想循环使用它们 其实我发现TextBox array firstTextBox secondTextBox 也有效
  • 使用 JNI 从 Java 代码中检索 String 值的内存泄漏

    我使用 GetStringUTFChars 从使用 JNI 的 java 代码中检索字符串的值 并使用 ReleaseStringUTFChars 释放该字符串 当代码在 JRE 1 4 上运行时 不会出现内存泄漏 但如果相同的代码在 JR
  • Rx 中是否有与 Task.ContinueWith 运算符等效的操作?

    Rx 中是否有与 Task ContinueWith 运算符等效的操作 我正在将 Rx 与 Silverlight 一起使用 我正在使用 FromAsyncPattern 方法进行两个 Web 服务调用 并且我想这样做同步地 var o1
  • 未定义的行为或误报

    我 基本上 在野外遇到过以下情况 x x 5 显然 它可以在早期版本的 gcc 下编译干净 在 gcc 4 5 1 下生成警告 据我所知 警告是由 Wsequence point 生成的 所以我的问题是 这是否违反了标准中关于在序列点之间操
  • 未经许可更改内存值

    我有一个二维数组 当我第一次打印数组的数据时 日期打印正确 但其他时候 array last i 的数据从 i 0 到 last 1 显然是一个逻辑错误 但我不明白原因 因为我复制并粘贴了 for 语句 那么 C 更改数据吗 I use g
  • 将 log4net 与 Autofac 结合使用

    我正在尝试将 log4net 与 Autofac 一起使用 我粘贴了这段代码http autofac readthedocs org en latest examples log4net html http autofac readthed
  • 有没有办法强制显示工具提示?

    我有一个验证字段的方法 如果无法验证 该字段将被清除并标记为红色 我还希望在框上方弹出一个工具提示 并向用户显示该值无效的消息 有没有办法做到这一点 并且可以控制工具提示显示的时间 我怎样才能让它自己弹出而不是鼠标悬停时弹出 If the
  • 编译时“strlen()”有效吗?

    有时需要将字符串的长度与常量进行比较 例如 if line length gt 2 Do something 但我试图避免在代码中使用 魔法 常量 通常我使用这样的代码 if line length gt strlen Do somethi
  • 使用 GROUP 和 SUM 的 LINQ 查询

    请帮助我了解如何使用带有 GROUP 和 SUM 的 LINQ 进行查询 Query the database IEnumerable
  • 检查Windows控制台中是否按下了键[重复]

    这个问题在这里已经有答案了 可能的重复 C 控制台键盘事件 https stackoverflow com questions 2067893 c console keyboard events 我希望 Windows 控制台程序在按下某个

随机推荐

  • 【性能测试】Jenkins+Ant+Jmeter自动化框架的搭建思路

    前言 前面讲了Jmeter在性能测试中的应用及扩展 随着测试的深入 我们发现在性能测试中也会遇到不少的重复工作 比如某新兴业务处于上升阶段 需要在每个版本中 对某些新增接口进行性能测试 有时还需要在一天中的不同时段分别进行性能测试 如果一味
  • Gradle Core Plugins (plugin is not in 'org.gradle' namespace)

    记录一个由 gradle 构建项目遇到的问题 起因 项目原先运行正常 不过个人 移除掉默认仓库 gradle 仓库后 重新拉取报错如下 FAILURE Build failed with an exception Where Build f
  • 框架(Framework)中常用设计模式分析

    文章目录 简介 概述 模式分类 创建型模式设计与分析 简单工厂模式 工厂方法模式 Factory Method 抽象工厂 Abstract Factory 结构型模式设计及分析 适配器模式 Adapter 装饰模式 Decorator 代理
  • opencv学习(十五)之图像傅里叶变换dft

    在学习信号与系统或通信原理等课程里面可能对傅里叶变换有了一定的了解 我们知道傅里叶变换是把一个信号从时域变换到其对应的频域进行分析 如果有小伙伴还对傅里叶变换处于很迷糊的状态 请戳这里 非常通俗易懂 而在图像处理中也有傅里叶分析的概念 我这
  • chromecast投屏_谷歌Chromecast与安卓Miracast投屏技术

    Win10的无线连接显示器用的就是Miracast 安卓Miracast投屏技术 Miracast是WiFi联盟推出来的标准 但这个标准似乎并没有对兼容性作详细的要求 于是 很多电视厂商都基于Miracast 魔改出了自家的投屏技术 例如现
  • 几个排序理解

    快速排序 快速排序是对冒泡排序的一种改进 通过一趟排序将要排序的数据分割成独立的两部分 其中一部分的所有数据都比另一部分所有的数据都要小 然后再按此方法对这两部分数据分别进行快速排序 整个排序过程可以递归进行 以此达到整个数据变成有序序列
  • 提取json字符串中指定格式中的参数值

    直接上代码 import java util ArrayList import java util regex Matcher import java util regex Pattern public class TestDemo pub
  • Linux ./configure --prefix命令

    源码的安装一般由3个步骤组成 配置 configure 编译 make 安装 make install 具体的安装方法一般作者都会给出文档 这里主要讨论配置 configure Configure是一个可执行脚本 它有很多选项 使用命令 c
  • 局域网访问本地localhost-VS2015调试WebService

    两点步骤 一 配置IP 二 VS管理员启动 配置ip 可以自定义IP 或者用自动分配的IP cmd ipconfig VS运行右键 显示所有应用程序 打开配置文件 在
  • 【css学习】使用css3中的var实现主题切换

    一 首先搭建基础的页面结构
  • Spring cloud项目扩展(二)项目集成redis和辅助工具hutool

    最近看到了一个很好用的集成开发工具 里面有很多工具类 可以提高开发效率 官方文档请看 https hutool cn docs 下面主要介绍一下在我们项目中加入工具并且通过这个工具使用redis 话不多说 直接开始 1 在我们原有的项目的项
  • 2021-05-05

    实训3 信息加密与哈希函数 实验目的 理解加密系统的概念 掌握经典加密的主要方法 理解混淆与扩散的概念 掌握DES加密的主要方法 了解非对称加密的重要意义 掌握RSA加密算法的主要思想与使用方法 理解数字签名的作用及生成方法 实验准备及注意
  • java声明方法抛出的异常

    java声明方法抛出的异常 TestExceptions java import java io 异常 public class TestExceptions public static void main String args void
  • 贵阳人文科技学院新颖的计算机毕业设计题目大全50例

    最近要准备毕业设计了 不会选题 希望可以帮忙给一些毕业设计题目 我整整花了一周把之前做的答辩通过的毕业设计成品进行整理如下列表 计算机科学技术毕业设计题目推荐1 10题 1 Springboot美食网站92nn7 2 Springboot基
  • Adb connection Error:远程主机强迫关闭了一个现有的连接

    小编遇到这个烦人的问题 总是一直报错 浏览了许多网页 总结了以下几种解决方法 这些都是转载加上自己的见解 这里本人是用最后一种搞定的 不过有时候需要进入paltform tools目录下 因为没有PATH路径 注意这种方法需要启动虚拟记得就
  • Stable Diffusion公司重磅开源大语言模型StableLM,又爆火了!

    点击下方卡片 关注 CVer 公众号 AI CV重磅干货 第一时间送达 点击进入 gt 计算机视觉 微信技术交流群 金磊 发自 凹非寺转载自 量子位 QbitAI 万万没想到 以文生图著名的Stable Diffusion 也入局了大语言模
  • Java String 字符串 截取保留小数点后两位

    截取保留小数点后两位 public static String dealRateStr String rateStr int i rateStr indexOf 如果没有小数点不 if i 1 return rateStr 00 获取小数点
  • Java设计模式:23种设计模式全面解析,墙都不扶就服你

    命令模式 将命令请求封装为一个对象 使得可以用不同的请求来进行参数化 迭代器模式 一种遍历访问聚合对象中各个元素的方法 不暴露该对象的内部结构 观察者模式 对象间的一对多的依赖关系 仲裁者模式 用一个中介对象来封装一系列的对象交互 备忘录模
  • 关于定时任务简单小脚本

    1 每4小时备份一次 etc 目录至 backup目录中 保存的文件名称格式为 etc yyyy mm dd HH tar xz crontab e 进入编辑模式 tar JcPf创建文档并保存为 xz格式 0 4 tar JcPf bac
  • 深入理解数据结构——图

    include