8.11根据身高重建队列(LC406-M)

406. 根据身高重建队列 - 力扣(LeetCode)

算法:

本题有两个维度,h和k,

看到这种题目一定要想如何确定一个维度,然后再按照另一个维度重新排列。

k维度:

从小到大排

(因为k代表个数)

h维度:

从大到小排

(h代表身高,题目中说“身高更高或者相同的人排在前”,所以从大到小)

若h相同,k应该从小到大排

实操后发现:如果按照k来从小到大排序,排完之后,会发现k的排列并不符合条件,身高也不符合条件,两个维度哪一个都没确定下来。

身高一定是从大到小排(身高相同的话则k小的站前面),让高个子在前面。

此时我们可以确定一个维度了,就是身高,前面的节点一定都比本节点高!

那么只需要按照k为下标重新插入队列就可以了

举个例子:

people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]

(1)先按身高,从大到小排序,身高相同的话则k小的站前面

排序完的people: [[7,0], [7,1], [6,1], [5,0], [5,2],[4,4]]

(2)按照k为下标重新插入队列

插入的过程:

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

局部最优:优先按身高高的people的k来插入。插入操作过后的people满足队列属性

全局最优:最后都做完插入操作,整个队列满足题目队列属性

正确代码:

class Solution {
    public int[][] reconstructQueue(int[][] people) {
        //按照身高从大到小排序
        //a[0]第一个人的身高,a[1]第一个人前面的k
        //b[0]第二个人的身高,b[1]第二个人前面的k
        Arrays.sort(people,(a,b)->{
            if (a[0]==b[0]) return a[1]-b[1];
            return b[0]-a[0];
        });
        //按照k从小到大重新插入
        LinkedList<int[]> perpmute = new LinkedList<>();

        for (int[]p:people){
            perpmute.add(p[1],p);
        }
        return perpmute.toArray(new int[people.length][]);

    }
}

注意:

1.`Arrays.sort`使用了一个自定义的比较器,它接受两个参数a和b

  • a``b`代表了`people`数组中的两个元素,每个元素都是一个一维数组,表示一个人的身高和前面的人数。
  • `a[0]`表示第一个人的身高,`a[1]`表示第一个人的前面人数。
  • `b[0]`表示第二个人的身高,`b[1]`表示第二个人的前面人数。

因此,`(a, b) -> {...}`中的比较逻辑根据这些元素的身高和前面的人数进行排序。

  • 如果`a``b`的身高相同(`a[0] == b[0]`),则`a[1] - b[1]`表示根据前面的人数升序排列。( a - b 是升序)
  • 如果`a``b`的身高不同,则`b[0] - a[0]`表示根据身高降序排列。( b - a 是降序)

2.`LinkedList`来重新排列`people`数组中的元素

        //按照k从小到大重新插入
        LinkedList<int[]> perpmute = new LinkedList<>();

        for (int[]p:people){
            perpmute.add(p[1],p);
        }

`perpmute`是一个`LinkedList``int[]`表示这个链表中存储的是整型数组。

`for (int[] p : people)` 这行代码是一个增强型for循环,用于遍历`people`数组中的每个元素,其中`p`代表数组`people`中的每个元素。

在循环中,`perpmute.add(p[1], p)` 的作用是将数组`p`插入到链表`perpmute的指定位置`p[1]`。实际上是根据排好序的规则来插入元素。

这里`p[1]`表示当前人前面的人数,所以实际上是在链表中按照前面的人数进行插入操作。而`p`代表当前的人。

3.为什么要用LinkedList?

使用`LinkedList`是因为在插入操作中,`LinkedList`的插入效率更高。

当按照`k`值从小到大重新插入时,使用`LinkedList`能够更有效地支持这一操作。因为`LinkedList`的插入操作的时间复杂度为O(1),而数组的插入操作的时间复杂度为O(n),因此选择`LinkedList`能够更快速地完成按照`k`值重新插入的操作。

尽管使用`LinkedList`进行插入操作更高效,但在最后需要将结果转换成`int[][]`类型。这是因为方法的返回类型是`int[][]`,因此需要将`LinkedList`转换为`int[][]`,以符合方法的返回类型要求。

时间空间复杂度:

时间复杂度分析

  1. 排序操作:首先,对`people`数组进行排序的时间复杂度为O(nlog⁡n),其中n是`people`数组的长度。
  2. 插入操作:接下来,在循环中,对`LinkedList`进行插入操作,总共需要进行n次插入操作。每次插入操作的时间复杂度为O(1)。

因此,总体时间复杂度为O(nlog⁡n+n),简化后为O(nlog⁡n)。

空间复杂度分析

  1. 排序操作:排序操作通常是O(log⁡n)的空间复杂度。
  2. 链表空间:需要使用额外的空间来存储`LinkedList`中的元素,空间复杂度为O(n)。

因此,总体空间复杂度为O(n)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值