Heap 相关 295,373,1642,1405

295. Find Median from Data Stream
Hard

The median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value and the median is the mean of the two middle values.

  • For example, for arr = [2,3,4], the median is 3.
  • For example, for arr = [2,3], the median is (2 + 3) / 2 = 2.5.

Implement the MedianFinder class:

  • MedianFinder() initializes the MedianFinder object.
  • void addNum(int num) adds the integer num from the data stream to the data structure.
  • double findMedian() returns the median of all elements so far. Answers within 10-5 of the actual answer will be accepted.

 

Example 1:

Input
["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"]
[[], [1], [2], [], [3], []]
Output
[null, null, null, 1.5, null, 2.0]

Explanation
MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1);    // arr = [1]
medianFinder.addNum(2);    // arr = [1, 2]
medianFinder.findMedian(); // return 1.5 (i.e., (1 + 2) / 2)
medianFinder.addNum(3);    // arr[1, 2, 3]
medianFinder.findMedian(); // return 2.0

Constraints:

  • -105 <= num <= 105
  • There will be at least one element in the data structure before calling findMedian.
  • At most 5 * 104 calls will be made to addNum and findMedian

解法: 一个大顶堆(存前半段元素),一个小顶堆(存后半段元素),那么取中位数只需要从堆顶取即可

时间复杂度:

Follow up:

  • If all integer numbers from the stream are in the range [0, 100], how would you optimize your solution?  

解法:可以直接开个int[100]数组,插入元素进行count[i]++, 查找的时候根据count找到最终的中位数 ,插入和查找 都为O(1) 

  • If 99% of all integer numbers from the stream are in the range [0, 100], how would you optimize your solution?

解法:可以把数据分3段:  x < 0 (使用maxheap + minheap), x in 0 ~ 100 (使用数组count), x > 100  (使用maxheap + minheap), 在中位数落入100以外的范围的时候:

   比如, 如果落在了x < 0的范围中, 需要基于0~100 和 x > 100的数量来平衡min和max: maxheap.size() ~= minHeap.size + size(x: 0~100) + size(x > 100)

class MedianFinder {
    private PriorityQueue<Integer> minHeap;
    private PriorityQueue<Integer> maxHeap;
    
    public MedianFinder() {
        minHeap = new PriorityQueue<Integer>((x, y) -> x - y);//小顶堆,存后半段
        maxHeap = new PriorityQueue<Integer>((x, y) -> y - x);//大顶堆,存前半段
    }
    
    public void addNum(int num) {//O(logN)
        if(maxHeap.isEmpty() || maxHeap.peek()>num) maxHeap.offer(num);//如果小于大顶堆top,放前半段
        else minHeap.offer(num);//否则放后半段
        //保持两堆数量平衡,或者大顶堆多一个
        while(minHeap.size()>maxHeap.size()) maxHeap.offer(minHeap.poll());
        while(minHeap.size()<maxHeap.size()-1) minHeap.offer(maxHeap.poll());
    }
    
    public double findMedian() {//O(1)
        //如果数量一样,去各自头取平均
        if(minHeap.size()==maxHeap.size()) return ((double)minHeap.peek()+(double)maxHeap.peek())/2;
        //奇数个直接去大顶堆top,因为奇数时肯定大顶堆元素多一个
        return maxHeap.peek();
    }
}

 373. Find K Pairs with Smallest Sums

Medium

You are given two integer arrays nums1 and nums2 sorted in ascending order and an integer k.

Define a pair (u, v) which consists of one element from the first array and one element from the second array.

Return the k pairs (u1, v1), (u2, v2), ..., (uk, vk) with the smallest sums.

 Example 1:

Input: nums1 = [1,7,11], nums2 = [2,4,6], k = 3
Output: [[1,2],[1,4],[1,6]]
Explanation: The first 3 pairs are returned from the sequence: [1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6]

Example 2:

Input: nums1 = [1,1,2], nums2 = [1,2,3], k = 2
Output: [[1,1],[1,1]]
Explanation: The first 2 pairs are returned from the sequence: [1,1],[1,1],[1,2],[2,1],[1,2],[2,2],[1,3],[1,3],[2,3]

Example 3:

Input: nums1 = [1,2], nums2 = [3], k = 3
Output: [[1,3],[2,3]]
Explanation: All possible pairs are returned from the sequence: [1,3],[2,3]

 Constraints:

  • 1 <= nums1.length, nums2.length <= 105
  • -109 <= nums1[i], nums2[i] <= 109
  • nums1 and nums2 both are sorted in ascending order.
  • 1 <= k <= 104
class Solution {
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        List<List<Integer>> result = new ArrayList();
        if(nums1.length==0 || nums2.length==0) return result;
        PriorityQueue<int[]> pq = new PriorityQueue<>((x,y)->x[0]+x[1]-y[0]-y[1]);
        //将num1的元素与num2的第一个元素进行配对,并且在touple中记录num2的下标
        for(int i=0;i<nums1.length && i<k;i++){
            pq.offer(new int[]{nums1[i],nums2[0],0});
        }
        while(!pq.isEmpty() && result.size()<k){
            int[] curr = pq.poll();
            result.add(List.of(curr[0], curr[1]));
            //如果num2已经到了最后一个元素,那么没有元素可以添加了
            if(curr[2]+1 >= nums2.length) continue;
            //添加num2下一个元素对应当前的num1元素
            pq.offer(new int[]{curr[0], nums2[curr[2]+1], curr[2]+1});
        }
        return result;
    }
}

 

class Solution {
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        List<List<Integer>> result = new ArrayList<>();
        int i = 0, j = 0, count = 0, len1 = nums1.length, len2 = nums2.length;
        PriorityQueue<int[]> heap = new PriorityQueue<>((x, y) -> {
            return x[0] - y[0];
        });
        Set<Pair> visited = new HashSet<>();
        // 将[0,0]位置放入heap
        heap.offer(new int[]{nums1[0] + nums2[0], 0, 0});
        visited.add(new Pair(0, 0));
        while(result.size() < k) {
            int[] curr = heap.poll();
            int ind1 =  curr[1], ind2 = curr[2];
            result.add(List.of(nums1[ind1], nums2[ind2]));
            //每次取出pair后,将(x+1, y)和(x, y+1)放入队列,但是要记得排重
            if(ind1 < len1 - 1 && visited.add(new Pair(ind1 + 1, ind2))) {
                heap.offer(new int[]{nums1[ind1 + 1] + nums2[ind2], ind1 + 1, ind2});
            }
            if(ind2 < len2 - 1 && visited.add(new Pair(ind1, ind2 + 1))) {
                heap.offer(new int[]{nums1[ind1] + nums2[ind2 + 1], ind1, ind2 + 1});
            }
        }
        return result;
    }
    record Pair(int pos1, int pos2) {}
}

 

1642. Furthest Building You Can Reach
Medium

You are given an integer array heights representing the heights of buildings, some bricks, and some ladders.

You start your journey from building 0 and move to the next building by possibly using bricks or ladders.

While moving from building i to building i+1 (0-indexed),

  • If the current building's height is greater than or equal to the next building's height, you do not need a ladder or bricks.
  • If the current building's height is less than the next building's height, you can either use one ladder or (h[i+1] - h[i]) bricks.

Return the furthest building index (0-indexed) you can reach if you use the given ladders and bricks optimally.

Example 1:

Input: heights = [4,2,7,6,9,14,12], bricks = 5, ladders = 1
Output: 4
Explanation: Starting at building 0, you can follow these steps:
- Go to building 1 without using ladders nor bricks since 4 >= 2.
- Go to building 2 using 5 bricks. You must use either bricks or ladders because 2 < 7.
- Go to building 3 without using ladders nor bricks since 7 >= 6.
- Go to building 4 using your only ladder. You must use either bricks or ladders because 6 < 9.
It is impossible to go beyond building 4 because you do not have any more bricks or ladders.

Example 2:

Input: heights = [4,12,2,7,3,18,20,3,19], bricks = 10, ladders = 2
Output: 7

Example 3:

Input: heights = [14,3,19,3], bricks = 17, ladders = 0
Output: 3

Constraints:

  • 1 <= heights.length <= 105
  • 1 <= heights[i] <= 106
  • 0 <= bricks <= 109
  • 0 <= ladders <= heights.length

解题思路:

优先对与高台阶(diff or gap)使用ladders,因为ladder没有高度限制并且每个ladder只能使用一次, 较低的使用bricks

因此,当ladder不够的时候,我们从中取最小的台阶使用bricks(使用minheap),这样可以使的结果最优化

class Solution {
    public int furthestBuilding(int[] heights, int bricks, int ladders) {
        //1.define a heap
        PriorityQueue<Integer> pq = new PriorityQueue<>((x,y)->x-y);
        //2.for loop the heights,
        //2.1 if heap size is bigger than ladders, than we need to poll the least height one to use bricks
        for(int i=0;i<heights.length;i++){
            if(i>0 && heights[i]>heights[i-1]){
                int diff = heights[i]-heights[i-1];
                pq.offer(diff);
                if(pq.size()>ladders){
                    bricks-=pq.poll();
                }
                if(bricks<0) return i-1;
            }
        }
        return heights.length-1;
    }
}

 

2462. Total Cost to Hire K Workers
Medium

You are given a 0-indexed integer array costs where costs[i] is the cost of hiring the ith worker.

You are also given two integers k and candidates. We want to hire exactly k workers according to the following rules:

  • You will run k sessions and hire exactly one worker in each session.
  • In each hiring session, choose the worker with the lowest cost from either the first candidates workers or the last candidates workers. Break the tie by the smallest index.
    • For example, if costs = [3,2,7,7,1,2] and candidates = 2, then in the first hiring session, we will choose the 4th worker because they have the lowest cost [3,2,7,7,1,2].
    • In the second hiring session, we will choose 1st worker because they have the same lowest cost as 4th worker but they have the smallest index [3,2,7,7,2]. Please note that the indexing may be changed in the process.
  • If there are fewer than candidates workers remaining, choose the worker with the lowest cost among them. Break the tie by the smallest index.
  • A worker can only be chosen once.

Return the total cost to hire exactly k workers.

 

Example 1:

Input: costs = [17,12,10,2,7,2,11,20,8], k = 3, candidates = 4
Output: 11
Explanation: We hire 3 workers in total. The total cost is initially 0.
- In the first hiring round we choose the worker from [17,12,10,2,7,2,11,20,8]. The lowest cost is 2, and we break the tie by the smallest index, which is 3. The total cost = 0 + 2 = 2.
- In the second hiring round we choose the worker from [17,12,10,7,2,11,20,8]. The lowest cost is 2 (index 4). The total cost = 2 + 2 = 4.
- In the third hiring round we choose the worker from [17,12,10,7,11,20,8]. The lowest cost is 7 (index 3). The total cost = 4 + 7 = 11. Notice that the worker with index 3 was common in the first and last four workers.
The total hiring cost is 11.

Example 2:

Input: costs = [1,2,4,1], k = 3, candidates = 3
Output: 4
Explanation: We hire 3 workers in total. The total cost is initially 0.
- In the first hiring round we choose the worker from [1,2,4,1]. The lowest cost is 1, and we break the tie by the smallest index, which is 0. The total cost = 0 + 1 = 1. Notice that workers with index 1 and 2 are common in the first and last 3 workers.
- In the second hiring round we choose the worker from [2,4,1]. The lowest cost is 1 (index 2). The total cost = 1 + 1 = 2.
- In the third hiring round there are less than three candidates. We choose the worker from the remaining workers [2,4]. The lowest cost is 2 (index 0). The total cost = 2 + 2 = 4.
The total hiring cost is 4.

 Constraints:

  • 1 <= costs.length <= 105
  • 1 <= costs[i] <= 105
  • 1 <= k, candidates <= costs.length
class Solution {
    public long totalCost(int[] costs, int k, int candidates) {
        //pq
        PriorityQueue<Integer> leftpq = new PriorityQueue<>();
        PriorityQueue<Integer> rightpq = new PriorityQueue<>();
        //left pointer ->  , right pointer <- add the 
        int left = 0, right = costs.length - 1;
        int count = 0;
        long cost = 0;
        while(count < k){
            while(left <= right && leftpq.size() < candidates){
                leftpq.offer(costs[left++]);
            }
            while(left <= right && rightpq.size() < candidates){
                rightpq.offer(costs[right--]);
            }
            int result = 0;
            if(!leftpq.isEmpty() && !rightpq.isEmpty()){
                if(leftpq.peek() <= rightpq.peek()){
                    result = leftpq.poll();
                }
                else{
                    result = rightpq.poll();
                }
            }
            else if(!leftpq.isEmpty()){
                result = leftpq.poll();
            }
            else{
                result = rightpq.poll();
            }
            cost += result;
            // System.out.println(result);
            count++;
        }
        return cost;
    }
}

 253. Meeting Rooms II

Medium

Given an array of meeting time intervals intervals where intervals[i] = [starti, endi], return the minimum number of conference rooms required.

 Example 1:

Input: intervals = [[0,30],[5,10],[15,20]]
Output: 2

Example 2:

Input: intervals = [[7,10],[2,4]]
Output: 1

 Constraints:

  • 1 <= intervals.length <= 104
  • 0 <= starti < endi <= 106

解法:扫描线,将meeting排序后(按照时间点)从前往后扫,加入新的meeting前,看是否有已经结束的meeting

class Solution {
    public int minMeetingRooms(int[][] intervals) {
        //按照start,end排序
        Arrays.sort(intervals, (x,y)->{
            if(x[0]==y[0]) return x[1]-y[1];
            return x[0]-y[0];
        });
        //建heap按照结束时间排序
        PriorityQueue<int[]> heap = new PriorityQueue<>((x,y)->{
            return x[1]-y[1];
        });
        
        int size = 0;
        for(int[] pair:intervals){
            if(heap.isEmpty()) heap.offer(pair);
            else{
                //将之前已经结束的meeting移除
                while(!heap.isEmpty() && heap.peek()[1]<=pair[0]){
                    heap.poll();
                }
                //加入新meeting
                heap.offer(pair);
            }
            //计算同时meeting的总个数
            size = Math.max(size, heap.size());
        }
        return size;
    }
}

 

1405. Longest Happy String
Medium

A string s is called happy if it satisfies the following conditions:

  • s only contains the letters 'a''b', and 'c'.
  • s does not contain any of "aaa""bbb", or "ccc" as a substring.
  • s contains at most a occurrences of the letter 'a'.
  • s contains at most b occurrences of the letter 'b'.
  • s contains at most c occurrences of the letter 'c'.

Given three integers ab, and c, return the longest possible happy string. If there are multiple longest happy strings, return any of them. If there is no such string, return the empty string "".

A substring is a contiguous sequence of characters within a string.

 

Example 1:

Input: a = 1, b = 1, c = 7
Output: "ccaccbcc"
Explanation: "ccbccacc" would also be a correct answer.

Example 2:

Input: a = 7, b = 1, c = 0
Output: "aabaa"
Explanation: It is the only correct answer in this case.

 

Constraints:

  • 0 <= a, b, c <= 100
  • a + b + c > 0
class Solution {
    public String longestDiverseString(int a, int b, int c) {
        //heap to store count
        PriorityQueue<Node> pq = new PriorityQueue<>((x, y) -> y.count - x.count);
        if(a > 0) pq.offer(new Node('a', a));
        if(b > 0) pq.offer(new Node('b', b));
        if(c > 0) pq.offer(new Node('c', c));
        
        StringBuffer sb = new StringBuffer();
        //get the most two
        //如果队列中元素至少2个
        while(pq.size() > 1){
            Node first = pq.poll();
            Node second = pq.poll();
            //如果第一个元素多余第二个元素,那么尽量多拼第一个元素
            if(first.count > second.count) {
                sb.append(first.c);
                sb.append(first.c);
                sb.append(second.c);
                first.count -= 2;
                second.count -= 1;
            }
            //如果前两个元素个数相等,那么各拼一个
            else{
                sb.append(first.c);
                sb.append(second.c);
                first.count -= 1;
                second.count -= 1;
            }
            //如果拼完还有剩余,放回队列
            if(first.count > 0) pq.offer(first);
            if(second.count > 0) pq.offer(second);
        }
        //如果最终队列部位空,那么之多也只有1个
        if(pq.size() > 0){
            Node last = pq.poll();
            //这个时候这个元素最多拼两次
            sb.append(last.c);
            if(last.count > 2) {
                sb.append(last.c);
            }
        }
        return sb.toString();
    }
}
class Node{
    char c;
    int count;
    Node(char c, int count){
        this.c = c;
        this.count = count;
    }
}

/*
ccaccbcc

a:3,b:1,c:1

a:3
b:1

if(first > second){
    put first with 2
}
else{
    put one first and one second
}


aaabb


b
*/

 

给定一列trains, [1,2],[2,10],[3,8],[7,10],[11,13] 均匀分布到3个站台,

其中一个可行解为:P1: [1,2], [7,10]  P2: [2,10], [11,13]  P3: [3,8], 也可以是: P1: [1,2], [11,13]  P2: [2,10]  P3: [3,8], [7,10]

但是这个不是正确答案:P1: [1,2], [7,10], [11,13]  P2: [2,10]  P3: [3,8],因为它的分布不够均匀

解题思路:

public class Solution {
    public static List<List<int[]>> arrangeTrain(int[][] trains, int platNums) {
        // sort trains by starttime, endtime
        Arrays.sort(trains, (x, y) -> {
            if(x[0] == y[0]) return x[1] - y[1];
            return x[0] - y[0];
        });
        // define heap, sort by endtime, trains list size
        PriorityQueue<Node> heap = new PriorityQueue<>((x, y) -> {
            int xEnd = x.getEndTime(), yEnd = y.getEndTime();
            if(xEnd == yEnd) return x.trains.size() - y.trains.size();
            return xEnd - yEnd;
        });
        // loop trains
        for(int[] train : trains) {
            // if still have empty platform
            if(heap.size() < platNums) {
                heap.offer(new Node(train));
            }
            // if no empty platform, then pick up one usable, and have least trains
            else{
                Node curr = heap.poll();
                curr.trains.add(train);
                heap.offer(curr);
            }
        }
        // return result.
        List<List<int[]>> result = new ArrayList<>();
        while(!heap.isEmpty()) {
            result.add(heap.poll().trains);
        }
        return result;
    }
}

class Node{
    List<int[]> trains = new ArrayList<>();
    Node(int[] train) {
        trains.add(train);
    }
    int getEndTime() {
        return trains.get(trains.size() - 1)[1];
    }
}

 

posted @ 2022-02-04 05:06  xiaoyongyong  阅读(50)  评论(0)    收藏  举报