一、概念
图G有顶点集V和边集E组成,记为G=(V,E),其中V(G)表示图G中顶点的有限非空集;
E(G)表示图G中顶点之间的关系(边)集合。
若V={v1,v2,...,vn},则用|V|表示图G中顶点的个数。也称为图G的 阶,
E={(u,v)|u ∈V},用|E|表示图G中边的条数
图不可以是空,V一定是非空集
无向图
E是无向边(简称边)的有限集合
有向图
E是有向边(弧)的有限集合
弧是顶点的有序对,记为<v,w>,
v为弧尾,w为弧头
简单图
不存在重复边
不存在顶点到自身的边
多重图
某两个结点之间的边数多于一条,又允许顶点通过同一条边和自己关联
顶点的度、入度、出度
无向图:TD(v)
有向图:入度:ID(v) 出度:OD(v) TD(v)=ID(v)+OD(v)
顶点---顶点的关系描述
路径
回路
简单路径---顶点不重复出现
简单回路---除第一个顶点和最后一个顶点外,其余顶点不重复出现的回路
路径长度
点到点的距离
从顶点u出发到顶点w有路径存在,则此路径的长度称为从u到v的距离
若不存在路径,则为∞
连通
无向图v到w存在路径,v和w连通
强连通
y有向图,v到w和w到v都有路径
子图
V'是V的子集,E'是E的子集
生成子图:若有满足V(G')=V(G)的子图G'
连通分量
无向图中的极大连通子图(子图必须连通,且包含尽可能多的顶点和边)
强连通分量
有向图
生成树
连通图的生成树是包含图中全部顶点的一个极小连通图
若图中顶点数为n,则它的生成树含有n-1条边
对于生成树而言,砍去它的一条边,则会变成非连通图,加上一条边则会形成一个回路
边尽可能的少,但要保持连通
边的权、带权图/网
几种特殊形态的图
无向完全图:
无向图任意两个顶点之间都存在边
若无向图的顶点数|V|=n,则|E|[0,]=[0,n(n-1)/2]
有向完全图:
有向图中任意两个顶点之间都存在方向相反的两条弧
若有向图的顶点数|V|=n,则|E|[0,2]=[0,n(n-1)]
稀疏图
稠密图
树------不存在回路,且连通的无向图
n个顶点的树,必有n-1条边
考点:n个顶点的图,若|E|>n-1,则一定有回路
有向树
一个顶点的入度为0、其余顶点的入度为1的有向图
二、图的结构
Graph.h
cpp
#define DefaultVertices 50
template <class T>
class Graph {
protected:
int maxVertices;
int numEdges;
int numVertices;
public:
Graph(int sz = DefaultVertices) :maxVertices(sz), numEdges(0), numVertices(0) {}
~Graph() {}
bool GraphEmpty() {
if (numEdges == 0) return true;
else return false;
}
bool GraphFull() {
if (numVertices == maxVertices || numEdges == maxVertices * (maxVertices - 1) / 2) return true;
else return false;
}
int NumberOfVertices() { return numVertices; }
int NumberOfEdges() { return numEdges; }
virtual T getValue(int i) = 0;
virtual int getVertexPos(T vertex) = 0;
virtual int getFirstNeighbor(int v) = 0;
virtual int getNextNeighbor(int v, int w) = 0;
virtual int getWeight(int v1, int v2) = 0;
virtual bool insertVertex(T& vertex) = 0;
virtual bool insertEdge(int v1, int v2, int choice) = 0;
virtual bool removeVertex(int v) = 0;
virtual bool removeEdge(int v1, int v2) = 0;
};
三、邻接矩阵法
Graphmtx.h
1、定义图的结构
cpp
template <class T>
class Graphmtx :public Graph<T> {
private:
T* VerticesList;
int** Edge;
public:
Graphmtx(int sz = DefaultVertices);
~Graphmtx() {
delete[] VerticesList;
delete[] Edge;
}
T getValue(int i) {
return i >= 0 && i <= this->numVertices ? VerticesList[i] : NULL;
}
int getVertexPos(T vertex) {
for (int i = 0; i < this->numVertices; i++) {
if (VerticesList[i] == vertex) return i;
}
return -1;
}
int getWeight(int v1, int v2) {
return v1 != -1 && v2 != -1 ? Edge[v1][v2] : 0;
}
int getFirstNeighbor(int v);
int getNextNeighbor(int v, int w);
bool insertVertex(T& vertex);
bool insertEdge(int v1, int v2, int choice);
bool removeVertex(int v);
bool removeEdge(int v1, int v2);
};
2、创建图
cpp
template <class T>
Graphmtx<T>::Graphmtx(int sz) :Graph<T>(sz) {
int i, j;
VerticesList = new T[this->maxVertices];
Edge = (int**)new int*[this->maxVertices];
for (i = 0; i < this->maxVertices; i++) {
Edge[i] = new int[this->maxVertices];
}
for (i = 0; i < this->maxVertices; i++) {
for (j = 0; j < this->maxVertices; j++) {
Edge[i][j] = 0;
}
}
}
3、添加点和边
cpp
template <class T>
bool Graphmtx<T>::insertVertex(T& vertex) {
if (this->numVertices == this->maxVertices) return false;
VerticesList[this->numVertices++] = vertex;
return true;
}
template <class T>
bool Graphmtx<T>::insertEdge(int v1, int v2, int choice) {
if (v1 > -1 && v1<this->numVertices && v2>-1 && v2 < this->numVertices && Edge[v1][v2] == 0) {
Edge[v1][v2] = 1;
if (!choice) {
Edge[v2][v1] = 1;
}
this->numEdges++;
return true;
}
else return false;
}
4、删除
cpp
template <class T>
bool Graphmtx<T>::removeVertex(int v) {
if (v<0 || v>this->numVertices) return false;
if (this->numVertices == 1) return false;
int i, j;
VerticesList[v] = VerticesList[this->numVertices - 1];
for (i = 0; i < this->numVertices; i++) {
if (Edge[i][v] == 1) this->numEdges--;
}
for (i = 0; i < this->numVertices; i++) {
Edge[i][v] = Edge[i][this->numVertices - 1];
}
this->numVertices--;
for (j = 0; j < this->numVertices; j++) {
Edge[v][j] = Edge[this->numVertices][j];
}
return true;
}
template <class T>
bool Graphmtx<T>::removeEdge(int v1, int v2) {
if (v1 > -1 && v1<this->numVertices && v2>-1 && v2 < this->numVertices && Edge[v1][v2] == 1) {
Edge[v1][v2] = Edge[v2][v1] = 0;
this->numEdges--;
return true;
}
else return false;
}
5、获取值
cpp
template <class T>
int Graphmtx<T>::getFirstNeighbor(int v) {
if (v != -1) {
for (int col = 0; col < this->numVertices; col++) {
if (Edge[v][col] == 1)
return col;
}
}
return -1;
}
template <class T>
int Graphmtx<T>::getNextNeighbor(int v, int w) {
if (v != -1 && w != -1) {
for (int col = w + 1; col < this->numVertices; col++) {
if (Edge[v][col] == 1)
return col;
}
}
return -1;
}
四、邻接表法
Graphlnk.h
1、定义图的结构
cpp
#include "graph.h"
template<class T>
struct Edge {
int dest;//目标顶点
int cost;//边的权重
Edge<T>* link;//指向下一条边的指针
Edge(){}
Edge(int num,int weight):dest(num),cost(weight),link(NULL){}
bool operator != (Edge<T>& R)const {//基于目标顶点比较两个Edge对象是否不同
return (dest != R.dest) ? true : false;
}
};
template<class T>
struct Vertex {
T data;
Edge<T>* adj;//指向顶点的第一条边
};
cpp
template <class T>
class Graphlnk :public Graph<T> {
private:
Vertex<T>* NodeTable;
public:
Graphlnk(int sz = DefaultVertices);
~Graphlnk();
T getValue(int i) {
return (i >= 0 && i < this->numVertices) ? NodeTable[i].data : 0;
}
int getVertexPos(T vertex) {
for (int i = 0; i < this->numVertices; i++) {
if (NodeTable[i].data == vertex) return i;
}
return -1;
}
int getFirstNeighbor(int v);
int getNextNeighbor(int v, int w);
int getWeight(int v1, int v2);
bool insertVertex(T& vertex);
bool insertEdge(int v1, int v2, int choice);
bool removeVertex(int v);
bool removeEdge(int v1, int v2);
};
2、创建和销毁图
cpp
template <class T>
Graphlnk<T>::Graphlnk(int sz) :Graph<T>(sz) {
NodeTable = new Vertex<T>[this->maxVertices];
if (NodeTable == NULL) {
cerr << "存储分配错误" << endl; exit(1);
}
for (int i = 0; i < this->maxVertices; i++) {
NodeTable[i].adj = NULL;
}
}
template <class T>
Graphlnk<T>::~Graphlnk() {
for (int i = 0; i < this->numVertices; i++) {
Edge<T>* p = NodeTable[i].adj;
while (p != NULL) {
NodeTable[i].adj = p->link;
delete p;
p = NodeTable[i].adj;
}
}
delete[] NodeTable;
}
3、添加点和边
cpp
template <class T>
bool Graphlnk<T>::insertVertex(T& vertex) {
if (this->numVertices == this->maxVertices) return false;
NodeTable[this->numVertices].data = vertex;
this->numVertices++;
return true;
}
template <class T>
bool Graphlnk<T>::insertEdge(int v1, int v2, int choice) {//choice 是否创建有向边
if (v1 >= 0 && v1 < this->numVertices && v2 >= 0 && v2 < this->numVertices) {
Edge<T>* q, * p = NodeTable[v1].adj;
while (p != NULL && p->dest != v2) p = p->link;//找到从顶点v1出发的最后一条边
if (p != NULL) return false;
p = new Edge<T>;
p->dest = v2;
p->cost = 1;
p->link = NodeTable[v1].adj;
NodeTable[v1].adj = p;
q = new Edge<T>;
if (!choice) {
q->dest = v1;
q->cost = 1;
q->link = NodeTable[v2].adj;
NodeTable[v2].adj = q;
}
this->numEdges++;
return true;
}
return false;
}
4、删除
cpp
template <class T>
bool Graphlnk<T>::removeVertex(int v) {
if (this->numVertices == 1 || v < 0 || v >= this->numVertices) return false;
Edge<T>* p, * s, * t;
int k;
while (NodeTable[v].adj != NULL) {
p = NodeTable[v].adj;
k = p->dest;
s = NodeTable[k].adj;
t = NULL;
while (s != NULL && s->dest != v) {
t = s;
s = s->link;
}
if (s != NULL) {
if (t == NULL) NodeTable[k].adj = s->link;
else t->link = s->link;
delete s;
}
NodeTable[v].adj = p->link;
delete p;
this->numEdges--;
}
this->numVertices--;
NodeTable[v].data = NodeTable[this->numVertices].data;
p = NodeTable[v].adj = NodeTable[this->numVertices].adj;
while (p != NULL) {
s = NodeTable[p->dest].adj;
while (s != NULL) {
if (s->dest == this->numVertices) {
s->dest = v; break;
}
else {
s = s->link;
}
}
}
return true;
}
template <class T>
bool Graphlnk<T>::removeEdge(int v1, int v2) {
if (v1 != -1 && v2 != -1) {
Edge<T>* p = NodeTable[v1].adj, * q = NULL, * s = p;
while (p != NULL && p->dest != v2) {
q = p;
p = p->link;
}
if (p != NULL) {
if (p == s) NodeTable[v1].adj = p->link;
else q->link = p->link;
delete p;
}
else return false;
p = NodeTable[v2].adj;
q = NULL;
s = p;
while (p->dest != v1) {
q = p;
p = p->link;
}
if (p == s) NodeTable[v2].adj = p->link;
else q->link = p->link;
delete p;
return true;
}
return false;
}
5、获取值
cpp
template <class T>
int Graphlnk<T>::getFirstNeighbor(int v) {
if (v != -1) {
Edge<T>* p = NodeTable[v].adj;
if (p != NULL) return p->dest;
}
return -1;
}
template <class T>
int Graphlnk<T>::getNextNeighbor(int v, int w) {
if (v != -1) {
Edge<T>* p = NodeTable[v].adj;
while (p != NULL && p->dest != w) p = p->link;
if (p != NULL && p->link != NULL) return p->link->dest;
}
return -1;
}
template <class T>
int Graphlnk<T>::getWeight(int v1, int v2) {
if (v1 != -1 && v2 != -1) {
Edge<T>* p = NodeTable[v1].adj;
while (p != NULL && p->dest != v2) p = p->link;
if (p != NULL) return p->cost;
}
return 0;
}
五、搜索
1、深度优先搜索
cpp
//深度优先遍历
template <class T>
void DFS(Graph<T>& G, const T& v) {
int i, loc, n = G.NumberOfVertices();
bool* visited = new bool[n];
for (i = 0; i < n; i++) {
visited[i] = false;
}
loc = G.getVertexPos(v);//找到结点的索引
DFS(G, loc, visited);
cout << endl;
delete[] visited;
}
template <class T>
void DFS(Graph<T>& G, int v, bool visited[]) {
cout << G.getValue(v) << " "; //访问顶点
visited[v] = true;
int w = G.getFirstNeighbor(v);//第一个点
while (w != -1) {
if (visited[w] == false) DFS(G, w, visited);
w = G.getNextNeighbor(v, w);//接下来的点
}
}
2、广度优先搜索
cpp
template <class T>
void BFS(Graph<T>& G, const T& v)
{
int i, w, n = G.NumberOfVertices();
bool* visited = new bool[n];
for (i = 0; i < n; i++) {
visited[i] = false;
}
int loc = G.getVertexPos(v);//找到结点的索引
cout << G.getValue(loc) << " ";
visited[loc] = true;
queue<int>Q;
Q.push(loc);
while (!Q.empty())
{
loc = Q.front();
Q.pop();
w = G.getFirstNeighbor(loc);
while (w != -1)
{
if (visited[w] == false) {
cout << G.getValue(w) << " ";
visited[w] = true;
Q.push(w);
}
w = G.getNextNeighbor(loc, w);
}
}
cout << endl;
delete[] visited;
}