【力扣时间】【851】【中等】喧闹和富有

颇有意境的题目。

1、先看题

题目在此

在经历了昨天的困难题洗礼后,今天又回到了中等难度。
不过是一个中等中较为简单的题。

2、审题

题目再次罗里吧嗦地说了很多。
但是赖着性子看完后可以很快发现,这是一道关于图的题目。

图是一个相对比较复杂的结构。所以在算法中涉及到图的话,可以玩得各种五花八门。特别是困难题目,往难了去的话,图论能够把我这种一般社畜折磨得半死不活。
不过好在,今天的题难度全是图堆起来的。如果有入门级的图论知识的话,今天纯粹是道简单题。

按惯例,我们还是先来提取重点:

  1. richer[]数组构成了一个有向连通图。
  2. 由于richer[]是比较的关系,且题目保证了逻辑自洽,所以这是一个单向无环连通图。
  3. quiet[]数组记录了每个点的安静值,即为每个点的权重。(注意,这里的权重和图论中的路径加权不是一个概念,因而这不是一个加权图)
  4. 题目需要我们返回的数组,为记录从每个点出发,比它更有钱的点中,安静值最小的点。所以这只是一个单向图中的遍历问题。

此外,除了一些数据边界,便也没有什么需要注意的了。
思路很清晰,开始吧。

3、思路

当知道了题目的考点后,首先要做的自然是先构造出图来。
我初始化一个二维数组map[][],记录点和点之间的连通关系。
同时,初始化result[],来记录某一点的最终结果。
之后需要的,就只有深度优先遍历所有点的路径,并求其可以连通的所有点中的最小值了。

4、撸代码

class Solution {

    public int[] loudAndRich(int[][] richer, int[] quiet) {
        int n = quiet.length;

        int[] result = new int[n];

        byte[][] map = new byte[n][n];
        for (int[] rich : richer) {
            //由于0比1有钱,故查找时会从1向0查找,设置从1到0的路径
            map[rich[1]][rich[0]] = 1;
        }

        int[] isVis = new int[n];
        for (int i = 0; i < n; i++) {
            getQuiet(map, quiet, result, isVis, i);
        }

        return result;
    }

    private int getQuiet(byte[][] map, int[] quiet, int[] result, int[] isVis, int t) {
        //如果该点已经访问,则直接返回结果
        if (isVis[t] == 1) {
            return result[t];
        }

        //优先将最安静的指向自身
        int lowQuietness = t;
        //获取此点的全部路径
        byte[] loads = map[t];
        for (int i = 0; i < loads.length; i++) {
            //路径不通,或遇到此点本身时,跳过
            if (loads[i] != 1 || i == t) {
                continue;
            }

            //找到相通的路径时
            //获取该点的最小安静度
            int quietness = this.getQuiet(map, quiet, result, isVis, i);
            //当该点的安静度<当前最小安静度时,修改此点的安静度为该点
            if (quiet[quietness] < quiet[lowQuietness]) {
                lowQuietness = quietness;
            }
        }

        //记录此点的最小安静点
        result[t] = lowQuietness;
        //标记已经访问
        isVis[t] = 1;
        return result[t];
    }

}

5、解读

首先,我遍历了richer[],并以此构建了一个图。

 byte[][] map = new byte[n][n];
 for (int[] rich : richer) {
     //由于0比1有钱,故查找时会从1向0查找,设置从1到0的路径
     map[rich[1]][rich[0]] = 1;
 }

richer[]中出现了[i,j]的情况,则说明了ij富裕。
而由于考察的是比某点要富有的所有点的安静值,所以此时我们记录的是穷人到富人的路径,即j->i,于是我们设置map[j][i] = 1

事后我反思此处构建N*N的二维数组是纯属浪费的。
浪费空间不说,还会影响时间。

图构建完后,就可以遵循路径,进行dfs了。

此处,我以isVis数组来记录了每个点的访问情况。

这一步看似纯粹浪费空间。但事后我比较了许多大牛的解法,他们普遍使用Arrays.fill方法来给最终的结果数组进行了一遍初始化。
从而,我反而节省了一遍遍历的时间。

int[] isVis = new int[n];
for (int i = 0; i < n; i++) {
     getQuiet(map, quiet, result, isVis, i);
 }

之后就是dfs了,中规中矩的写法。注释也挺完善的,相信各位能看懂。

private int getQuiet(byte[][] map, int[] quiet, int[] result, int[] isVis, int t) {
        //如果该点已经访问,则直接返回结果
        if (isVis[t] == 1) {
            return result[t];
        }

        //优先将最安静的指向自身
        int lowQuietness = t;
        //获取此点的全部路径
        byte[] loads = map[t];
        for (int i = 0; i < loads.length; i++) {
            //路径不通,或遇到此点本身时,跳过
            if (loads[i] != 1 || i == t) {
                continue;
            }

            //找到相通的路径时
            //获取该点的安静度
            int quietness = this.getQuiet(map, quiet, result, isVis, i);
            //当该点的安静度<当前最小安静度时,修改此点的安静度为该点
            if (quiet[quietness] < quiet[lowQuietness]) {
                lowQuietness = quietness;
            }
        }

        //记录此点的最小安静点
        result[t] = lowQuietness;
        //标记已经访问
        isVis[t] = 1;
        return result[t];
    }

6、提交

在这里插入图片描述
情况比我预期的要好。
空间换时间后,让时间排名较前。

7、咀嚼

唔……
看上去我的代码循环特别多,但仔细分析则不然。

首先,我遍历了一遍richer[],时间复杂度为O(N),N为richer[]的长度。
之后,就是进行dfs。此处看似遍历了很多次,而且循环套循环。但实际上,我控制了每个点仅会访问一次,实际上是线性的耗时,时间复杂度为O(M),M为quiet[]的长度,也是点的数量。
于是,整体的时间复杂度实际上是O(N+M)

由于画了个图,空间复杂度为O(N2)

8、他人的智慧

我扫了题解区的大部分解法,无论语言,大家的思路都如出一辙,并没有看到什么黑科技。

不过倒是看到一个大牛,其思路与我一致,但解法将空间换时间表现到了极致,从而耗时极低。
虽然写得很乱,也没有什么注释,但仍然值得学习下。

9、总结

就如我开头所说,涉及到图的算法题上限极高,下限也不低的。
我本人在这方面就很薄弱,涉及到复杂的多半只能缴械投降。

今天的题还是相较容易的,所以才能信手拈来。

但图论是我不太熟悉的题目,之后值得加大力度去学习。
姑且一步一个脚印,慢慢来吧。

周三了,社畜的一周不知不觉就过半了。
大家共勉!熬过今年,明年就有老头环玩了。(^-^)V

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值