持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第9天,点击查看活动详情
面试题 17.08. 马戏团人塔
有个马戏团正在设计叠罗汉的表演节目,一个人要站在另一人的肩膀上。出于实际和美观的考虑,在上面的人要比下面的人矮一点且轻一点。已知马戏团每个人的身高和体重,请编写代码计算叠罗汉最多能叠几个人。
「示例1:」
输入:height = [65,70,56,75,60,68] weight = [100,150,90,190,95,110]
输出:6
解释:从上往下数,叠罗汉最多能叠 6 层:(56,90), (60,95), (65,100), (68,110), (70,150), (75,190)
「提示:」
height.length == weight.length <= 10000
解题思路
// 第一种
也是通过排序+二分法获取最大子串长度解决的。
将height升序排列,如果遇到同值,将对应序列的weight进行降序排列
使用二分法获取weight的最大子串的长度,就是最终结果
// 第二种
数组二分搜索
代码实现
// 第一种
/**
* @param {number[]} height
* @param {number[]} weight
* @return {number}
*/
var bestSeqAtIndex = function(height, weight) {
const data = [];
const dp = [];
for(let i = 0;i < height.length;i++) {
const item = {
height: height[i],
weight: weight[i]
};
data.push(item);
}
data.sort(function(a, b) {
if(a.height === b.height) {
return b.weight - a.weight;
}
return a.height - b.height;
});
// 利用二分法获取weight的最长子串的值就是结果
let res = 0;
for(let index in data) {
index = Number(index);
let w = data[index].weight;
let i = 0;
let j = res;
while(i < j) {
const m = parseInt((i + j) / 2);
if(dp[m] < w) {
i = m + 1;
} else {
j = m;
}
}
dp[i] = w;
if(j === res) res++;
}
return res;
};
// 第二种
/**
* @param {number[]} height
* @param {number[]} weight
* @return {number}
*/
var bestSeqAtIndex = function(height, weight) {
const n = height.length;
if (n === 0) {
return 0;
}
// 使用people数组同时储存身高和体重。
const people = new Array(n).fill(0).map(() => new Array(2));
for(let i=0; i<n; i++) {
people[i][0] = height[i];
people[i][1] = weight[i];
}
// 先按身高增序排列,身高相同时,体重大的放到前面,避免身高相同时算入结果(不允许)
people.sort((a,b) => {
if (a[0] < b[0]) {
return -1;
} else if (a[0] === b[0]) {
return b[1] - a[1];
} else {
return 1;
}
});
// dp数组,以endNum[i]结束的数字长度为i,让endNum[i]尽可能小,这样就能让子序列尽可能长
const endNum = new Array(n+1);
let len = 1;
endNum[1] = people[0][1];
for (let i = 1; i < n; ++i) {
const target = people[i][1];
if (target > endNum[len]) {
++len;
endNum[len] = target;
} else {
// 二分搜索:如果target更小,则替换掉相应位置的endNum
let l = 1;
let r = len;
let pos = 0; // 辅助指针
while (l <= r) {
const mid = l + (r-l >> 1);
if (endNum[mid] < target) {
pos = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
endNum[pos + 1] = target;
}
}
// 在i的位置有结果(非空),说明有长度为i的子序列(这个值等于filter后数组的长度)
return endNum.filter(v => v!== undefined).length;
};
如果你对这道题目还有疑问的话,可以在评论区进行留言;