一、题目描述

假设有打乱顺序的一群人站成一个队列。每个人由一个整数对 (h, k)表示,其中 h 是这个人的身高,k 是排在这个人前面且身高大于或等于 h 的人数。编写一个算法来重建这个队列。

注意:

总人数少于1100人。

示例

输入:
[[7,0][4,4][7,1][5,0][6,1][5,2]]

输出:
[[5,0][7,0][5,2][6,1][4,4][7,1]]

二、题目解析

题目输入是一个二维数组,这个数组的元素是被打乱的,每个元素是个二元祖,表示的一个人的身高以及他前面比他高或者和他一样高的人的个数,让你基于此重新排序。

在做这道题之前,先摒弃一个观念就是:在还没有完全想出可行解的时候就去思考时间复杂度

除非题目对时间复杂度或是输入数据规模有硬性要求,你可以基于题目要求的时间复杂度来思考解题的方向。

但是一般的情况下,我们都是试着先去想出可行解,然后根据可行解一步步的优化,不然的话,你做题很容易就会没有思路,因为你心中你所猜测的那个最优时间复杂度会不自不觉地帮你 排除 很多 “可能的解”。

回到这道题,我们先试着找出可行解。

直觉告诉你这是一个和排序相关的题目,但是这其中的两个变量让排序变得棘手,我们不仅需要考虑身高,还需要考虑前面的人数。

突破口在哪?

当然了,光排序肯定是不够的,我们还需要一些额外的操作,你可以想象一些,我们要对这一堆人(元素)进行排序,每个人都有属于他的位置,我们的工作是将人一个个放到属于他的位置,那我们该怎么放,或是说以一个什么样的顺序去放,才不会出错?

如果我们先放个子矮的是怎么样的情况,考虑例子 [[5,0],[6,0],[7,0]]

我们先放置元素 [5,0],将其放在第一个位置,那么接下来考虑[6,0],这时就会出现问题,如果光考虑 [6,0] 本身,它其实应该被放在第一个位置,但是问题是如果把 [6,0] 放到第一个位置,之前放置的 [5,0] 就不正确了,那你可能会说,那就把 [6,0] 放到[5,0] 的后面呗,这个例子下是可以,但是如果例子是 [[5,1],[6,0],[7,0]] 呢?也就是说你考虑当前元素的时候还必须去重新考虑一遍之前放置的元素。

你可以看到,按这样的考虑方式,之前放置的元素会受到当前放置元素的影响

我们再来看看先放个子高的是怎么样的情况,还是考虑例子 [[5,0],[6,0],[7,0]],我们先放置元素 [7,0],放到第一个位置,[[7,0]] 然后放置 [6,0],还是放到第一个位置,[[6,0], [7,0]]

你可以看到,按这样的顺序,每当我们考虑一个元素,之前已经考虑过的元素都比当前元素大或是等于当前元素,不管把当前元素放到哪,当前元素的放置均不会对之前已经放置的元素造成影响而出问题,只需要按 “前面比他高的人数” 的值将当前元素插入到指定位置即可。

于是,我们可以采用第二种方式进行插入。这里有个小细节就是,如果两个人的身高相同,我们要先考虑放置人数少的。这一点也很好解释,对于身高相同的人,最后的答案肯定是,人数少的会插在人数多的前面,如果先插人数多的,那么再插人数少的就会对人数多的产生影响,而先插入人数少的就不会有这个问题。

综上所述,这道题的整体思路就是先排序后插入即可,但是你依然可以看到这其中的一些很微妙的思考过程。

三、动画描述

四、参考代码

public int[][] reconstructQueue(int[][] people) {
     // 对输入数组基于身高排序(倒序)
     // 如果身高相同,基于人数排序(正序)
    Arrays.sort(people, new Comparator<int[]>() {
        @Override
        public int compare(int[] a, int[] b) {
            if (a[0] != b[0]) {
                return b[0] - a[0];
            }
            return a[1] - b[1];
        }
    });

    List<int[]> result = new LinkedList<>();
     // 基于人数将元素插入到指定位置即可
    for(int[] p : people){
        result.add(p[1], p);
    }

    return result.toArray(new int[people.length][]);
}

五、复杂度分析

这道题目,因为插入的关系,最差时间复杂度是 O(n^2)

至于空间复杂度,因为用了个 list 作为过渡,虽然说没有创建新的数组,但是因为引用的问题,空间复杂度是 O(n),用些技巧你也可以省去这个复杂度,但这不是重点。

重点是我们能否继续在时间上进行优化呢?

想要继续优化,我们可能需要用到比较高阶的树的知识。

在这先买个关子,感兴趣的话,你可以思考一下,下次我们再重新以另外一个角度来审视这道题,敬请期待,大家加油:)


©著作权归作者所有:来自51CTO博客作者mb5fe18fab305a5的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. 超详细!详解一道高频算法题:数组中的第 K 个最大元素
  2. 动画:面试必刷之二维数组中查找一个元素
  3. 巩固 | 最全面的算法复杂度分析
  4. 前 K 个高频元素告诉你桶排序有啥用
  5. 这道算法题太简单?你忽略了时间复杂度的要求!
  6. 冰与火之歌:「时间」与「空间」复杂度
  7. 看动画轻松理解时间复杂度(二)
  8. 看动画轻松理解时间复杂度(一)
  9. Python办公自动化|光速对比并提取两份Word/Excel中的不同元素

随机推荐

  1. Android图表控件MPAndroidChart——曲线
  2. android Makefile
  3. Android requires compiler compliance l
  4. android跨进程通讯一:android中跨进程通
  5. 初探Android
  6. Android概述
  7. Android -- java代码设置margin
  8. Android入门一:Android 开发环境安装配置
  9. 补间动画--透明渐变XML
  10. Android(安卓)init.c简析