Leetcode 题解:1046. Last Stone Weight

题目:
We have a collection of rocks, each rock has a positive integer weight.(我们有一个石头的集合,每一个石头的重量都是正整数)

Each turn, we choose the two heaviest rocks and smash them together. Suppose the stones have weights x and y with x <= y. The result of this smash is:(每一轮,我们都会选择两个最重的石头,然后让它们一起废碎销毁。加入石头的重量是x和y,x<=y, 那么他们粉碎的结果是:)

If x == y, both stones are totally destroyed;(如果x==y, 那么两个石头一起销毁)
If x != y, the stone of weight x is totally destroyed, and the stone of weight y has new weight y-x.(如果x!=y, 那么x 完全被销毁,y 的重量改变为y-x).
At the end, there is at most 1 stone left. Return the weight of this stone (or 0 if there are no stones left.)(最后,最多只剩下一个石头,返回这个石头的重量,如果没有石头剩下,则返回0).

Example 1:

Input: [2,7,4,1,8,1]
Output: 1
Explanation:
We combine 7 and 8 to get 1 so the array converts to [2,4,1,1,1] then,
we combine 2 and 4 to get 2 so the array converts to [2,1,1,1] then,
we combine 2 and 1 to get 1 so the array converts to [1,1,1] then,
we combine 1 and 1 to get 0 so the array converts to [1] then that’s the value of last stone.

Note:

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

求解思路:

  1. 每次取最大的,和次大的,所以想到堆排序的概念来构建大顶堆(父节点的值大于等于叶子节点的值)。
  2. 石头销毁的过程其实就是删除堆的顶部元素,并且重建堆的过程。具体销毁的过程:
    a, 最后一个元素覆盖堆顶元素。
    b, 删除最后一个元素。
    c, 调整堆使之满足大顶堆。
  3. 由于堆是一个完全二叉树,使用数组存储可以提高访问效率。但是需要注意的是,空出数组的第一个元素,从第二个元素开始存储,这样可以保证对于节点i, 他的左子节点下标为2×i,右子节点下标为2×i+1.;相反,对于节点i, 它的父节点的下标为i/2。

代码:

class Solution {
public:
    int lastStoneWeight(vector<int>& stones) {
        vector<int> heap = buildHeap(stones); //构建大顶堆
        while(heap.size()>2) //删除堆顶元素并堆调整满足顶部最大的原则
        {
           int count = heap.size();
            int maxPos = 1; //最大的元素在下标为1的位置
            int secondPos =2; //次大元素可能是左子节点
            if(heap.size()>3 && heap[secondPos] < heap[secondPos+1]) secondPos =3;//次大元素可能是右子节点
            
           if(heap[maxPos]==heap[secondPos])  //如果最大两个元素相等 则将两个元素依次删除并调整堆
           {
               heap[maxPos] = heap[count-1]; //将最小的元素放到堆顶 使之随着堆的调整下沉到适当位置
               heap.pop_back();
               adjustHeap(heap);
             
               heap[maxPos] = heap[count-2]; //将次小的元素放到堆顶 使之随着堆的调整下沉到适当位置
               heap.pop_back();
               adjustHeap(heap);
           }else{ // 如果最大两个元素不等
               int heap1 = heap[maxPos];
               int heap2 = heap[secondPos];
               heap[maxPos] = heap[count-1]; //销毁最大的元素 并调整堆
               heap.pop_back();
               adjustHeap(heap);
               heap[maxPos] = heap1-heap2; //将次大的元素改为两者之差,并调整堆
               adjustHeap(heap);
           }   
        }
        if(heap.size()<=1) return 0; //如果最后全被销毁,则返回0
        return heap[1]; //返回仅剩的元素
    }
    
    void adjustHeap(vector<int>& heap)
    {
        int i = 1;
        int n = heap.size()-1;
        while(true)
        {
            int maxPos = i;
            int leftChildIndex = i*2;
            int rightChildIndex = i*2+1;
            if(leftChildIndex<=n && heap[i]<heap[leftChildIndex]) maxPos = leftChildIndex;
            if(rightChildIndex<=n && heap[maxPos]<heap[rightChildIndex]) maxPos = rightChildIndex;
            
            if(maxPos == i) break;
            int temp = heap[i];
            heap[i] = heap[maxPos];
            heap[maxPos] = temp;
            i=maxPos;
        }
    }
    
    vector<int> buildHeap(vector<int>& stones)
    {
        vector<int> heap;
        heap.push_back(-1);  //先存入一个无意义的元素。
        
        for(int i=0;i<stones.size();i++)
        {
            heap.push_back(stones[i]); //将原数组的元素依次放入堆的最后
            int heapIndex = heap.size()-1;
            while(heapIndex/2>0 && heap[heapIndex]>heap[heapIndex/2]) //调整大元素的位置,使之沿着父节点上升
            {
                int temp = heap[heapIndex];
                heap[heapIndex] = heap[heapIndex/2];
                heap[heapIndex/2] = temp;
                heapIndex=heapIndex/2;
            }
        }
       return heap;
    }
};
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值