LeetCode 1129. Shortest Path with Alternating Colors

题目

在这里插入图片描述

知识点

BFS

结果

在这里插入图片描述
时间空间都是还好,但是不算太差了,正常机试要求都是1000ms和32MB啦,先不对自己有过多的要求了。

实现

码前思考

  1. 我一看到这道题要求最短路径——Shortest Path,我就一头钻到了Dijkstra这类算最短路径的算法里面了,后来也是想用Dijkstra来解题,结果情况越来越多,没有做下去了。。。

  2. 后来看题解,发现很多人的题解都涉及到了 BFS,于是我开始思考怎么用BFS呢?

    其实,这虽然是一道求Shortest Path的题目,但是由于这里每个相邻节点之间的距离是1,所以我们不必去想Dijkstra这类算法,我们可以直接把路径等价为BFS的层数!这样我们就是求每个节点最早出现在BFS中的哪一层就好了。

  3. 题中的颜色交替路径如何实现?

    我们考虑下列情况,假如现在一条蓝色边指向节点v,那么节点v就可以把自己指出去的红边的目的节点加入到我们的BFS队列中。为了表达上述信息:
    (1)我们设计BFS队列里面存储的是结构体,这个结构体包含边的颜色边的目标节点
    (2)同时我们将一个顶点的红边指向的邻接节点蓝边指向的邻接节点分开用数组存储。

  4. 如何确定最小路径呢?

    扫描二维码关注公众号,回复: 10549029 查看本文章

    当我们的顶点第一次加入到BFS队列时,此时的层数就代表找到了最短路径了。
    假设节点v第一次进入队列时是蓝边指向的,那么就要入队它的红边出度节点,同时,更新之后我们可以直接清空它的红边数组,因为就算以后再有蓝边指向v,那也不是最短的,所以没必要计算,直接清空它的红边数组,如果不清空红边数组,我们程序的复杂度会增大的。

  5. 再解释一下3中的清空边数组的操作:
    由于我们使用了BFS,所有体现了层次关系,先入队列的长度一定是小于等于后入队列的长度的。
    假如一条蓝边r让节点v第一次入了队列,那么对于所有v红边集合,它们目前的最短路径就决定了,因为后面出现的蓝边指向节点v的路径一定是不大于当前的长度的!所以要清空,避免以后又碰见指向节点v的蓝边又重复计算!在节点v第一次入了队列之后,它的最短路径就确定了,但是它的出度蓝边集合可能会使其他节点入队,我们需要考虑,因此当后面有指向它的红边时,我们可以入队它的蓝边出度节点,然后清空蓝边集合,同样也是避免重复计算。

  6. 下面是一张实例图,解释了上面的说法:
    在这里插入图片描述

  7. 自连边是有用的,它的处理与普通处理方法相同;

  8. 两个节点之间的重复边看作一条就行。

  9. 看是难看懂的,还是要自己多思考,多动手用代码实现才对,实现中去体会思想!

实现代码

//使用BFS来进行解题
//万物皆可DFS,BFS~~~
//所谓最短路径,其实就是BFS的层数了
//不要看到最短路径就是Dijkstra它们,对于这种权值为1的最短路径,BFS和DFS都可以的!!!
//如果一条边加入过队列,那么它不能再加一次,因为它加入之后没有用,因为我们已经求过到它的最短距离了,所以没有必要再加它进来,它除了加大长度别无用处!!!
//平行边是一视同仁的

//定义边结构体
struct edge{
    bool color; //0表示红色,1表示蓝色
    int v;  //表示边的终点
    //构造函数
    edge(bool _color,int _v):color(_color),v(_v){}
};

class Solution {

private:
    //红边数组
    vector<vector<int>> radj;
    //蓝边数组
    vector<vector<int>> badj;
    //距离数组
    vector<int> d;

public:
    vector<int> shortestAlternatingPaths(int n, vector<vector<int>>& red_edges, vector<vector<int>>& blue_edges) {
        //对n=1进行特判
        if(n == 1){
            return vector<int>(1,0);
        }

        //对数据进行初始化
        radj.assign(n,vector<int>());    
        badj.assign(n,vector<int>());
        d.assign(n,-1); //使用-1没关系的

        //读入数据
        for(auto pair : red_edges){
            radj[pair[0]].push_back(pair[1]);
        }
        for(auto pair : blue_edges){
            badj[pair[0]].push_back(pair[1]);
        }

        //进行BFS得到结果
        BFS();

        //d就是我们的结果
        return d;
    }

    void BFS(){
        //BFS的初始化
        queue<edge> q;
        //路径长度;
        int len = 0;
        //对0进行处理
        d[0] = len;
        // vis[0] = true;
        
        //对0的所有边进行操作
        for(int i = 0;i<radj[0].size();i++){
            q.push(edge(0,radj[0][i]));
        }
        //入完之后清空
        radj[0].clear();

        for(int i = 0;i<badj[0].size();i++){
            q.push(edge(1,badj[0][i]));
        }
        //入完之后清空
        badj[0].clear();

        //开始
        while(!q.empty()){
            //记录当前长度
            int size = q.size();
            len++;

            //遍历这一层
            for(int i=0;i<size;i++){
                edge e = q.front();
                q.pop();

                //对这一条边进行一些操作
                bool color = e.color;
                int v = e.v;

                //如果这个顶点没有被访问,说明他要求最短路径
                if(d[v] == -1){ //vis数组可以舍弃
                    d[v] = len;
                }

                //再然后遍历这个点所到的所有红边或者蓝边
                //对v的所有边进行操作
                if(!color){
                    for(int i = 0;i<badj[v].size();i++){
                        q.push(edge(1,badj[v][i]));
                    }
                    //入完之后清空
                    badj[v].clear();
                }else{
                    for(int i = 0;i<radj[v].size();i++){
                        q.push(edge(0,radj[v][i]));
                    }
                    //入完之后清空
                    radj[v].clear();
                }
            }
        }
    }
};

码后反思

  1. 对1个节点这种特殊情况写特判,LeetCode常有这种特判;
  2. 再总结一遍思路:BFS + 最多遍历一次一个点的红边集合 + 最多遍历一次一个点的蓝边集合,后面就是代码实现了。
  3. 这位网友的题解会比我的更清晰,更加易懂,很nice。ta是对每个点都计算蓝边入度和红边入度的最短路径。
  4. 我失败的原因应该是没想到用BFS,BFS是关键啊!这道题就是简单BFS加上统计每个点的蓝边入度和红边入度的最短路径长度!时间复杂度为 O ( V + E ) O(V+E)
  5. 我才发现DFS和BFS可以用来解决带权图的单源最短路径,之前一直把自己困在了那些比较出名的算法上面了,忽视了这些传统算法!BFS和DFS究极重要啊啊啊!!!图里面全是BFS和DFS,不能忘本!!!
发布了138 篇原创文章 · 获赞 3 · 访问量 3825

猜你喜欢

转载自blog.csdn.net/yc_cy1999/article/details/105046917