1、定义:图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。
2、由定义可知,图的邻接矩阵存储方式是用两个数据来表示。一个数据中存储图中顶点信息,另一个数据中(称为邻接矩阵)存储图中的边的信息。见下图:(图片来源于《大话数据结构》)
3、图的其它定义:
1) 有向图与无向图;完全图;弧和边;入度和出度;连通图与非连通图---自行查看相关定义。
4、图的遍历:
1)图的深度遍历:
图的深度优先遍历类似于二叉树的深度优先遍历,其基本思想是:从图中某个顶点v出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到。显然,这是一个递归的搜索过程。
在上图中我们坚持沿着右手边走,如果碰见标记的跳过,选择另一条。到最后时再返回遍历确认是否都有标记。其实就是树的前序遍历。以上图为例,假定A是出发点,首先访问A。这时两个邻接点B、F均未被访问,右手边为B,访问B之后,按照右手原则遍历到C,相同原理A→B→C→D→E→F,在F上向右为A,因为A遍历过,则访问G。最后到H,再走发现到D,E,两者都访问了。关键此时,还有没访问到的,按照原路返回,返回到G,一条条返回,最后得到的访问序列为A→B→C→D→E→F→G→H→I。
2)图的广度优先遍历:
图的广度优先遍历算法是一个分层遍历的过程,和二叉树的广度优先遍历类似,其基本思想在于:从图中的某一个顶点Vi触发,访问此顶点后,依次访问Vi的各个为层访问过的邻接点,然后分别从这些邻接点出发,直至图中所有顶点都被访问到。广度优先遍历类似树的层序遍历。
首先访问v1 和v1 的邻接点v2 和v3,然后依次访问v2 的邻接点v4 和v5 及v3 的邻接点v6 和v7,最后访问v4 的邻接点v8。由于这些顶点的邻接点均已被访问,并且图中所有顶点都被访问,由些完成了图的遍历。得到的顶点访问序列为:
v1→v2 →v3 →v4→ v5→ v6→ v7 →v8
5、图的C++实现:
.h文件:
#pragma once
#include <vector>
using namespace std;
/* 一个无向图:假设无向图的权值都为1
A
/ \
B D
/ \ / \
C F G H
\/
E
*/
/* 对应的邻接矩阵:
A B C D E F G H
A 0 1 0 1 0 0 0 0
B 1 0 1 0 0 1 0 0
C 0 1 0 0 1 0 0 0
D 1 0 0 0 0 0 1 1
E O O 1 O O 1 O O
F O 1 O O 1 O O O
G 0 0 0 1 0 0 0 0
H O O O 1 O O O O
*/
class CNode
{
public:
CNode(char cData = 0);
public:
char m_cData;
bool m_bIsVisited;
};
class CZzcGrapha
{
public:
CZzcGrapha(int nCapacity);
~CZzcGrapha(void);
bool AddNodeToGrapha(CNode* pNode); //向图中增加节点
void ResetNodeVisitFlag(); //将所有节点的访问标识置为false
bool SetValueToMatrixForDirectedGraph(int row,int col,int value = 1);//向有向图矩阵中设置值
bool SetValueToMatrixForUnDirectedGraph(int row,int col,int value = 1);//向无向图矩阵设置值
bool GetValueFromMatrix(int row,int col,int& value);//从邻接矩阵中获取值
void PrintMatrix();//打印出图的邻接矩阵
void DepthFirstTraverse(int nodeindex);//深度优先遍历
void WidthFirstTraverse(int nodeindex);//广度优先遍历
void WidthTraverseIteration(vector<int> prevec);
private:
int m_nCapacity; //图的容量(可以容纳的节点数)
int m_nCurNodeCount; //图中当前的节点个数
CNode* m_pNodeArray; //用来存放定点数据
int* m_pMatrix; //用来存放邻接矩阵数据
};
.cpp文件:
#include "StdAfx.h"
#include "ZzcGrapha.h"
#include "Windows.h"
#include <iostream>
using namespace std;
CNode::CNode(char cData)
{
m_cData = cData;
m_bIsVisited = false;
}
CZzcGrapha::CZzcGrapha(int nCapacity)
{
m_nCapacity = nCapacity;
m_nCurNodeCount = 0;
m_pNodeArray = new CNode[m_nCapacity];
m_pMatrix = new int[m_nCapacity * m_nCapacity];
ZeroMemory(m_pMatrix,m_nCapacity * m_nCapacity * sizeof(int));
}
CZzcGrapha::~CZzcGrapha(void)
{
if (m_pNodeArray)
{
delete[]m_pNodeArray;
m_pNodeArray = NULL;
}
if (m_pMatrix)
{
delete[]m_pMatrix;
m_pMatrix = NULL;
}
}
bool CZzcGrapha::AddNodeToGrapha(CNode* pNode)
{
if(pNode == NULL) return false;
m_pNodeArray[m_nCurNodeCount].m_cData = pNode->m_cData;
m_nCurNodeCount++;
return true;
}
void CZzcGrapha::ResetNodeVisitFlag()
{
for(int i = 0;i < m_nCapacity;i++)
{
m_pNodeArray[i].m_bIsVisited = false;
}
}
bool CZzcGrapha::SetValueToMatrixForDirectedGraph(int row,int col,int value)
{
if(row < 0||row >= m_nCapacity) return false;
if(col < 0||col >= m_nCapacity) return false;
m_pMatrix[m_nCapacity * row + col] = value;
return true;
}
bool CZzcGrapha::SetValueToMatrixForUnDirectedGraph(int row,int col,int value)
{
if(row < 0||row >= m_nCapacity) return false;
if(col < 0||col >= m_nCapacity) return false;
m_pMatrix[m_nCapacity * row + col] = value;
m_pMatrix[m_nCapacity * col + row] = value;
return true;
}
bool CZzcGrapha::GetValueFromMatrix(int row,int col,int& value)
{
if(row < 0||row >= m_nCapacity) return false;
if(col < 0||col >= m_nCapacity) return false;
value = m_pMatrix[m_nCapacity * row + col];
return true;
}
void CZzcGrapha::PrintMatrix()
{
for (int i = 0;i < m_nCapacity;i++)
{
for (int k = 0;k < m_nCapacity;k++)
{
cout<<m_pMatrix[m_nCapacity * i + k]<<" ";
}
cout<<endl;
}
}
void CZzcGrapha::DepthFirstTraverse(int nodeindex)
{
int value = 0;
cout<<m_pNodeArray[nodeindex].m_cData<<" ";
m_pNodeArray[nodeindex].m_bIsVisited = true;
for (int i = 0;i < m_nCapacity;i++)
{
GetValueFromMatrix(nodeindex,i,value);
if (value == 1)
{
if(m_pNodeArray[i].m_bIsVisited == true) continue;
DepthFirstTraverse(i);
}
else
{
continue;
}
}
}
void CZzcGrapha::WidthFirstTraverse(int nodeindex)
{
cout<<m_pNodeArray[nodeindex].m_cData<<" ";
m_pNodeArray[nodeindex].m_bIsVisited = true;
vector<int> curVec;
curVec.push_back(nodeindex);
WidthTraverseIteration(curVec);
}
void CZzcGrapha::WidthTraverseIteration(vector<int> prevec)
{
int value = 0;
vector<int> curVec;
for(int i = 0;i < (int)prevec.size();i++)
{
for (int j = 0;j < m_nCapacity;j++)
{
GetValueFromMatrix(prevec[i],j,value);
if (value != 0)
{
if(m_pNodeArray[j].m_bIsVisited) continue;
cout<<m_pNodeArray[j].m_cData<<" ";
m_pNodeArray[j].m_bIsVisited = true;
curVec.push_back(j);
}
else
{
continue;
}
}
}
if(curVec.size() == 0) return;
WidthTraverseIteration(curVec);
}
测试:
// 图.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "ZzcGrapha.h"
#include <iostream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
CZzcGrapha* pGrapha = new CZzcGrapha(8);
CNode* pNodeA = new CNode('A');
CNode* pNodeB = new CNode('B');
CNode* pNodeC = new CNode('C');
CNode* pNodeD = new CNode('D');
CNode* pNodeE = new CNode('E');
CNode* pNodeF = new CNode('F');
CNode* pNodeG = new CNode('G');
CNode* pNodeH = new CNode('H');
pGrapha->AddNodeToGrapha(pNodeA);
pGrapha->AddNodeToGrapha(pNodeB);
pGrapha->AddNodeToGrapha(pNodeC);
pGrapha->AddNodeToGrapha(pNodeD);
pGrapha->AddNodeToGrapha(pNodeE);
pGrapha->AddNodeToGrapha(pNodeF);
pGrapha->AddNodeToGrapha(pNodeG);
pGrapha->AddNodeToGrapha(pNodeH);
pGrapha->SetValueToMatrixForUnDirectedGraph(0,1);
pGrapha->SetValueToMatrixForUnDirectedGraph(0,3);
pGrapha->SetValueToMatrixForUnDirectedGraph(1,2);
pGrapha->SetValueToMatrixForUnDirectedGraph(1,5);
pGrapha->SetValueToMatrixForUnDirectedGraph(3,6);
pGrapha->SetValueToMatrixForUnDirectedGraph(3,7);
pGrapha->SetValueToMatrixForUnDirectedGraph(6,7);
pGrapha->SetValueToMatrixForUnDirectedGraph(2,4);
pGrapha->SetValueToMatrixForUnDirectedGraph(4,5);
pGrapha->PrintMatrix();
cout<<endl;
pGrapha->DepthFirstTraverse(0);
cout<<endl;
pGrapha->ResetNodeVisitFlag();
pGrapha->WidthFirstTraverse(0);
//......delete操作省略......
return 0;
}