刷题(11)-图-(0)

一些概念的介绍。

数据结构与算法总结——图 | 无境

图的表示

  • 稀疏图(顶点数大于边数), 用邻接表(邻接链表数组, e.g A[2]里面放的是一个链表,是和节点2相连的节点)
  • 稠密图(顶点数小于边数), 用邻接矩阵 (n个接点,nxn个矩阵,假如i和j相连,A【i】【j】, A【j】【i】 为true)

图的分类

  • 有向/无向
  • 加权/不加权

  上面两两组合, 所以一共可以有4种图

图的邻接表 表示

 一般给的输入是edges= [[1,0],[2,0],[3,1],[3,2]] 和 节点个数 num
 
  1) 无向图
      a) 给了节点个数
          vector<vector<int>> adjs(num);
          for (auto &edge: edges)
          {
              int i = edge[0], j = edge[1];
              adjs[i].push_back(j);
              adjs[j].push_back(i);
          }
          adjs【i】 是一个vector, 里面存着节点编号为i的 所有边的 除i以外另一个节点编号
      b) 没给节点个数
          unordered_map<int, vector<int>> adjs;
  2)有向图
      struct Edge
      {
          Edge(int val, int from, int to): val(val), from(from), to(to) {}
          int val; // 有权 要这个数据成员, 无权图不要
          int from, to;
      };
 
      a) 给了节点个数用   vector<vector<Edge>> adjs(num);
      b) 没给节点个数用   unordered_map<int, vector<Edge>> adjs;

DFS

基本思路:

不撞南墙不回头.寻找离起点最远的顶点,只在碰到死胡同时才回来

用了一个下压栈结构,获取下一个顶点的方式是最晚加入的顶点

用途:

        节点A和B之间的可达性, 并且给出一条路径

非递归伪代码

深度优先遍历有「回头」的过程,在树中由于不存在「环」(回路),对于每一个结点来说,每一个结点只会被递归处理一次。而「图」中由于存在「环」(回路),就需要 记录已经被递归处理的结点(通常使用布尔数组或者哈希表),以免结点被重复遍历到。


// 使用邻接表 adjs 表示图的 dfs 递归版代码

void dfs(int cur, vector<vector<int>> &adjs, vector<bool> &visited)
{
    visited[cur] = true; // 设置当前节点已经访问过了
    for (int i: adjs[cur]) // 遍历cur节点的所有邻接点(就是跟cur会组成边的节点)
    {
        if (!visited[i])
            dfs(i);
    }
}

void dfs(int cur, vector<vector<int>> &adjs, vector<bool> &visited)
{
    stack<int> stk;
    stk.push(cur);
    visited[root] = true;
    cout << root<<endl;  // 一进来就入栈并打印
    while (stk.size())
    {
        cur = stk.top();
        stk.pop();
        for (int i: adjs[cur])
        {
            if (!visited[i])
            {
                cout << i << endl;
                stk.push(cur);
                stk.push(i);
                visited[i] = true;
            }
        }
    }
}

BFS(广度优先搜索)

基本思路:

一圈一圈的扫荡。首先扫荡距离起点为1的点,然后扫荡距离起点为2的点,按照与起点的距离的顺序来遍历所有的顶点

基本实现:

用一个队列,先把起点0入队,标记0 visited[0]=1,然后循环条件当队列不为空的时候, 先出队一个节点, 遍历它的邻接点w,假如没被标记过,标记,入队到队尾。(假设要求最短路径,设置path[w] = v)

用途:

节点A和B之间的可达性, 并且给出最短的路径

void bfs(int cur, vector<vector<int>> &adjs, vector<bool> &visited)
{
    queue<int> que;
    visited[cur] = true;
    queue.push(cur);
    while (!que.empty())
    {
        cur = que.front();
        que.pop();
        cout << cur<< endl; // 访问该节点
        for (int i: adjs[cur]) // 访问该节点的所有邻接点
        {
            if (!visited[i]) //假如没有被访问过
            {
                que.push(i);
                visited[i] = true;
            }
        }
    }
}

有无环

  • 无向图

    • 并查集
  • 有向图
    • 123

拓扑排序

针对有向无环图

概念: 

首先必须是有向无环图才能有拓扑排序. 将所有顶点排序,保证所有的有向边都是排在前面的顶点指向后面的顶点。

解决方法:

 1)Kahn算法:

    用队列实现,入度为0的顶点全都入队(顺序不重要), 然后进行循环,条件队列不为空,第一个元素出队,同时设为已访问且放进一个数组A里面,对这个元素的所有邻接点入度减1,假如入度为0 了,就入队,退出循环之后,遍历所有点,看有没有没访问的,有就是有环,也就没有拓扑排序,没有那就输出数组A,就是拓扑排序.

经典题目:

leetcode 210. 课程表 II

最小生成树(MST)

针对加权连通无向图

概念: 

  首先只有连通图(从图的任意一个顶点能到任意另一个顶点)才存在最小生成树. 

  生成树是图的一颗含有其所有顶点的无环连通子图,最小生成树,就是权值和最小的生成树

解决方法:

  1)prim算法
  2)kruskal算法

1) Kruskal算法

基本思路:

        1) 所有边由小到大排序

        2) 取最小的边,假如不会和已经在最小生成树中的边形成一个环,那就把这条边加到最小生成树里面,,假如会形成环,跳过

        3)直到有 v-1个边为止

 具体方法:最小堆把边排序,并查集识别会形成环的边,队列保存MST结果

时间复杂度: O(ElogE)   E 表示边数。

空间复杂度: O(V)。V 表示顶点数。

2) prim算法

最小路径树

针对加权有向图
解决方法:
  1)Dijkstra算法  (限制权值非负)
  2)Bellman-Ford算法  (适用于所有情况,权值可以负,可以有环,但别有负权重环,存在负权重环,最短路径树就不存在了)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值