面试150——堆
215. 数组中的第K个最大元素

思路
将数组堆化成大顶堆,之后弹出k个元素,第k个即是第k大元素。
手写一个堆
代码
class Solution {public int findKthLargest(int[] nums, int k) {int size = nums.length;heaplify(nums, size);int res = 0, cnt = 0;while (k != 0) {res = pop(nums, size - cnt);cnt ++;k --;}return res;}int parent(int x) {return (x - 1) / 2;}int leftChild(int x) {return 2 * x + 1;}int rightChild(int x) {return 2 * x + 2;}void swap(int[] nums, int i, int j) {int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}void siftDown(int[] heap, int i, int size) {int maxIndex = i;int left = leftChild(i);int right = rightChild(i);if (left < size && heap[left] > heap[maxIndex]) maxIndex = left;if (right < size && heap[right] > heap[maxIndex]) maxIndex = right;if (maxIndex != i) {swap(heap, i, maxIndex);siftDown(heap, maxIndex, size);}}void heaplify (int[] heap, int size) {for (int i = parent(size - 1); i >= 0; i --) {siftDown(heap, i, size);}}int pop(int[] heap, int size) {int top = heap[0];heap[0] = heap[size - 1];siftDown(heap, 0, size - 1);return top;}
}
502. IPO
思路
每次希望选择能满足资本且最大利润的项目。初始资本为w,将需要资本小于w的项目加入大根堆中。弹出堆顶元素假设为利润为pi,资本为ci。完成项目后资本为w-ci + pi。紧接着继续将剩余满足条件的加入堆中。
代码
class Solution {public int findMaximizedCapital(int k, int w, int[] profits, int[] capital) {int n = profits.length, index = 0;Pair[] pair = new Pair[n];for (int i = 0; i < n; i ++) {pair[i] = new Pair(profits[i], capital[i]);}Arrays.sort(pair, (a, b) -> (a.c - b.c));PriorityQueue<Pair> pq = new LinkedList<>((a, b) -> (b.p - a.p));for (int i = 0; i < k; i ++) {while (w >= pair[index].c) {pq.offer(pair[index]);index ++;}Pair p = pq.poll();w = w + p.p;}return w;}
}
class Pair {int p;int c;Pair(int p, int c) {this.p = p;this.c = c;}
}
373. 查找和最小的 K 对数字

思路
找出最小的k个数对,其中一个数来自第一个数组,第二个数来自第二个数组。每个数组按照非递减顺序排序。那么容易确定最小的数对坐标为(0, 0),其次是(0, 1)或者(1, 0),在这之后可能是(1, 1),(0, 2), (1,1), (2, 1),…;可以看出其中会有重复的数对,出现这种情况的原因是两边都交替+1。我们规定(i, j)出堆时,只有(i + 1, j)入堆,而(i , j + 1),只会是由(i - 1, j + 1)得来。因此初始时要将所有(0, j)都压入小顶堆中。
代码
class Solution {public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {int n = nums1.length, m = nums2.length;PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> ((nums1[a[0]] + nums2[a[1]]) - (nums1[b[0]] + nums2[b[1]])));for (int i = 0; i < m; i ++) {pq.offer(new int[]{0, i});}List<List<Integer>> res = new ArrayList<>();for (int i = 0; i < k; i ++) {int[] index = pq.poll();List<Integer> list = new ArrayList<>();list.add(nums1[index[0]]);list.add(nums2[index[1]]);res.add(list);if (index[0] + 1 < n) pq.offer(new int[]{index[0] + 1, index[1]});}return res;}
}
295. 数据流的中位数

思路
由于是边添加边返回目前为止的中位数,因此需要维护一个数据结构可以及时返回当前中位数。中位数的特点是左边都是比中位数小的右边都是比中位数大的,也就是中位数比左边最大值比右边最小值小并且左边和右边的长度在个数是奇数时要相等,个数是偶数时也要相等。默认奇数时中位数在左边,也就是右边要小于等于中位数。由于数据结构要动态获取最大值或者最小值,因此维持一个堆结构即可。
代码
class MedianFinder {PriorityQueue<Integer> maxHeap;PriorityQueue<Integer> minHeap;public MedianFinder() {maxHeap = new PriorityQueue<>((a, b) -> (b - a)); // 存左边minHeap = new PriorityQueue<>((a, b) -> (a - b)); // 存右边}public void addNum(int num) {int maxSize = maxHeap.size(), minSize = minHeap.size();// 维持数量if (maxSize <= minSize) {maxHeap.offer(num);} else {minHeap.offer(num);}// 维持大小if (!maxHeap.isEmpty() && !minHeap.isEmpty()) {int leftValue = maxHeap.peek(), rightValue = minHeap.peek();if (leftValue > rightValue) {maxHeap.poll();maxHeap.offer(rightValue);minHeap.poll();minHeap.offer(leftValue);} } }public double findMedian() {int maxSize = maxHeap.size(), minSize = minHeap.size();if (maxSize > minSize) return maxHeap.peek();else return ((double) (maxHeap.peek() + minHeap.peek())) / 2;}
}/*** Your MedianFinder object will be instantiated and called as such:* MedianFinder obj = new MedianFinder();* obj.addNum(num);* double param_2 = obj.findMedian();*/
