leetcode.1046. 最后一块石头的重量

1046. 最后一块石头的重量

We have a collection of stones, each stone has a positive integer weight.

Each turn, we choose the two heaviest stones and smash them together. Suppose the stones have weights x and y with x <= y. The result of this smash is:

  • If x == y, both stones are totally destroyed;
  • If x != y, the stone of weight x is totally destroyed, and the stone of weight yhas new weight 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.)

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

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

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

最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 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. 1 <= stones.length <= 30
  2. 1 <= stones[i] <= 1000

代码与思路

将所有石头的重量放入最大堆中。每次依次从队列中取出最重的两块石头 ab,必有 a ≥ b a \geq b ab。如果 a>b,则将新石头 a-b 放回到最大堆中;如果 a=b,两块石头完全被粉碎,因此不会产生新的石头。重复上述操作,直到剩下的石头少于 2 块。

最终可能剩下 1 块石头,该石头的重量即为最大堆中剩下的元素,返回该元素;也可能没有石头剩下,此时最大堆为空,返回 0。

java

class Solution {
    public int lastStoneWeight(int[] stones) {
        PriorityQueue<Integer> pq = new PriorityQueue<Integer>((a, b) -> b - a);
        for (int stone : stones) {
            pq.offer(stone);
        }

        while (pq.size() > 1) {
            int a = pq.poll();
            int b = pq.poll();
            if (a > b) {
                pq.offer(a - b);
            }
        }
        return pq.isEmpty() ? 0 : pq.poll();
    }
}

cpp

class Solution {
public:
    int lastStoneWeight(vector<int>& stones) {
        priority_queue<int> q;
        for (int s: stones) {
            q.push(s);
        }

        while (q.size() > 1) {
            int a = q.top();
            q.pop();
            int b = q.top();
            q.pop();
            if (a > b) {
                q.push(a - b);
            }
        }
        return q.empty() ? 0 : q.top();
    }
};

c语言

void swap(int *a, int *b) {
    int tmp = *a;
    *a = *b, *b = tmp;
}

void push(int *heap, int *heapSize, int x) {
    heap[++(*heapSize)] = x;
    for (int i = (*heapSize); i > 1 && heap[i] > heap[i >> 1]; i >>= 1) {
        swap(&heap[i], &heap[i >> 1]);
    }
}

void pop(int *heap, int *heapSize) {
    int tmp = heap[1] = heap[(*heapSize)--];
    int i = 1, j = 2;
    while (j <= (*heapSize)) {
        if (j != (*heapSize) && heap[j + 1] > heap[j]) ++j;
        if (heap[j] > tmp) {
            heap[i] = heap[j];
            i = j;
            j = i << 1;
        } else {
            break;
        }
    }
    heap[i] = tmp;
}

int top(int *heap) {
    return heap[1];
}

int lastStoneWeight(int *stones, int stonesSize) {
    if (stonesSize == 1) {
        return stones[0];
    }
    if (stonesSize == 2) {
        return fabs(stones[0] - stones[1]);
    }
    int heap[stonesSize + 2], heapSize = 0;
    for (int i = 0; i < stonesSize; i++) {
        push(heap, &heapSize, stones[i]);
    }

    while (heapSize > 1) {
        int a = top(heap);
        pop(heap, &heapSize);
        int b = top(heap);
        pop(heap, &heapSize);
        if (a > b) {
            push(heap, &heapSize, a - b);
        }
    }
    return heapSize ? top(heap) : 0;
}

java(手写堆,不用API)

   public int lastStoneWeight(int[] stones) {
        MyHeap<Integer> myHeap = new MyHeap<>(stones.length, (a, b) -> b - a);
        for (int stone : stones)
            myHeap.add(stone);
        int distant = 0;
        while (myHeap.getSize() > 1)
            if ((distant = myHeap.remove() - myHeap.remove()) > 0)
                myHeap.add(distant);
        return myHeap.getSize() == 0 ? 0 : myHeap.remove();
    }

    public class MyHeap<E> {
        private Object[] data;//数据存放区
        private int size;//堆的大小
        private Comparator<? super E> comparator;//比较器

        public MyHeap(int initialCapacity) {
            this(initialCapacity, null);
        }

        public MyHeap(int initialCapacity, Comparator<? super E> comparator) {
            if (initialCapacity < 1)
                throw new IllegalArgumentException("堆的大小必须大于0");
            this.data = new Object[initialCapacity];
            this.comparator = comparator;
        }

        /**
         * @param e 向堆中添加元素
         * @return
         */
        public boolean add(E e) {
            if (e == null)//不能为空
                throw new NullPointerException();
            if (size >= data.length)//如果堆的空间不够了就扩容,这里是扩大2倍
                data = Arrays.copyOf(data, data.length << 1);
            if (size == 0)//如果堆是空的,直接添加就可以了,不需要调整,因为就一个没法调整
                data[0] = e;
            else//如果堆不是空的,就要往上调整。
                siftUp(e);
            size++;//添加完之后size要加1
            return true;
        }

        public int getSize() {
            return size;
        }

        //删除堆顶元素
        public E remove() {
            if (size == 0)
                return null;
            size--;
            E result = (E) data[0];//获取堆顶的元素
            E x = (E) data[size];//取出数组的最后一个元素
            data[size] = null;//然后把最后一个元素的位置置空
            if (size != 0)
                siftDown(x);//这里实际上是把数组的最后一个元素取出放到堆顶,然后再往下调整。
            return result;
        }

        //访问堆顶元素,不删除
        public E peek() {
            return (size == 0) ? null : (E) data[0];
        }

        /**
         * 返回数组的值
         *
         * @param a
         * @param <T>
         * @return
         */
        public <T> T[] toArray(T[] a) {
            if (a.length < size)
                return (T[]) Arrays.copyOf(data, size, a.getClass());
            System.arraycopy(data, 0, a, 0, size);
            if (a.length > size)
                a[size] = null;
            return a;
        }

        /**
         * 往上调整,往上调整只需要和父节点比较即可,如果比父节点大就不需要在调整
         *
         * @param e
         */
        private void siftUp(E e) {
            int s = size;
            while (s > 0) {
                int parent = (s - 1) >>> 1;//根据子节点的位置可以找到父节点的位置
                Object pData = data[parent];
                //和父节点比较,如果比父节点大就退出循环不再调整
                if (comparator != null) {
                    if (comparator.compare(e, (E) pData) >= 0)
                        break;
                } else {
                    if (((Comparable<? super E>) e).compareTo((E) pData) >= 0)
                        break;
                }
                //如果比父节点小,就和父节点交换,然后再继续往上调整
                data[s] = pData;
                s = parent;
            }
            //通过上面的往上调整,找到合适的位置,再把e放进去
            data[s] = e;
        }

        /**
         * 往下调整,往下调整需要和他的两个子节点(如果有两个子节点)都要比较,哪个最小就和哪
         * 个交换,如果比两个子节点都小就不用再交换
         *
         * @param e
         */
        private void siftDown(E e) {
            int half = size >>> 1;
            int index = 0;
            while (index < half) {
                int min = (index << 1) + 1;//根据父节点的位置可以找到左子节点的位置
                Object minChild = data[min];
                int right = min + 1;//根据左子节点找到右子节点的位置
                if (right < size) {//如果有右子节点就执行这里的代码
                    //如果有右子节点,肯定会有左子节点。那么就需要左右两个子节点比较,把小的赋值给minChild
                    if (comparator != null) {
                        if (comparator.compare((E) minChild, (E) data[right]) > 0)
                            minChild = data[min = right];
                    } else {
                        if (((Comparable<? super E>) minChild).compareTo((E) data[right]) > 0)
                            minChild = data[min = right];
                    }
                }
                //用节点e和他的最小的子节点比较,如果小于他最小的子节点就退出循环,不再往下调整了,
                if (comparator != null) {
                    if (comparator.compare(e, (E) minChild) <= 0)
                        break;
                } else {
                    if (((Comparable<? super E>) e).compareTo((E) minChild) <= 0)
                        break;
                }
                //如果e比它的最小的子节点小,就用最小的子节点和e交换位置,然后再继续往下调整。
                data[index] = minChild;
                index = min;
            }
            data[index] = e;
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值