【LeetCode】每日一题 面试题 17.08. 马戏团人塔

98 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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;
};

如果你对这道题目还有疑问的话,可以在评论区进行留言;