题目:
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
求解思路:
- 每次取最大的,和次大的,所以想到堆排序的概念来构建大顶堆(父节点的值大于等于叶子节点的值)。
- 石头销毁的过程其实就是删除堆的顶部元素,并且重建堆的过程。具体销毁的过程:
a, 最后一个元素覆盖堆顶元素。
b, 删除最后一个元素。
c, 调整堆使之满足大顶堆。 - 由于堆是一个完全二叉树,使用数组存储可以提高访问效率。但是需要注意的是,空出数组的第一个元素,从第二个元素开始存储,这样可以保证对于节点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;
}
};