数据结构之图(6)

一、概念

图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;
}
相关推荐
Schwertlilien3 分钟前
图像处理-Ch1-数字图像基础
图像处理·人工智能·算法
程序员一诺3 分钟前
【深度学习】嘿马深度学习笔记第10篇:卷积神经网络,学习目标【附代码文档】
人工智能·python·深度学习·算法
刚学HTML2 小时前
leetcode 05 回文字符串
算法·leetcode
Yan.love2 小时前
开发场景中Java 集合的最佳选择
java·数据结构·链表
AC使者2 小时前
#B1630. 数字走向4
算法
冠位观测者2 小时前
【Leetcode 每日一题】2545. 根据第 K 场考试的分数排序
数据结构·算法·leetcode
古希腊掌管学习的神3 小时前
[搜广推]王树森推荐系统笔记——曝光过滤 & Bloom Filter
算法·推荐算法
qystca3 小时前
洛谷 P1706 全排列问题 C语言
算法
浊酒南街3 小时前
决策树(理论知识1)
算法·决策树·机器学习
就爱学编程3 小时前
重生之我在异世界学编程之C语言小项目:通讯录
c语言·开发语言·数据结构·算法