一日一练: 最后一块石头的重量

104 阅读1分钟

有一堆石头,每块石头的重量都是正整数。每一回合,从中选出两块 最重的 石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:如果 x == y,那么两块石头都会被完全粉碎;如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。

为了尽可能多的石头被粉碎,每次找到重量最大的两个石头,比较重量,如果他们相等就都粉碎,如果不相等,会产生一块新的石头;然后再找到最大的两个,重新比较。。。

这里用最大堆。这篇文章有最小堆的实现解析 💁 最小堆。最大堆的实现思路基本一致,区别在于比较的时候,较大值会上升到堆顶。

代码:

let s = []
let len = 0

function push(el) {
  s[len] = el
  ++len
  if (len > 1) {
    swim(len - 1)
  }
}


function pop() {
  if (len === 0) {
    return null
  }
  const top = s[0]

  if (len > 1) {
    s[0] = s[len - 1]
    sink(0, len - 1)
  }
  len--
  return top
}

function peek() {
  if (len === 0) {
    return null
  } else {
    return s[0]
  }
}

function sink(idx, lastIdx) {
  while(idx <= lastIdx) {
    const rightIdx = (idx + 1) * 2
    const leftIdx = rightIdx - 1
    if (leftIdx <= lastIdx) {
      let valueIdx = leftIdx
      if (rightIdx <= lastIdx && compare(valueIdx, rightIdx) < 0) {
        valueIdx = rightIdx
      }
      if (compare(idx, valueIdx) < 0) {
        swap(idx, valueIdx)
        idx = valueIdx
      } else {
        break
      }
    } else {
      break
    }
  }
}


function swim(idx) {
  while(idx >= 0) {
    let parent = (idx - 1) >> 1
    if (compare(parent, idx) < 0) {
      swap(parent, idx)
      idx = parent
    } else {
      break
    }
  }
}

function swap(a, b) {
  let temp = s[a]
  s[a] = s[b]
  s[b] = temp
}

function compare(a, b) {
  return s[a] - s[b]
}


var lastStoneWeight = function(stones) {
  // 这里可以使用类去实现,避免每次都得reset
  s = []
  len = 0
  stones.forEach(push)
  while(len > 1) {
      const a = pop()
      const b = pop()
      if (a !== b) {
          push(a - b)
      }

  }
  return len > 0 ? s[0] : 0
};