前言
本篇文章为笔者的读书笔记,未经允许请勿转载。如果对你有帮助记得点个赞(●’◡’●)
本文主要讲的图的两种实现方法,第一种是邻接矩阵,遍历采用的是dfs栈实现,第二种是邻接列表,遍历采用的是dfs递归实现。
源码如下:
main.cpp
#include <iostream>
#include"Graph.h"
#include"GraphList.hpp"
#include"GraphMatrix.hpp"
using namespace std;
void test()
{
GraphMatrix<char> g(undirector);
//添加元素
g.add('A');
g.add('B');
g.add('C');
g.add('D');
g.add('E');
g.add('F');
g.add('G');
//连接元素
g.link('A', 'B');
g.link('B', 'C',2);
g.link('C', 'D',3);
g.link('A', 'D',4);
g.link('A', 'E',5);
g.link('B', 'D',6);
g.link('E', 'G',7);
g.out();
//栈实现
g.dfs('A','C');
cout << boolalpha;
cout << g.isLink('A', 'B') << endl;
cout << g.isLink('V', 'A') << endl;
}
void test1()
{
GraphList<char> g(director);
g.add('A');
g.add('B');
g.add('C');
g.add('D');
g.add('E');
g.add('F');
g.add('G');
g.link('A', 'B');
g.link('B', 'C', 2);
g.link('C', 'D', 3);
g.link('A', 'D', 4);
g.link('A', 'E', 5);
g.link('B', 'D', 6);
g.link('E', 'G', 7);
//断开连接
g.unlink('E', 'G');
g.out();
//递归实现
g.dfs('A');
cout << boolalpha;
cout << g.isLink('A', 'B') << endl;
cout << g.isLink('V', 'A') << endl;
}
int main()
{
// test();
//test1();
system("pause");
return 0;
}
测试结果
test()如下
A B C D E F G
A 0 1 0 4 5 0 0
B 1 0 2 6 0 0 0
C 0 2 0 3 0 0 0
D 4 6 3 0 0 0 0
E 5 0 0 0 0 0 7
F 0 0 0 0 0 0 0
G 0 0 0 0 7 0 0
A E G A D C
true
false
test1()如下
A:(B,1)(D,4)(E,5)
B:(C,2)(D,6)
C:(D,3)
D:
E:
F:
G:
A B C D E
true
false
Graph.h
#pragma once
//虚基类
enum eMode {
director = 0, //有向图
undirector, //无向图
};//enum无作用域,enum class有作用域
template <class T>
class Graph
{
public:
virtual ~Graph() = default;//虚析构先子后父
//添加顶点
virtual void add(T&& c1) = 0;
//连接两个顶点
virtual bool link(T&& c1, T&& c2, int&& weight = 1) = 0;
//分离两个顶点
virtual bool unlink(T&& c1, T&& c2) = 0;
//两个顶点是否连接
virtual bool isLink(T&& c1, T&& c2) = 0;
};
GraphList.hpp
#pragma once
#include "Graph.h"
#include<list>
#include<vector>
#include<map>
#include<iostream>
template <class T>
class GraphList :public Graph<T>
{
public:
~GraphList() = default;
GraphList(eMode emod=undirector, size_t&& length=5) :emod(emod), length(length)
{
//初始化二维vector
list.resize(length);
}
void dfs(T&& start,T&& end='\0')
{
destination = end;
mark = false;//终点标志位置0
//路径标志位置0
for (const auto& e : vec)
{
m.insert({ e,false });
}
_dfs(start);
std::cout << '\n';
}
void out()
{
for (size_t i = 0; i < vec.size(); i++)
{
std::cout << vec[i] << ':';
for (const auto& e : list[i])
{
std::cout << "(" << e.first << "," << e.second << ")";
}
std::cout << '\n';
}
}
// 通过 Graph 继承
virtual void add(T&& c1) override
{
vec.push_back(c1);
if (vec.size() > length)
{
length += (length >> 1) > 1 ? (length >> 1) : 1;//当新增后vec的length大于现有length,就将length扩大1.5倍。
list.resize(length);//重新初始化二维vector
}
}
virtual bool link(T&& c1, T&& c2, int&& weight = 1) override
{
int i1 = _get(c1);//获取c1下标
int i2 = _get(c2);
//c1,c2坐标不合法就返回false
if (!_check(i1, i2))
{
return false;
}
if (emod == undirector)
{
list[i1].push_back(std::make_pair( c2,weight ));//这两种写法都可以
list[i2].push_back({ c1,weight });
}
else
{
list[i1].push_back({ c2,weight });
}
return true;
}
virtual bool unlink(T&& c1, T&& c2) override
{
int i1 = _get(c1);
int i2 = _get(c2);
if (!_check(i1, i2))
{
return false;
}
if (emod == undirector)
{
for (auto i = list[i1].begin(); i != list[i1].end();)
{
if ((*i).first == c2)
{
i = list[i1].erase(i);//erase返回值指向下一个元素
}
else
{
i++;
}
}
for (auto i = list[i2].begin(); i != list[i2].end();)//迭代器的返回值是一个右值,无法对其取别名也就是不能auto&=list[i2].begin();
{
if ((*i).first == c1)
{
//没有给模板写示例模板参数,没运行时候编译器不会识别erase。
list[i2].erase(i++);//这种写法和上面写法效果一至,一定是后置++,i的旧值先被传入(拷贝传入),传入的一瞬间(意思是erase还没执行操作),i就已经指向下一个迭代器。具体请参考effective stl。
/*不能写成
list[i2].erase(i);
i++;*/
}
else
{
i++;
}
}
}
else
{
for (auto i = list[i1].begin(); i != list[i1].end();)
{
if ((*i).first == c2)
{
i = list[i1].erase(i);
}
else
{
i++;
}
}
}
return false;
}
virtual bool isLink(T&& c1, T&& c2) override
{
int i1 = _get(c1);
int i2 = _get(c2);
if (!_check(i1, i2))
{
return false;
}
if (emod == undirector)
{
for (const auto& e : list[i1])
{
if (e.first == c2)
{
return true;
}
}
for (const auto& e : list[i2])
{
if (e.first == c1)
{
return true;
}
}
}
else
{
for (const auto& e : list[i1])
{
if (e.first == c2)
{
return true;
}
}
}
return false;
}
private:
eMode emod;
std::vector < std::list < std::pair<T,int>>> list;//用邻接列表来存储
std::vector<T> vec;//存储顶点元素
std::map < T, bool > m;//路径标志位
size_t length;//最大顶点长度
bool mark;//终点标志位
T destination;//目的地
int _get(const T& c1)
{
for (size_t i = 0; i < vec.size(); i++)
{
if (c1 == vec[i])
{
return i;
}
}
return -1;
}
bool _check(const int& i1, const int& i2)
{
if (i1 == -1 || i2 == -1 || i1 == i2)
{
return false;
}
return true;
}
void _dfs(const T& start)
{
std::cout << start << ' ';
if (start == destination)
{
mark = true;
return;
}
int i1 = _get(start);
for (const auto& e : list[i1])
{
if (!mark&&!m.at(e.first))
{
m[e.first] = true;
_dfs(e.first);
}
}
}
};
GraphMatrix.hpp
#pragma once
#include "Graph.h"
#include<vector>
#include<stack>
#include<map>
#include<iostream>
template <class T>
class GraphMatrix : public Graph<T>
{
public:
~GraphMatrix() = default;
GraphMatrix(eMode emode=undirector, size_t&& length=5):emode(emode),length(length)
{
matrix.resize(length, std::vector<int>(length));
}
//栈实现,c1起始点,c2终点,终点不填表示遍历所有
void dfs(T&& start, T&& end='\0')
{
std::map<T, bool> m;//map的作用是防止出现死循环
for (const auto& e : vec)
{
m.insert({ e, false });
}
std::stack<T> st;
st.push(start);
size_t row;//定义一个row来获取该字符在vec中的下标,同时它也是matrix的行坐标。
while (!st.empty())
{
const auto& ch= st.top();
st.pop();
std::cout << ch << " ";//第一个A没有将标志位置一,所以A出现了两次
if (ch == end)
{
std::cout << '\n';
return;
}
row = _get(ch);
for(size_t i=0;i<vec.size();i++)
{
if (matrix[row][i] >= 1 && !m.at(vec[i]))
{
m.at(vec[i]) = true;
st.push(vec[i]);
}
}
}
std::cout << '\n';
}
// 通过 Graph 继承
//添加顶点
virtual void add(T&& c1) override
{
vec.push_back(c1);
if (vec.size() > length)
{
//每次增加之前的一半
length += (length >> 1) > 1 ? (length >> 1) : 1;
matrix.resize(length);
for (auto& e : matrix)
e.resize(length);
}
}
//连接两个顶点
virtual bool link(T&& c1, T&& c2, int&& weight = 1) override
{
int i1 = _get(c1);
int i2 = _get(c2);
if (!_check(i1, i2))
{
return false;
}
if (emode == director)
{
matrix[i1][i2] = weight;
}
else
{
matrix[i1][i2] = weight;
matrix[i2][i1] = weight;
}
return true;
}
//拆开两个顶点
virtual bool unlink(T&& c1, T&& c2) override
{
int i1 = _get(c1);
int i2 = _get(c2);
if (!_check(i1, i2))
{
return false;
}
if (emode == director)
{
matrix[i1][i2] = 0;
}
else
{
matrix[i1][i2] = 0;
matrix[i2][i1] = 0;
}
return true;
}
//两个顶点是否连接
virtual bool isLink(T&& c1, T&& c2) override
{
int i1 = _get(c1);
int i2 = _get(c2);
if (!_check(i1, i2))
{
return false;
}
if (matrix[i1][i2]>=1||matrix[i2][i1]>=1)
{
return true;
}
return false;
}
//输出
void out()
{
std::cout <<'\t';
for (size_t e = 0; e < vec.size(); e++)
{
std::cout << vec[e] << '\t';
}
std::cout << std::endl;
for (size_t i = 0; i < vec.size(); i++)
{
std::cout << vec[i] << '\t';
for (size_t j = 0; j < matrix[i].size(); j++)
{
std::cout << matrix[i][j] << '\t';
}
std::cout << std::endl;
}
}
private:
std::vector<T> vec;//顶点容器
std::vector<std::vector<int>> matrix;//邻接矩阵
size_t length;//最大顶点长度
eMode emode;
//获取下标,不存在就返回-1
int _get(const T& c1)
{
for (size_t i = 0; i < vec.size(); i++)
{
if (c1 == vec[i])
{
return i;
}
}
return -1;
}
//判断位置是否有效
bool _check(const int& i1,const int& i2)
{
if (i1 == -1 || i2 == -1 || i1 == i2)
{
return false;
}
return true;
}
};