[路飞]_leetcode-1046-最后一块石头的重量

196 阅读2分钟

[题目地址] [B站地址]

有一堆石头,每块石头的重量都是正整数。

每一回合,从中选出两块 最重的 石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

  • 如果 x == y,那么两块石头都会被完全粉碎;
  • 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x

最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0

示例:

输入: [2,7,4,1,8,1]
输出: 1
解释:
先选出 78,得到 1,所以数组转换为 [2,4,1,1,1],
再选出 24,得到 2,所以数组转换为 [2,1,1,1],
接着是 21,得到 1,所以数组转换为 [1,1,1],
最后选出 11,得到 0,最终数组转换为 [1],这就是最后剩下那块石头的重量。

提示:

  • 1 <= stones.length <= 30
  • 1 <= stones[i] <= 1000

本题很简单,只需要根据题意要求,每次找出最大的两个值
如果相等,继续取出最大的两个值
如果不等,将它们的差值插入数组,继续取值 直到数组中元素的数量小于两个,此时返回数组中唯一的元素值或者 0 即可

基于以上解题思路,我们有三种方式解题

sort 排序

每次将数组进行升序排序,这样最大的两个值就是数组末尾的两个值
不断取出最大的两个值操作,直到数组中元素数量小于 2

代码如下:

var lastStoneWeight = function(stones) {
  // 当数组长度大于1的时候取出最大的两个值操作
  while(stones.length>1){
    // 将数组进行升序排序
    stones.sort((a,b) => a-b)
    // 排序后数组末尾的两个值就是最大的两个值
    const num1 = stones.pop(),
    num2 = stones.pop();
    // 如果两数不等,将差值插入数组
    if(num1!==num2) stones.push(num1-num2);
  }
  return stones[0]||0;
};

二分算法

我们还可以只对数组进行一次排序,接下来每次有差值的时候
通过二分算法,在数组中找到第一个大于等于差值的位置进行插入
这样就不用每次都对数组进行排序了

代码如下:

var lastStoneWeight = function(stones) {
  // 首先对数组进行一次排序
  stones.sort((a,b) => a-b);
  // 当数组长度大于1的时候取出最大的两个值操作
  while(stones.length>1){
    const num1 = stones.pop(),
    num2 = stones.pop();
    // 如果两数不等,将差值通过二分算法查找合适的位置插入
    if(num1!==num2) stones = insert(stones,num1-num2);
  }
  // 二分算法插入差值
  function insert(arr,num){
    if(num<arr[0]) return [num,...arr]
    else if(num>arr[arr.length-1]) return [...arr,num]
    // 二分查找第一个大于等于num的位置
    let l = 0,r = arr.length-1;
    while(l<r){
      const mid = (l+r)>>1;
      if(arr[mid]<num) l = mid+1
      else r = mid;
    }
    // 将num插入
    arr.splice(l,0,num);
    return arr;
  }
  return stones[0]||0;
};

大顶堆

涉及到维护最值的问题,都可以通过 来解题
本题需要获取的是数组的最大值,所以我们可以通过大顶堆来维护数组中的值,而堆顶元素就是数组中的最大值
每次取出堆顶的两个最大值进行操作 直到堆中的元素数量小于 2

代码如下:

// 大顶堆
class BigHeap{
  constructor(){
    this.arr = [];
    this.size = 0;
  }
  // 插入操作
  push(val){
    this.arr.push(val);
    this.size++;
    // 插入向上调整
    if(this.size>1){
      let cur = this.size-1,
      parent = (cur-1)>>1;
      while(cur>0&&this.arr[cur]>this.arr[parent]){
        [this.arr[parent],this.arr[cur]] =
        [this.arr[cur],this.arr[parent]]
        cur = parent,parent = (cur-1)>>1;
      }
    }
  }
  // 弹出操作
  pop(){
    if(this.size===0) return false;
    if(this.size===1){
      this.size = 0;
      return this.arr.pop();
    }
    const res = this.arr[0];
    this.arr[0] = this.arr.pop();
    this.size--;
    // 弹出向下调整
    let cur = 0,
    childl = 1,childr = 2;
    while(
      (childl<this.size&&this.arr[childl]>this.arr[cur]) ||
      (childr<this.size&&this.arr[childr]>this.arr[cur])
    ){
      if(childr<this.size&&this.arr[childr]>this.arr[childl]){
        [this.arr[cur],this.arr[childr]] =
        [this.arr[childr],this.arr[cur]]
        cur = childr;
      }else{
        [this.arr[cur],this.arr[childl]] =
        [this.arr[childl],this.arr[cur]]
        cur = childl;
      }
      childl = cur*2+1,childr = cur*2+2;
    }
    return res;
  }
}
var lastStoneWeight = function(stones) {
  // 创建大顶堆实例
  const heap = new BigHeap();
  // 将数组中元素插入堆中
  for(let i = 0;i<stones.length;i++){
    heap.push(stones[i])
  }
  // 当堆中元素数量大于1的时候取出最大的两个值操作
  while(heap.size>1){
    const num1 = heap.pop(),
    num2 = heap.pop();
    // 如果有差值,将差值插入堆中
    if(num1!==num2) heap.push(num1-num2);
  }
  return heap.pop()||0;
};

至此我们就完成了 leetcode-1046-最后一块石头的重量

如有任何问题或建议,欢迎留言讨论!