1046. 最后一块石头的重量
有一堆石头,每块石头的重量都是正整数。
每一回合,从中选出两块 最重的 石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎; 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。 最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。
示例:
输入:[2,7,4,1,8,1]
输出:1
解释:
先选出 7 和 8,得到 1,所以数组转换为 [2,4,1,1,1],
再选出 2 和 4,得到 2,所以数组转换为 [2,1,1,1],
接着是 2 和 1,得到 1,所以数组转换为 [1,1,1],
最后选出 1 和 1,得到 0,最终数组转换为 [1],这就是最后剩下那块石头的重量。
解题思路:本体纯数组才做比较简单,这里就不介绍了,直接上大顶堆实现方式,大顶堆介绍可以参考最小的k个数,代码如下:
var lastStoneWeight = function(stones) {
if(!stones) return 0;
data = [];
cnt = 0;
for(let i = 0; i < stones.length; i ++){
//让无序数组有大顶堆特性
push_up(stones[i]);
}
//如果只有一个数,直接返回
if(data.length == 1) return data[0];
//开始跳石头粉碎
while(data.length > 1){
//大顶堆特性根节点最大,获得最大数
let x = data[0];
//删除最大根节点
pop_down();
//删除最大后,因为维护了根特性,此时最大值就是根节点
let y = data[0];
pop_down();
//判断第一大值和第二大值是否相等,如果不相等就入队两者差
if(x != y){
push_up(x - y)
}
}
return data[0] ? data[0] : 0;
};
let data = [],cnt = 0;
var push_up = function(val){
data.push(val);
let idx = cnt;//当前节点
let gIdx = parseInt((idx - 1) / 2);//根节点
while(idx && data[idx] > data[gIdx]){
//交互节点,向上调整
swap(data[idx],idx,data[gIdx],gIdx);
idx = gIdx;
gIdx = parseInt((idx - 1)/2);
}
cnt ++;
return;
}
var pop_down = function(){
if(data.length <= 1){
data = [];
cnt = 0;
return;
}
//删除最大值,将尾结点放在开头,位置树结构
data[0] = data.pop();
cnt --;
let idx = 0;//当前节点
let n = cnt -1;//最大节点个数
let zIdx = 2 * idx + 1;//左子节点
//左节点小于n就说明存在左子节点
while(zIdx <= n){
let temp = idx;//三角区最大值下标
if( data[idx] < data[zIdx] ) temp = zIdx;
//判断右子节点是否存在,然后比较大小,选出最大值的下标
if(zIdx + 1 <= n && data[temp] < data[zIdx + 1]) temp = zIdx + 1;
if(temp == idx) break;//相等说明没必要调整,根节点已经是最大的
//交互根节点和最大子节点,保持向下调整
swap(data[idx],idx,data[temp],temp);
idx = temp;
zIdx = 2*idx +1;
}
return;
}
var swap = function(v1,i1,v2,i2){
data[i1] = v2;
data[i2] = v1;
}