您的位置:首页 > 其它

二维动态数组与图的遍历

2016-01-25 22:54 375 查看

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: