LeetCode 406. 根据身高重建队列
https://leetcode-cn.com/problems/queue-reconstruction-by-height/
难度:中等
假设有打乱顺序的一群人站成一个队列。 每个人由一个整数对(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]]
解法1:排序+调整
通过观察,我们可以知道,对于一组数据,如示例中的 (7,0)
和 (7,1)
,无论它们所处的位置在哪,在最终结果中,它们的相对位置是不会发生变化的:(7,1)
一定会在 (7,0)
的后面,可能中间隔了一个、可能隔了 n
个。
因此,我们可以先对这组数据排序,先保证它们的相对位置,具体做法是根据 (h, k)
来排序:我们先按 h
从大到小进行排序,这样保证了对于每一个数 i
,它前面的数字都不会小于自身,然后对于 h
相同的数字,按 k
从小到大排序,这样,对于 h
相同的数据,它们的相对位置开始就是正确的。
之后,遍历这组排序过的数据,我们使用 tuple
来表示一个 (h, k)
对,遍历过程中,数组索引 i
能够表示不小于当前 tuple
的所有 tuple
的个数(因为我们提前按 h
进行排序了),这样,我们只要根据每个 tuple
的 k
来进行调整即可。
具体调整方法是:对于当前的 tuple
,若 k < i
,说明在当前 tuple
前面的位置,比它高的 tuple
个数太多了,需要将这个 tuple
向前调整 i-k
个位置,让其前面的 tuple
个数刚好等于 k
,也就是移动到第 k
个位置。且这样调整并不会影响到已调整的序列,因为那些数据的相对位置并不会发生改变。
JS 代码:
/**
* @param {number[][]} people
* @return {number[][]}
*/
var reconstructQueue = function(people) {
if (people.length < 2) {
return people;
}
let res = [];
// 先根据 h 和 k 排序,主 h 副 k , h 从大到小排序,k 从小到大排序
people.sort((a, b) => {
if (a[0] === b[0]) {
return a[1] - b[1];
}
return b[0] - a[0];
});
for (let i = 0; i < people.length; i++) {
let k = people[i][1];
if (k < i) {
res.splice(k, 0, people[i]);
} else {
res[i] = people[i];
}
}
return res;
};
解法2:逐个插入、调整
(第 27/37 个 case 会超时)
主要思路是先找到序列的第一个 tuple
,然后根据每个 tuple
的 h
和 k
找到插入的位置,所有数据都插入完成之后,有些 tuple
会因为插入操作而导致站位错误,因此需要逐个检查判断是否站错,若站错,则将这个 tuple
重新插入,反复执行此操作,直到所有数据都满足站位要求:
/**
* @param {number[][]} people
* @return {number[][]}
*/
var reconstructQueue = function(people) {
if (people.length < 2) {
return people;
}
let res = [];
let n = people.length;
let first;
let tuple, k, height, insertIndex;
// 先进行第一次排序
for (let i = 0; i < n; i++) {
tuple = people[i];
k = tuple[1];
height = tuple[0];
insertIndex = 0;
if (k === 0) {
while(insertIndex < res.length && res[insertIndex][0] < height) {
insertIndex++;
}
res.splice(insertIndex, 0, tuple);
} else {
while(insertIndex < res.length && k > 0) {
if (res[insertIndex++][0] >= height) {
k--;
}
}
res.splice(insertIndex, 0, tuple);
}
}
let flag = true;
while(flag) {
flag = false;
for (let i = 0; i < n; i++) {
tuple = res[i];
k = tuple[1];
height = tuple[0];
if (k !== 0) {
// 遍历当前 tuple 前面的人,看是否满足 k 的要求
for (let j = 0; j < i; j++) {
let tuple1 = res[j];
if (tuple1[0] >= height) {
k--;
}
}
if (k !== 0) {
k = tuple[1]; // k 要重新赋值,用于下面的重新插入
res.splice(i, 1); // 将错误的 tuple 从结果中移除
flag = true; // 需要再次判断
break;
}
}
}
// 若存在错误的站位,则重新插入,且 k 、height 和 tuple 可以就使用之前的
if (flag) {
insertIndex = 0;
while(insertIndex < n-1 && k > 0) {
if (res[insertIndex++][0] >= height) {
k--;
}
}
res.splice(insertIndex, 0, tuple);
}
}
return res;
};
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!