数据结构与算法学习笔记(图)

1.图的结构

 

图会包括点的集合与边的集合,然后单独去构建出点和边的类
所以图一共会联系三个类  分别为Node点类,Edge边类,Graphic图类

class Node;
class Edge;
class Graph;

class Edge
{
public :
	int weight;//权值
	Node* from;//这个边的初始点
	Node* to;//这个边的终点
}

class Node
{
public :
	int val;  //这个点的值,可以是string,或其他类型
	int in; //入度
	int out; //出度
	list<Node*> nexts; // 所有与这个点相连的下一个点
	list<Edge*> edges; //所有与这个点有联系的边

	Node(int val) : val(val)
	{
		in = 0;
		out = 0;
		nexts = {};
		edges = {};
	}
}

class Graph
{
public :
	//int为对应的值,可以是string
	unordered_map<int, Node*> m_map;
	unordered_set<Edge*> m_set;
	Graph()
	{
		m_map = {};
		m_set = {};
	}
}

2.将给定结构转为这个结构


比如给一个二维数组,其中0代表权值,1代表出发点,2代表目的点
{
    {1,2,3}
    {4,5,6}
}
,将其转化为Graph结构

Graph* Transformation(const vector<vector<int>> &v)
{
    //先构建点,然后构建边,然后将边和点都加入到我们万能Graph中
    Graph* graph = new Graph();


    for(int i = 0; i < v.size(); i ++)
    {
        int weight = v[i][0], from = v[i][1], to = v[i][2];
        //构建点Node并将Node加入到graph中
        if(graph->m_map.count(from) == 0) graph->m_map[from] = new Node(from);
        if(graph->m_map.count(to) == 0) graph->m_map[to] = new Node(to);

        //构建边
        Node* from = graph->m_map[from];
        Node* to = graph->m_map[to];
        
        Edge* edge = new edge(weight, from, to);

        //if(graph->m_set.count(edge) == 0) 
        graph->m_set.insert(edge);
        //出入度以及边集的调整
        from->out ++;
        to->in ++;
        from->nexts.push_back(to);
    }
    return graph;
}

3.广度遍历,给定某个图的某个节点,以这个节点出发,广度遍历这个图

 

8a0fba77b5fd4cbb84704f3af25f5f4f.png

广度遍历是先将与这个点的相邻的点都先打印一遍,在打印的过程中,要加入其它点连接的点

首先将A打印,然后打印A的下一个节点BCD,在打印B的情况下要记录E的位置,之后在打印完BCD之后打印E。如何记录E的位置,队列先进先出就比较符合,先将A加入队列,然后将ABC加入队列,然后再将E加入队列即可。

如何保证在打印C的时候不重复将B加入队列呢(因为是找的与C相邻的点,所以可能在打印C时加入B)?可以用一个哈希表来辅助。每次打印完一个节点后,就将其加入到这个哈希表中,在将节点加入队列前先判断这个节点是否已经在哈希表中了,如果在的话跳过即可。

void DFS(Node* node)
{
    //创建一个队列,以及一个set,其中set用于记录某个节点是否添加到过队列中
    queue<Node*> qu;
    unordered_set<Node*> se;
    qu.emplace(node);
    se.emplace(node);

    while(!qu.empty()) //队列如果不空,那么就将队列首个元素弹出,并且记录,然后遍历这个元素的所有nexts,如果里边的元素没有存在set中,证明还没加入过队列
    {
        Node* temp = qu.front();
        cout << temp->val << " ";  //出队打印即可

        for(auto i : temp->nexts)
        {
            if(se.count(i) == 0)//证明还没有加入过队列
            {    
                se.emplace(i);
                qu.emplace(i);
            }
        }
    }
}

4.深度优先遍历


507429791e2f44569637759833fa91fa.png

深度优先遍历是找到某一个点,然后一直向下打印,直到这个点已经找不到下一个点了,然后找另一条路,直到没有路可走了。

在这里就是打印A,从A找一条路,可以是B或C或D,这里找B,B下面可以是E或C,这里选C,然后接着走是D,之后发现没路了,回退一个,发现C也没路了,继续回退到B,B发现还有E,打印E,然后E没路了,回退到B,B没路了回退到A,A发现也没路了,因为C和D都已经打印过了,A没办法回退了,直接结束

如何做到回退,可以用栈去记录节点,比如打印ABCD这条路,我们压栈ABCD,D继续发现没路了,将D弹出,还能得到C,C没路了弹出得到B,B找到E将E入栈,变为ABE,之后ABE都被弹出,栈空了直接返回就行

同时用一个哈希表记录是否已经打印过了

void DFS(Node* node)
{
    //深度优先遍历需要一个栈去记录当前打印的顺序,set仍然是记录Node是否进入过栈
    stack<Node*> st;
    unordered_set<Node*> se;

    st.emplace(node);
    cout << st.top()->val << " ";//进栈打印
    se.emplace(node);

    while(!st.empty())
    {
        //首先存储栈顶的Node
        Node* temp = st.top();
        st.pop();
        for(auto i : temp->nexts())
        {
            if(se.count(i) == 0) //证明还没有加入过栈中
            {
                st.emplace(temp);
                st.emplace(i);
                cout << st.top()->val << " ";//进栈打印
                se.emplace(i);
                break;
            }    
        }
    }
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值