• 【LeetCode】堆 heap(共31题)


    链接:https://leetcode.com/tag/heap/

    【23】 Merge k Sorted Lists

    【215】 Kth Largest Element in an Array (无序数组中最小/大的K个数)(2018年11月30日第一次复习)

    给了一个无序数组,可能有重复数字,找到第 k 个最大的元素并且返回这个元素值。

    题解:直接用直接用个堆保存数组中最大的 K 个数。时间复杂度是 O(NlogK)。

     1 //时间复杂度是 O(NlogK), 用堆辅助。
     2 class Solution {
     3 public:
     4     int findKthLargest(vector<int>& nums, int k) {
     5         const int n = nums.size();
     6         priority_queue<int, vector<int>, greater<int>> pq;
     7         for (int i = 0; i < n; ++i) {
     8             if (pq.size() < k) {
     9                 pq.push(nums[i]);
    10             } else {
    11                 if (pq.top() < nums[i]) {
    12                     pq.pop();
    13                     pq.push(nums[i]);
    14                 }
    15             }
    16         }
    17         return pq.top();
    18     }
    19 };
    View Code

    本题可以有 O(N) 的解法,详见《剑指offer》或者《程序员代码面试指南》P336

    做法是quickSelect。

    【218】 The Skyline Problem(2019年2月5日,算法群打卡复习)

    给了一个tuple list, 每个三元组 [a, b, h] 代表一个楼的x轴坐标是在[a, b],楼的高度是 h,返回所有楼轮廓的左上角坐标。

    题解:用了一个 multiset 来存这个楼是进来还是出去,如果是进来,就存[a, h], 如果是出去,就存[b, -h]。 然后遍历这个 multiset,如果这个楼是进来的话,就看当前高度它是不是最高的,如果是,那么这个点在答案中,如果这个楼是出去的话,先把这个高度删除,然后看当前这个高度是不是比剩下所有楼都高,如果是,就把当前坐标和剩下的最高的楼组成的坐标加入答案中。

     1 class Solution {
     2 public:
     3     vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings) {
     4         for (auto& b : buildings) {
     5             record.insert(make_pair(b[0], b[2]));
     6             record.insert(make_pair(b[1], -b[2]));
     7         }
     8         vector<pair<int, int>> ret;
     9         for (auto& r : record) {
    10             bool entry = r.second > 0 ? true : false;
    11             int idx = r.first, h = abs(r.second);
    12             // printf("idx = %d, h = %d, entry = %d
    ", idx, h, entry);
    13             if (entry) {
    14                 if (h > getMaxHeight()) {
    15                     ret.push_back(make_pair(idx, h));
    16                 }
    17                 height.insert(h);
    18             } else {
    19                 auto iter = height.find(h);
    20                 height.erase(iter);
    21                 if (h > getMaxHeight()) {
    22                     ret.push_back(make_pair(idx, getMaxHeight()));
    23                 }
    24             }
    25         } 
    26         return ret;
    27     }
    28     struct kcmp {
    29         bool operator() (const pair<int, int>& p1, const pair<int, int>& p2) const {
    30             if (p1.first == p2.first) {
    31                 return p1.second > p2.second;
    32             }
    33             return p1.first < p2.first;
    34         }
    35     };
    36     multiset<pair<int, int>, kcmp> record;    
    37     multiset<int> height;
    38     int getMaxHeight() {
    39         if (height.empty()) {return 0;}
    40         return *height.rbegin();
    41     }
    42 };
    View Code

    【239】 Sliding Window Maximum (2019年2月18日复习)(用deque做)

    求一个滑动窗口的最大值。

    题解:用deque做。单调队列。一开始还想错了,需要多跑几个case验证一下正确性。时间复杂度是O(N)

     1 class Solution {
     2 public:
     3     // [1  3  -1] -3  5  3  6  7 
     4     // 0 1 2 
     5     vector<int> maxSlidingWindow(vector<int>& nums, int k) {
     6         const int n = nums.size();
     7         deque<int> dq;
     8         vector<int> res;
     9         for (int i = 0; i < n; ++i) {
    10             if (!dq.empty() && i - dq.front() >= k) {
    11                 dq.pop_front();
    12             }
    13             while (!dq.empty() && nums[dq.back()] < nums[i]) {
    14                 dq.pop_back();
    15             }
    16             dq.push_back(i);
    17             if (i - k + 1 >= 0) {
    18                 res.push_back(nums[dq.front()]);
    19             }
    20         }
    21         return res;
    22     }
    23 };
    View Code

    【253】 Meeting Rooms II

    题意是252的升级版,给了一个数组,数组里面的每个元素代表一个会议的开始时间和结束时间,问想安排下所有的会议,至少需要多少个会议室。

    题解:这个题目在 sort 的分类里面说过,链接:https://www.cnblogs.com/zhangwanying/p/9914941.html 

    【264】 Ugly Number II

    返回第 N 个丑数,丑数的定义是因子只有2 ,3 ,5 的数。

    题解:我们用三根指针指向ans中应该乘2, 3 ,5的位置,然后从中选出最小的元素。

     1 class Solution {
     2 public:
     3     int nthUglyNumber(int n) {
     4         vector<int> nums(n, INT_MAX);
     5         nums[0] = 1;
     6         int p1 = 0, p2 = 0, p3 = 0;
     7         for (int i = 1; i < n; ++i) {
     8             int minn = min(nums[p1] * 2, min(nums[p2] * 3, nums[p3] * 5));
     9             if (minn == nums[p1] * 2) {++p1;}
    10             if (minn == nums[p2] * 3) {++p2;}
    11             if (minn == nums[p3] * 5) {++p3;}
    12             nums[i] = minn;
    13         }
    14         return nums[n-1];
    15     }
    16 };
    View Code

    【295】 Find Median from Data Stream (2018年11月30日,堆的题目)

    给了一个数据流,设计api接口,addNum() 用于接收一个数,findMedian() 用于返回数据流的中位数。返回数据流的中位数。

    Example:
    addNum(1)
    addNum(2)
    findMedian() -> 1.5
    addNum(3) 
    findMedian() -> 2

    题解:这题是今天看算法课看到的一个题,我们用一个大根堆存储数据流比较小的一半数字,用一个小根堆存数据流的比较大的一半数字。(这两个堆的元素个数要么相等,要么存小数的那个堆比存大数的那个堆多存一个数。)中位数如果两个堆元素个数相差 1 ,那么就返回大根堆的堆顶。不然返回两个堆顶的平均数。

     1 class MedianFinder {
     2 public:
     3     /** initialize your data structure here. */
     4     MedianFinder() {
     5         
     6     }
     7     void addNum(int num) {
     8         lowhalf.push(num);
     9         int k = lowhalf.top(); lowhalf.pop();
    10         highhalf.push(k);
    11         if (lowhalf.size() < highhalf.size()) {
    12             k = highhalf.top(); highhalf.pop();
    13             lowhalf.push(k);
    14         }
    15         return;
    16     }
    17     double findMedian() {
    18         if (lowhalf.size() == highhalf.size()) {
    19             return (double)(lowhalf.top() + highhalf.top()) / 2;
    20         }
    21         return (double)lowhalf.top();
    22     }
    23     priority_queue<int, vector<int>, greater<int>> lowhalf;  //小根堆存大的那边
    24     priority_queue<int> highhalf; //大根堆存小的那边 
    25 };
    26 
    27 /**
    28  * Your MedianFinder object will be instantiated and called as such:
    29  * MedianFinder obj = new MedianFinder();
    30  * obj.addNum(num);
    31  * double param_2 = obj.findMedian();
    32  */
    View Code

    【313】 Super Ugly Number (2019年2月9日,heap复习)

    给了一个prime的数组(数组中 m 个元素),返回第 n 个 ugly number,ugly number 的定义是所有的因子都必须是 prime 数组中的元素。

    题解:解法同因子是 2,3,5 的丑数,我们用 m 个指针分别指向prime数组中数应该乘的位置。然后同ugly number那题一样。时间复杂度是 O(N*M)

     1 class Solution {
     2 public:
     3     int nthSuperUglyNumber(int n, vector<int>& primes) {
     4         const int m = primes.size();
     5         sort(primes.begin(), primes.end());
     6         vector<int> ptr(m, 0);
     7         vector<int> ans(n, INT_MAX);
     8         ans[0] = 1;
     9         for (int i = 1; i < n; ++i) {
    10             int minn = INT_MAX;
    11             for (int k = 0; k < m; ++k) {
    12                 minn = min(minn, ans[ptr[k]] * primes[k]);
    13             }
    14             for (int k = 0; k < m; ++k) {
    15                 if (minn == ans[ptr[k]] * primes[k]) {
    16                     ptr[k]++;
    17                 }
    18             }
    19             ans[i] = minn;
    20         }
    21         return ans[n-1];
    22     }
    23 };
    View Code

    【347】 Top K Frequent Elements

    【355】 Design Twitter (2018年12月3日,heap专题)

    实现四个 api。 

    1. postTweet(userId, tweetId): Compose a new tweet.
    2. getNewsFeed(userId): Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent.
    3. follow(followerId, followeeId): Follower follows a followee.
    4. unfollow(followerId, followeeId): Follower unfollows a followee.
    Example:
    Twitter twitter = new Twitter();
    
    // User 1 posts a new tweet (id = 5).
    twitter.postTweet(1, 5);
    
    // User 1's news feed should return a list with 1 tweet id -> [5].
    twitter.getNewsFeed(1);
    
    // User 1 follows user 2.
    twitter.follow(1, 2);
    
    // User 2 posts a new tweet (id = 6).
    twitter.postTweet(2, 6);
    
    // User 1's news feed should return a list with 2 tweet ids -> [6, 5].
    // Tweet id 6 should precede tweet id 5 because it is posted after tweet id 5.
    twitter.getNewsFeed(1);
    
    // User 1 unfollows user 2.
    twitter.unfollow(1, 2);
    
    // User 1's news feed should return a list with 1 tweet id -> [5],
    // since user 1 is no longer following user 2.
    twitter.getNewsFeed(1);

    题解:用个 unordered_map 标记用户关系,然后一个 vector 存储 twitter post

     1 class Twitter {
     2 public:
     3     /** Initialize your data structure here. */
     4     Twitter() {
     5         
     6     }
     7     
     8     /** Compose a new tweet. */
     9     void postTweet(int userId, int tweetId) {
    10         twit.push_back(make_pair(userId, tweetId));
    11     }
    12     
    13     /** Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent. */
    14     vector<int> getNewsFeed(int userId) {
    15         set<int> people = followRelation[userId];
    16         people.insert(userId);
    17         vector<int> ret;
    18         int idx = twit.size() - 1, cnt = 0;;
    19         while (cnt < 10 && idx >= 0) {
    20             auto p = twit[idx--];
    21             int uid = p.first, tid = p.second;
    22             if (people.find(uid) != people.end()) {
    23                 ret.push_back(tid);
    24                 cnt++;
    25             }
    26         }
    27         return ret;
    28     }
    29     
    30     /** Follower follows a followee. If the operation is invalid, it should be a no-op. */
    31     void follow(int followerId, int followeeId) {
    32         followRelation[followerId].insert(followeeId);
    33     }
    34     
    35     /** Follower unfollows a followee. If the operation is invalid, it should be a no-op. */
    36     void unfollow(int followerId, int followeeId) {
    37         if (followRelation.find(followerId) == followRelation.end()) { return; }
    38         if (followRelation[followerId].find(followeeId) == followRelation[followerId].end()) {return;}
    39         followRelation[followerId].erase(followeeId);
    40     }
    41     
    42     unordered_map<int, set<int>> followRelation;
    43     vector<pair<int, int>> twit;  // (user, id)
    44 };
    45 
    46 /**
    47  * Your Twitter object will be instantiated and called as such:
    48  * Twitter obj = new Twitter();
    49  * obj.postTweet(userId,tweetId);
    50  * vector<int> param_2 = obj.getNewsFeed(userId);
    51  * obj.follow(followerId,followeeId);
    52  * obj.unfollow(followerId,followeeId);
    53  */
    View Code

      

    【358】 Rearrange String k Distance Apart

    【373】 Find K Pairs with Smallest Sums (2018年12月1日)

    给了两个整数数组 nums1 和 nums2, 和一个整数 k,求 nums1 和 nums2 的笛卡尔积中(就是 nums1 中选择一个数字 n1,nums2 中选择一个数字 n2 组成pair),sum 最小的 k 个pair。

    题解:本题就是自定义排序的大根堆,pair排序的规则是 sum 大的在堆顶。

     1 class Solution {
     2 public:
     3     vector<pair<int, int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
     4         if (nums1.empty() || nums2.empty()) {return vector<pair<int, int>>();}
     5         priority_queue<pair<int, int>, vector<pair<int, int>>, cmp> pq;
     6         for (auto n1 : nums1) {
     7             for (auto n2 : nums2) {
     8                 pq.push(make_pair(n1, n2));
     9                 if (pq.size() > k) { pq.pop(); }
    10             }
    11         }
    12         vector<pair<int, int>> ret; //可能nums1和nums2的组合的pair不够 k 个,所以不能一开始初始化为 k 个结果。
    13         while (!pq.empty()) {
    14             ret.push_back(pq.top());
    15             pq.pop();
    16         }
    17         return ret;
    18     }
    19     struct cmp{
    20         bool operator()(const pair<int, int>& p1, const pair<int, int>& p2) {
    21             return p1.first + p1.second < p2.first + p2.second;
    22         }
    23     };
    24 };
    View Code

      

    【378】 Kth Smallest Element in a Sorted Matrix

    【407】 Trapping Rain Water II

    【451】 Sort Characters By Frequency

    【502】 IPO (2019年2月9日,算法群打卡)

    给了一个 n 个项目,每个项目都有一个能力值 capital[i] 和一个利益值 profits[i],一开始有一个初始的能力值 W,要求是从这 n 个项目中选取 k 个项目,每次选取的项目的能力值capital[i]必须小于等于当前的人的能力值。每次做完一个项目,这个项目的利益值就会加在现在的能力值上面。问做完 k 个项目之后,最后能力值最大是多少。

    题解:题目不难,重点在于利用heap实现。我们想着把 capital 数组 和 profits 数组综合成一个数组,然后给新的数组排序,排序的方式按照capital从小到大,然后 profits 随意。

    然后利用一个 heap, 每次选择一个项目的时候,都把小于当前能力值的项目放进heap,然后从 heap 中弹出一个当前利益值最大的项目,作为当前项目的选择。

    时间复杂度是 nlog(n)

     1 class Solution {
     2 public:
     3     int findMaximizedCapital(int k, int W, vector<int>& Profits, vector<int>& Capital) {
     4         const int n = Profits.size();
     5         vector<pair<int, int>> project(n);
     6         for (int i = 0; i < n; ++i) {
     7             project[i] = make_pair(Capital[i], Profits[i]);
     8         }
     9         sort(project.begin(), project.end());
    10         priority_queue<pair<int, int>, vector<pair<int, int>>, cmp> pq;
    11         int idx = 0;
    12         for (int i = 0; i < k; ++i) {
    13             while (idx < n && project[idx].first <= W) {
    14                 pq.push(project[idx]);
    15                 ++idx;
    16             }
    17             if (pq.empty()) { break; }
    18             pair<int, int> p = pq.top();
    19             pq.pop();
    20             W += p.second;
    21         }
    22         return W;
    23     }
    24     struct cmp {
    25         bool operator() (const pair<int, int>& p1, const pair<int, int>& p2) const {
    26             return p1.second < p2.second;
    27         }  
    28     };
    29 };
    View Code

    【659】 Split Array into Consecutive Subsequences

    【692】 Top K Frequent Words (2018年11月30日)

    给了一个string类型的数组 words,在给定整数k,请严格按照出现频率顺序打印出现次数前 k 名的字符串。(出现频率相同的按照字典顺序打印)

    题解:用个 unordered_map 存储每个单词出现的次数,然后建立一个小根堆,里面存 k 个pair,(小根堆自定义排序class),然后遍历一遍map,把最后剩下在堆里的 k 个pair的字符串提取出来。 

     1 class Solution {
     2 public:
     3     vector<string> topKFrequent(vector<string>& words, int k) {
     4         const int n = words.size();
     5         unordered_map<string, int> mp;
     6         for (auto w : words) { mp[w]++; }
     7 
     8         priority_queue<pair<string, int>, vector<pair<string, int>>, cmp> pq;  
     9         for (auto ele : mp) {
    10             pq.push(ele);
    11             if (pq.size() > k) { pq.pop(); }
    12         }
    13         vector<string> ret(k, "");
    14         int idx = k-1;
    15         while (!pq.empty()) {
    16             auto p = pq.top();
    17             pq.pop();
    18             ret[idx--] = p.first;
    19         }
    20         return ret;
    21     }
    22     //自定义比较函数,为啥我想小的排前面要写大于。。
    23     struct cmp {
    24         bool operator()(const pair<string, int> a, const pair<string, int>& b) {
    25             if (a.second == b.second) {
    26                 return a.first < b.first;
    27             }
    28             return a.second > b.second;
    29         }
    30     };
    31 };
    View Code

        //自定义比较函数,为啥我想小的排前面要写大于。。(优先队列的自定义比较函数要搞明白,还是不懂啊) 

    【703】 Kth Largest Element in a Stream

    给个数字流,总是返回最大的第K个元素

    解法就是用一个只有K个元素的堆,维护这这些数字里面从最大的第K个到最大的元素。

    [最小元素...第K大的元素..最大元素], 这个堆总维护后半段的K个

    View Code

    【719】 Find K-th Smallest Pair Distance

    【743】 Network Delay Time (2018年12月4日)

    一个社交网络里面有 N 个结点,标号为 1 ~ N,给了一个 times 数组,里面的元素 t(u, v, w) 代表从 u结点 到 v结点需要 w 的时间,给了一个源点 K, 问从 K 开始传播一条信息,到每个结点需要至少多少时间,如果从 K 到一个结点不可达,就返回 -1。

    题解:floyed求最短路。

     1 class Solution {
     2 public:
     3     int networkDelayTime(vector<vector<int>>& times, int N, int K) {
     4         vector<vector<int>> g(N+1, vector<int>(N+1, INT_MAX));
     5         for (auto t : times) {
     6             int u = t[0], v = t[1], cost = t[2];
     7             g[u][v] = cost;
     8         }
     9         //floyed
    10         for (int f = 1; f <= N; ++f) {
    11             for (int i = 1; i <= N; ++i) {
    12                 g[i][i] = 0;
    13                 for (int j = 1; j <= N; ++j) {
    14                     if (g[i][f] != INT_MAX && g[f][j] != INT_MAX) {
    15                         g[i][j] = min(g[i][f] + g[f][j], g[i][j]);
    16                     }
    17                 }
    18             }
    19         }
    20         int ret = INT_MIN;
    21         for (int i = 1; i <= N; ++i) {
    22             if (i == K) {continue;}
    23             if (g[K][i] == INT_MAX) {
    24                 ret = -1;
    25                 break;
    26             }
    27             ret = max(ret, g[K][i]);
    28         }             
    29         return ret;            
    30     }
    31 };
    View Code

    此外,我没看出来跟 heap 有啥关系。

    【759】 Employee Free Time

    【767】 Reorganize String

    【778】 Swim in Rising Water

    【786】 K-th Smallest Prime Fraction

    【787】 Cheapest Flights Within K Stops (2019年2月9日)

    2019年4月6日更新。heap 是个好东西。

    给定了一张有向图,还有边的权重,问最多经过K个结点的情况下,从 src 到 dst 的最小花费。

    题解:可以用 dijkstra 的思路,只不过这个题多了一个条件是最多经过 K 个结点,那么我们就可以在放入heap的结点中,新增一个纬度,叫做经过的站数。

     1 //dijkstra 的思路,加上一个K站的条件
     2 class Solution {
     3 public:
     4     int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int K) {
     5         vector<vector<int>> graph(n, vector<int>(n, -1));
     6         for (int u = 0; u < n; ++u) { graph[u][u] = 0; }
     7         for (auto& f : flights) {
     8             int u = f[0], v = f[1], w = f[2];
     9             graph[u][v] = w;
    10         }
    11         priority_queue<vector<int>, vector<vector<int>>, greater<vector<int>>> pq; //{cost, city, how many stops visited}
    12         pq.push({0, src, 0});
    13         while (!pq.empty()) {
    14             auto temp = pq.top(); pq.pop();
    15             int price = temp[0], city = temp[1], stops = temp[2]; 
    16             if (city == dst) {return price;}
    17             for (int v = 0; v < n; ++v) {
    18                 if (graph[city][v] == -1 || city == v) {continue;}
    19                 if (stops <= K) {
    20                     pq.push({price + graph[city][v], v, stops + 1});
    21                 }
    22             }
    23         }
    24         return -1;
    25     }
    26 };
    View Code

    【818】 Race Car

    【857】 Minimum Cost to Hire K Workers (2019年3月15日,google tag)

    题目给了两个规则让我们设计一个花费最小的办法雇佣K个工人。

    1. Every worker in the paid group should be paid in the ratio of their quality compared to other workers in the paid group. 必须按照unitWage最大的那个工人的unitWage来付所有人的钱。
    2. Every worker in the paid group must be paid at least their minimum wage expectation.

    题解:我们考虑如果将wage[i]/quality[i]作为评价指标来进行排序意味着什么?unitWage从小到大排序。 (wisdompeak大佬的题解)

    wage[i]/quality[i]最高的那位,意味着最不实惠的工人,它拉高了unitWage,使得其他工人都必须按照这个unitWage乘以各自的quality拿工资.但转念一想,如果我们必须雇佣这个最不实惠的工人的话,那么剩下的工人该如何选择呢?显然我们只要选K-1个quality最低的工人,他们可以拉高那个"最不实惠工人"的quality比重,从而减少其他工人的quality比重,从而降低总工资.

    我们再考虑,如果选择了wage[i]/quality[i]第二高的那位,那么我们就在接下来的 N-2 个人里面选择 K-1 个quality最底的工人即可.

    由此贪心法的最优策略就出来了.实际操作中,我们根据 wage[i]/quality[i] 从低到高进行处理.

     1 class Solution {
     2 public:
     3     double mincostToHireWorkers(vector<int>& quality, vector<int>& wage, int K) {
     4         int n = quality.size();
     5         vector<pair<double, int>> workers(n);
     6         for (int i = 0; i < n; ++i) {
     7             workers[i] = {(double)wage[i]/quality[i], quality[i]};
     8         }
     9         sort(workers.begin(), workers.end());
    10         int totQuality = 0;
    11         double res = DBL_MAX;
    12         priority_queue<int, vector<int>, less<int>> pq;
    13         for (int i = 0; i < n; ++i) {
    14             double unitWage = workers[i].first;
    15             int qua = workers[i].second;
    16             if (pq.size() == K) {
    17                 totQuality -= pq.top();
    18                 pq.pop();
    19             }
    20             totQuality += qua;
    21             pq.push(qua);
    22             if (pq.size() == K) {
    23                 res = min(res, unitWage * totQuality);
    24             }
    25         }
    26         return res;
    27     }
    28 };
    View Code

    【864】 Shortest Path to Get All Keys

    【871】 Minimum Number of Refueling Stops

    【882】 Reachable Nodes In Subdivided Graph

  • 相关阅读:
    用 C# 获取 IE 临时文件(转)
    vs2008打包程序需要.net3.5支持问题的解决方案
    关于使用ssh账号上外网
    元数据管理技术及发展应用现状
    一个拨号上网的批处理文件
    windows下启动和关闭oracle数据库的bat脚本
    Solaris下配置网络
    开启windows 2000 server上的远程桌面
    FileZilla客户端使用TIPs
    学习使用gvim
  • 原文地址:https://www.cnblogs.com/zhangwanying/p/9357141.html
Copyright © 2020-2023  润新知