LeetCode-406. 根据身高重建队列

题目来源

406. 根据身高重建队列

题目详情

假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好ki 个身高大于或等于 hi 的人。

请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。

示例 1:

输入: people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
输出: [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
解释:
编号为 0 的人身高为 5 ,没有身高更高或者相同的人排在他前面。
编号为 1 的人身高为 7 ,没有身高更高或者相同的人排在他前面。
编号为 2 的人身高为 5 ,有 2 个身高更高或者相同的人排在他前面,即编号为 0 和 1 的人。
编号为 3 的人身高为 6 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
编号为 4 的人身高为 4 ,有 4 个身高更高或者相同的人排在他前面,即编号为 0、1、2、3 的人。
编号为 5 的人身高为 7 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
因此 [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] 是重新构造后的队列。

示例 2:

输入: people = [[6,0],[5,0],[4,0],[3,2],[2,2],[1,4]]
输出: [[4,0],[5,0],[2,2],[3,2],[1,4],[6,0]]

提示:

  • 1 <= people.length <= 2000
  • 0 <= hi <= 106
  • 0 <= ki < people.length
  • 题目数据确保队列可以被重建

题解分析

解法一:从低到高考虑

  1. 本题其实算是一道思维题,因为它没有涉及到动态规划等常见的模板解决方案。
  2. 回到本题,我们可以首先对原始队列进行排序,按照身高从小到大排序。
  3. 为了叙述方便,我们设人数为 n,在进行排序后,它们的身高依次为 h_0, h_1, ..., h_{n-1},且排在第 i 个人前面身高大于 h_i 的人数为 k_i。如果我们按照排完序后的顺序,依次将每个人放入队列中,那么当我们放入第 i 个人时:
    • 第 0,⋯,i−1个人已经在队列中被安排了位置,并且他们无论站在哪里,对第 i 个人都没有任何影响,因为他们都比第 i 个人矮;
    • 而第 i+1,⋯,n−1个人还没有被放入队列中,但他们只要站在第 i 个人的前面,就会对第 i 个人产生影响,因为他们都比第 i 个人高。
  4. 然后,我们可以建立一个空的结果队列,并且按照之前的排序结果来一个个地安排每个人的位置:
    • 关键之处在于,当放入第i个人的时候,它的放置位置前面一定还有ki个空位置,这ki个空位置是用来放置身高大于等于当前人的
    • 换句话说,第i个人的位置,就是队列中从左往右数第ki+1个空位置
  5. 需要注意的一个地方就是,对于身高相同的人来说,应该如何进行排序。这里可以想象为:如果a的身高等于b的身高,而且a的k大于b的身高。也就是说,a在结果队列中一定排在b的后面,那倒过来,就需要先考虑a的位置,因为a需要给大于等于自己身高的人在队列前面留空位置。
class Solution {
    public int[][] reconstructQueue(int[][] people) {
        Arrays.sort(people, (int[] peo1, int[] peo2) -> {
            if(peo1[0] == peo2[0]){
                return peo2[1] - peo1[1];// 如果身高相同,则按照k进行排序,k越大的越先考虑位置
            }else{
                return peo1[0] - peo2[0];// 按照身高从小到大排序
            }
        });
        int[][] ans = new int[people.length][];
        for(int[] peo : people){
            // 考虑当前的人
            int space = peo[1] + 1;// 根据规则,当前人的位置应该在前面的空格数 + 1位置,空格用于存放身高大于等于当前的人
            for(int i=0; i<people.length; i++){
                if(ans[i] == null){
                    space--;// space用于存放身高比自己高的人
                }
                if(space == 0){
                    ans[i] = peo;// 当前人在结果队列中的位置
                    break;
                }
            }
        }
        return ans;
    }
}

解法二:从高到低考虑

  1. 其实还有另外一种解法,就是将身高从高到低进行排序来依次安排每个人在结果队列中的位置。
  2. 具体地,我们来看将身高从高到低排序后的队列特征:
    • 假设前面的人已经在队列中了,那么后面的人的排序方式将不会改变前面已经排好的结果,因为后面的这些人身高比前面的人都低。
    • 而对于第i个人来说,i前面的人的出现都会影响i,因为它们身高都比i高
  3. 与解法一不同,考虑到从高到低排序的特征,这里不能再使用留空位置的方式来解决了,因为这里无法确定i之前需要留几个空位置,后面的人都有可能会随时插入进来。所以,这里使用插入法,根据第i个人的k值,插入到结果队列的第k个位置。
class Solution {
    public int[][] reconstructQueue(int[][] people) {
        Arrays.sort(people, (int[] peo1, int[] peo2) -> {
            if(peo1[0] == peo2[0]){
                return peo1[1] - peo2[1];// 如果身高相同,则按照k进行从小到大排序
            }else{
                return peo2[0] - peo1[0];// 按照身高从高到低排序
            }
        });
        List<int[]> ans = new ArrayList<>();
        for(int[] peo : people){
            ans.add(peo[1], peo);// 将第i个人插入到结果队列中,保证在他前面有ki个人比他高。因为后面的人身高比前面的人都矮,所以后面人的插入不会影响前面已经插好的人的结果
        }
        return ans.toArray(new int[people.length][]);
    }
}
posted @ 2021-12-31 10:00  Garrett_Wale  阅读(41)  评论(0编辑  收藏  举报