二维动态数组与图的遍历

1. 二维动态数组

一般图结构创建是根据邻接矩阵的定义,采用链表的的方式实现。对于这里的邻接矩阵借鉴了存储图像数据的动态二维数组结构,他的构造和析构如下:

const int num = 5;

//分配空间
int **array = new int* [num];
for (int i=0; i<num; i++)
{
<span style="white-space:pre">	</span>array[i] = new int[num];
}
//释放资源
for (int i=0; i<num; i++)
{
	delete[] array[i];
}
delete []array;

在C++11标准里面可以采用std::tr1::shared_ptr 来管理这片内存,特别是当涉及到跨文件进行处理的时候,因忘记析构而内存泄露的风险会减小很多。

2. 图及其遍历

2.1 基于链式存储的图数据结构

首先要一个数组,里面包含图的边数和顶点数。每个数组元素里面包含数据域和指向连接表头结点的头指针。

//基于链式存储的邻接表图数据结构
const int MAXVEX = 20;	//定义图的最大顶点数目
typedef char DataType;
typedef struct arc		//定义表结点类型
{
	int adjvex;			//顶点序号
	int weight;			//权值
	struct arc* next;	//指向下一个邻接点的指针
}ArcNode, *PArcNode;

typedef struct			//顶点表结点类型
{
	DataType data;		//顶点信息
	ArcNode* head;		//编表头指针
}VexNode;

typedef struct				//邻接表类型
{
	VexNode list[MAXVEX];	//顶点表
	int edges, vexs;		//用于统计图的顶点与边数
}VGraph, *AdjList;

2.2 图的创建

图的创建是通过二维for循环实现的,外层for循环实现头结点和顶结点数据域的初始化,内层for循环实现与每个相连的顶点的创建(链表实现)和设置权值。

/************************************************************************/
/* 
创建图结构
输入参数
grap:		邻接表类型指针
m:			邻接矩阵的数据
n:			顶点的总数

返回值
成功返回true 失败返回false
*/
/************************************************************************/
bool CGraphLink::CreateGraph(VGraph* grap, int** m, int n)
{
	if ((nullptr==grap) || (nullptr==m) || (0>n))
	{
		return false;
	}
	grap->edges = 0;	//初始化边的总数目和顶点的数目为0
	grap->vexs = n;

	for (int i=0; i<n; i++)
	{
		grap->list[i].data = 'A'+i;	//给顶点信息赋值
		grap->list[i].head = nullptr;	//初始化头指针为空
		for (int j=0; j<n; j++)
		{
			if (0 != m[i][j])	//如果二维数组m中的对应元素非0
			{
				ArcNode *p = new ArcNode;
				if (nullptr == p)	//分配内存错误
				{
					std::cout << "\n分配内存错误!\n";
					return false;
				}
				p->adjvex = j;					//设置顶点序号
				p->next = grap->list[i].head;	//从链表头部插入新结点
				p->weight = m[i][j];			//设置权值
				grap->list[i].head = p;			//设置头指针指向p
				grap->edges++;					//边数加1
			}			
		}
	}

	return true;
}

2.3 深度优先遍历

/************************************************************************/
/*
连通图的深度优先遍历方法 递归实现
grap:		输入的图结构
k:			起始的顶点序号
visited:   已经访问过的顶点的存储数组
*/
/************************************************************************/
void CGraphLink::DFS(VGraph* grap, int k, int* visited)
{
	std::cout << grap->list[k].data;	//显示顶点数据
	visited[k] = 1;					//标记已经访问了的顶点
	int u = GetFirst(grap, k);		//获取头结点指向的下一个顶点的编号
	while (-1 != u)	//还有节点存在
	{
		if (0 == visited[u])	//判断顶点是否被访问过
		{
			DFS(grap, u, visited);	//若没有被访问过递归调用进行访问
		}
		u = GetNext(grap, k, u);	//获取下一个邻接点
	}
}

/************************************************************************/
/* 
获取图邻接表的第k+1个的第一个顶点编号
grap:		输入的图结构
k:			第K+1个邻接链表,顶点的序号

返回值:
滴K+1个邻接链表的第一个值	若为空则返回为-1
*/
/************************************************************************/
int CGraphLink::GetFirst(VGraph* grap, int k)
{
	if ((k<0) || (k>grap->vexs))
	{
		std::cout << "\n输入的K超出了范围!\n";
		return -1;
	}
	if (nullptr == grap->list[k].head)
	{
		std::cout << "\n需要获取的连接表头指针为空!\n";
		return -1;
	}
	else
	{
		return grap->list[k].head->adjvex;
	}	
}


/************************************************************************/
/* 
在顶点k的边表中获取下一个邻接点 先在边表中找u,找到u之后,u的next指针域即指向下一个邻接点
*/
/************************************************************************/
int CGraphLink::GetNext(VGraph* grap, int k, int u)
{
	if ((k<0) || (k>grap->vexs) || (u<0) || (u>grap->vexs))
	{
		std::cout << "\n输入的K超出了范围!\n";
		return -1;
	}
	PArcNode p = grap->list[k].head;
	while ((nullptr!=p->next) && (p->adjvex!=u))
	{
		p = p->next;
	}
	if (nullptr == p->next)
	{
		return -1;
	}
	else
	{
		return p->next->adjvex;
	}
}

2.4 广度优先遍历

/************************************************************************/
/* 
连通图数据结构的广度优先遍历     #_# 非连通图的遍历参照广度优先非连通图的遍历实现 #_#
grap:		需要广度优先遍历的图结构
k:			广度优先遍历的其实顶点
返回值
连通图遍历成功返回true 失败返回false
*/
/************************************************************************/
bool CGraphLink::BFS(VGraph* grap, int k, const int num)
{
	//前期错误检查
	if ((nullptr==grap) || (0>k))
	{
		std::cout << "\nSFS error occurred\n";
		return false;
	}
	if (nullptr == grap->list[k].head)
	{
		std::cout << "\nBFS pos error\n";
		return false;
	}

	int *visited = new int[num];			//构造一个访问与否标志位判别数组 0(没访问过)1(访问过)
	memset(visited, 0, sizeof(int)*num);	//将数组清零 也是初始化

	std::vector<VexNode> vec;			//定义一个向量 存储遍历到的顶点标号
	vec.push_back(grap->list[k]);		//将初始的顶点 压入到向量中 类型为VexNode
	std::cout << "\n" << vec[k].data;	//显示顶点处的信息 VecNode里面
	visited[k] = 1;						//设置初始位置的访问标志位为1

	PArcNode p = nullptr;				//定义一个遍历边链表的中间变量
/************************************************************************/
/*
遍历的思想(个人理解):
图结构的广度优先遍历与树结构的层次遍历具有相似的地方,下面的方法也是借鉴树结构的层次得来的。(回顾:在树的层次遍历中定义了cur和end两个
向量当前位置和长度标志变量,在第一层while中判别cur<vec.size();是遍历结束的条件也就是向量的元素个数不再增加。之后vec中cur到end元素的
左右孩子指针增加vec中元素的个数,直到最后遍历完成,vec中的元素不再增加cur=vec.size())。而在图结构中采用了回溯思想,每次取向量的最后
一个元素的头指针(访问过的顶点就删除出向量),以这个头指针为起点依次将没有走过的顶点存入向量中,也将数组中对应标志位置1。直到向量中的
元素为空,则图的广度优先遍历完成。
*/
/************************************************************************/
	while (!vec.empty())				//判断向量是否为空 若为非空则继续遍历
	{
		VexNode q = vec.back();			//取出向量最后的顶点
		p = q.head;						//将它的头结点的指针取出
		vec.pop_back();					//删除最后向量的最后一个元素		回溯思想
		while (p)						//判断p是不是空指针	
		{
			if (0 == visited[p->adjvex])	//判断p中存储的对应顶点编号有没有被访问过
			{
				std::cout << "\n" << grap->list[p->adjvex].data;	//显示对应顶点序号处 顶点的信息 data的值
				visited[p->adjvex] = 1;		//将访问过的顶点编号处的标志位赋值为1 (顶点标号与list里面的标号对应N*N的矩阵)
				vec.push_back(grap->list[p->adjvex]);	//将对应序号的顶点(VexNode)存入向量
			}
			p = p->next;	//指向下一个顶点编号
		}
	}

	return true;
}
    原文作者:数据结构之图
    原文地址: https://blog.csdn.net/m_buddy/article/details/50583658
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞