C++图的实现


前言

本篇文章为笔者的读书笔记,未经允许请勿转载。如果对你有帮助记得点个赞(●’◡’●)
本文主要讲的图的两种实现方法,第一种是邻接矩阵,遍历采用的是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;
	}
};
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值