LeetCode贪心 greedy(共38题)

Posted zhangwanying

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode贪心 greedy(共38题)相关的知识,希望对你有一定的参考价值。

【44】Wildcard Matching 

【45】Jump Game II (2018年11月28日,算法群衍生题)

题目背景和 55 一样的,问我能到达最后一个index的话,最少走几步。

题解:

 

【55】Jump Game (2018年11月27日,算法群)

给了一个数组nums,nums[i] = k 代表站在第 i 个位置的情况下, 我最多能往前走 k 个单位。问我能不能到达最后一个 index。

题解:虽然是贪心分类,我还是用dp解了。dp[i] 代表我能不能到达第 i 个位置。 

 1 class Solution {
 2 public:
 3     bool canJump(vector<int>& nums) {
 4         const int n = nums.size();
 5         if (n == 0) {return false;}
 6         vector<int> f(n, 0); //f[i] 代表第i个index是不是可达
 7         f[0] = 1;
 8         for (int i = 0; i < n; ++i) {
 9             if (f[i]) {
10                 const int k = nums[i];
11                 for (int j = 1; j <= k; ++j) {
12                     if (i+j >= n) {break;}
13                     f[i+j] = 1;
14                 }
15             }
16         }
17         return f[n-1] == 1;
18     }
19 };
View Code

  

【122】Best Time to Buy and Sell Stock II (2018年11月26日,算法群)

这个题目股票系列里面说了,这里不重复写了。股票系列:https://www.cnblogs.com/zhangwanying/p/9360841.html

 

【134】Gas Station (2019年1月27日,谷歌tag)

一个圆形的跑道,上面有 N 个加油站,每个加油站能加的油是 gas[i],假设你的车能加无限的油量,从第 i 个加油站跑到第 i+1 个加油站所消耗的油是 cost[i], 返回从第几个加油站能够顺时针跑完一圈,如果从任意一个都不能跑完的话,就返回-1

题解:本题似乎用到了一个数学定理/方法。就是如果能跑完一圈的话,必定存在从一个点开始,任意的点上的油量都不会为负数。

 1 class Solution {
 2 public:
 3     int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
 4         int total = 0;
 5         int sum = 0;
 6         int ans = 0;
 7         for(int i = 0; i < gas.size(); ++i){
 8             sum += gas[i] - cost[i];
 9             total += gas[i] - cost[i];
10             if(sum < 0){
11                 ans = i+1;
12                 sum = 0;
13             }
14         }
15         return total < 0? -1 : ans;
16     }
17 };
View Code

 

【135】Candy (2019年1月27日,谷歌tag)

每个小孩有一个 rating,你要给这些小孩发糖果,满足两条规则:规则1. 每个小孩至少一个糖果; 规则2. 如果一个小孩的rating比它的邻居高,那么他的糖果数量要比邻居多。问发完所有小孩的最少糖果数量。

题解:设置两个数组,一个front,从前往后应该发的糖果数量。一个back,表示从后往前应该发的糖果数量。final[i] = max(front[i], back[i]); O(N) with 2 pass

 1 //每个小孩至少一个糖果,
 2 //如果当前小孩的rating比它前一个小孩大的话,就是它前一个小孩的糖果数量+1
 3 //如果当前小孩的rating比它后一个小孩大的话,就是它后一个小孩的糖果数量+1
 4 class Solution {
 5 public:
 6     int candy(vector<int>& ratings) {
 7         const int n = ratings.size();
 8         vector<int> front(n , 1), back(n, 1);
 9         for (int i = 1; i < n; ++i) {
10             front[i] = ratings[i-1] < ratings[i] ? front[i-1] + 1 : 1;
11         }
12         int res = max(front[n-1], back[n-1]);
13         for (int i = n - 2; i >= 0; --i) {
14             back[i] = ratings[i] > ratings[i+1] ? back[i+1] + 1 : 1;
15             res += max(front[i], back[i]);
16         }
17         return res;
18     }
19 };
View Code

 

【253】Meeting Rooms II 

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

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

 

【316】Remove Duplicate Letters 

 

【321】Create Maximum Number 

 

【330】Patching Array 

  

【334】Increasing Triplet Subsequence (2019年2月14日,google tag)(greedy)

给了一个数组 nums,判断是否有三个数字组成子序列,使得子序列递增。题目要求time complexity: O(N),space complexity: O(1)

Return true if there exists i, j, k 
such that arr[i] < arr[j] < arr[k] given 0 ≤ i < j < k ≤ n-1 else return false.

题解:可以dp做,LIS 最少 nlog(n)。 这个题可以greedy,可以做到O(N). 我们用两个变量,min1 表示当前最小的元素,min2表示当前第二小的元素。可以分成三种情况讨论: 

(1)nums[i] < min1, -> min1 = nums[i]

(2)nums[i] > min1 && nums[i] < min2 -> min2 = nums[i]

(3)nums[i] > min2 -> return true

 1 class Solution {
 2 public:
 3     bool increasingTriplet(vector<int>& nums) {
 4         int min1 = INT_MAX, min2 = INT_MAX;
 5         for (auto& num : nums) {
 6             if (num > min2) {return true;}
 7             else if (num < min1) {
 8                 min1 = num;
 9             } else if (min1 < num && num < min2) {
10                 min2 = num;
11             }
12         }
13         return false;
14     }
15 };
View Code

 

【358】Rearrange String k Distance Apart (2019年2月17日,谷歌tag) (H)

给了一个非空的字符串 s 和一个整数 k,重新排列这个字符串使得新的字符串相同字母之间的距离至少为 k。返回新的字符串。没有这样的答案的话,返回一个空的字符串。

Input: s = "aabbcc", k = 3
Output: "abcabc" 
Explanation: The same letters are at least distance 3 from each other.

题解:我们用贪心的想法,我们选一个字符放在新字符串的当前位置,那么怎么选择这个字符呢,首先它的剩余次数需要是最多的,其次它不能在当前的 k 窗口里面出现过。所以我们用一个mp统计所有字母的频次。然后按照频次从大到小塞到heap里面。我们用k当作一个窗口,循环查找窗口中每个值。每次我们从heap里面弹出一个最大频次的字符,放在当前位置,如果它的频次减去1,剩下的值大于 0 的话,说明这个字符还存在,以后还会用到,那么就把它放在一个 cache 里面,防止在当前的 k 窗口内部继续抽到它。结束了当前 k 的窗口,把 cache 里面所有的元素都放进heap里面。

 1 class Solution {
 2 public:
 3     string rearrangeString(string s, int k) {
 4         if (k == 0) {return s;}
 5         int size = s.size();
 6         unordered_map<char, int> mp;
 7         for (auto& c : s) { mp[c]++; }
 8         priority_queue<pair<int, char>> pq;
 9         for (auto& p : mp) {
10             pq.push(make_pair(p.second, p.first));
11         }
12         string res = "";
13         while (!pq.empty()) {
14             vector<pair<int, char>> cache;
15             int count = min(size, k);
16             for (int i = 0; i < count; ++i) {
17                 if (pq.empty()) {return "";}
18                 auto p = pq.top();
19                 pq.pop();
20                 res += string(1, p.second);
21                 p.first--;
22                 size--;
23                 if (p.first > 0) {
24                     cache.push_back(p);
25                 }
26             }
27             for (auto& p: cache) {
28                 pq.push(p);
29             }
30         }
31         return res;
32     }
33 };
View Code

 

【376】Wiggle Subsequence 

【392】Is Subsequence 

【402】Remove K Digits 

 

【406】Queue Reconstruction by Height(2018年11月26日)

给了一个 people 的数组,数组里面每个元素是一个 pair (h, k) 代表 这个人身高是 h, 在排序好的队列中前面有 k 个人的升高大于等于 h。返回这个排序好的队列。

题解:有点类似于插入排序。我们先把people排序,排序按照身高降序,身高相同就按照 k 递增排序。然后做插入排序。对于排序好的people的每个元素 people[i],直接插入ret数组中的 people[i].second = k 这个位置上。

 1 class Solution {
 2 public:
 3     vector<pair<int, int>> reconstructQueue(vector<pair<int, int>>& people) {
 4         const int n = people.size();
 5         if (n == 0) {return people;}
 6         sort(people.begin(), people.end(), cmp);
 7         vector<pair<int, int>> ret;
 8         for (int i = 0; i < people.size(); ++i) {
 9             ret.insert(ret.begin() + people[i].second, people[i]);
10         }
11         return ret;
12     }
13     static bool cmp(const pair<int, int>& p1, const pair<int, int>& p2) {
14         if (p1.first == p2.first) {
15             return p1.second < p2.second;
16         }
17         return p1.first > p2.first;
18     }
19 };
View Code

 

【435】Non-overlapping Intervals (2018年11月26日)

这题应该见过了orz,莫名的熟悉。题意就是给了一堆线段,问这些线段在不重叠的前提下,最少要剔除几条满足这个线段不重叠的条件。

题解:最少剔除几条才能让所有线段不重叠,其实翻译过来,就是这些线段最多多少条不重叠。我们想这条直线上放尽可能多的线段,需要什么样的策略呢?就是第一条线段的末端的数值尽可能的小,这样后面能选择的空间才比较大。

所以先排序,按照线段末端从从小到大排序。然后贪心一个一个处理。(唯一一个注意点是线段的头尾都有可能是负数。)

 1 /**
 2  * Definition for an interval.
 3  * struct Interval {
 4  *     int start;
 5  *     int end;
 6  *     Interval() : start(0), end(0) {}
 7  *     Interval(int s, int e) : start(s), end(e) {}
 8  * };
 9  */
10 class Solution {
11 public:
12     int eraseOverlapIntervals(vector<Interval>& intervals) {
13         const int n = intervals.size();
14         if (n == 0) {return n;}
15         sort(intervals.begin(), intervals.end(), cmp);
16         int cnt = 0;
17         int b = intervals[0].start;
18         for (auto inter : intervals) {
19             if (inter.start >= b) {
20                 ++cnt;
21                 b = inter.end;
22             }
23         }
24         return n - cnt;
25     }
26     static bool cmp(const Interval& p1, const Interval& p2) {
27         if (p1.end == p2.end) {
28             return p1.start > p2.start;
29         }
30         return p1.end < p2.end;
31     }
32 };
View Code

  

【452】Minimum Number of Arrows to Burst Balloons 

【455】Assign Cookies 

【484】Find Permutation 

【502】IPO 

 

【621】Task Scheduler (2019年2月17日)

给了一个 list 的 tasks,每个task占用一个时钟,给了一个数字 n,任意两个相同的task必须间隔 n 个时钟以上。返回最小所有任务都能完成的时间。

Input: tasks = ["A","A","A","B","B","B"], n = 2
Output: 8
Explanation: A -> B -> idle -> A -> B -> idle -> A -> B. 

题解:本题同 358 Rearrange String k Distance Apart 一个解法。用一个 heap,和一个窗口 n + 1做。有个需要注意的点是,有可能当前已经没有task了,但是窗口还没有走完,需要及时break循环。时间复杂度是O(N),因为字母有限,建立堆是O(26log26)的时间复杂度。

 1 class Solution {
 2 public:
 3     int leastInterval(vector<char>& tasks, int n) {
 4         int size = tasks.size();
 5         unordered_map<char, int> mp;
 6         for (auto& c : tasks) { mp[c]++; }
 7         priority_queue<pair<int, char>> pq;
 8         for (auto& p : mp) {
 9             pq.push(make_pair(p.second, p.first));
10         }
11         int res = 0;
12         while (!pq.empty()) {
13             vector<pair<int, char>> cache;
14             int count = n + 1;
15             for (int i = 0; i < count; ++i) {
16                 if (pq.empty()) {
17                     ++res; 
18                     continue;
19                 }
20                 auto p = pq.top(); pq.pop();
21                 ++res;
22                 p.first--; size--;
23                 if (p.first > 0) {
24                     cache.push_back(p);
25                 }
26                 if (pq.empty() && cache.empty()) {break;}
27             }
28             for (auto& p : cache) {
29                 pq.push(p);
30             }
31         }
32         return res;
33     }
34 };
View Code

 

【630】Course Schedule III 

【649】Dota2 Senate 

【651】4 Keys Keyboard 

【659】Split Array into Consecutive Subsequences 

【714】Best Time to Buy and Sell Stock with Transaction Fee 

【738】Monotone Increasing Digits 

【757】Set Intersection Size At Least Two 

【759】Employee Free Time 

 

【763】Partition Labels (2018年11月27日)(这题第一遍的时候不会写,看了答案才会写。)

给了一个只含有小写字母的字符串,求这个字符串的能变成 partition label 的所有子串的长度(partition的越多越好)。能变成 partition label 的条件是,一个label里面含有的字母不能在其他label里面含有。

题解:我们用一个 map 记录每个字母最后一次出现的下标。然后用两个变量, start 和 end 表示当前 label 的开始和截止位置。遍历整个字符串,更新 end = max(mp[S[i]], end), 当我们发现 i == end 的时候,这个时候就是这个 label 结束了。

 1 //本题第一遍的时候不会做看了答案。
 2 class Solution {
 3 public:
 4     vector<int> partitionLabels(string S) {
 5         const int n = S.size();
 6         unordered_map<char, int> mp; //record the last pos of c appear in S
 7         for (int i = 0; i < n; ++i) {
 8             mp[S[i]] = i;
 9         }
10         int start = 0, end = 0;
11         vector<int> ret;
12         for (int i = 0; i < n; ++i) {
13             end = max(mp[S[i]], end);
14             if (end == i) {
15                 ret.push_back(end - start + 1);
16                 start = i + 1;
17             }
18         }
19         return ret;
20     }
21 };
View Code

  

【765】Couples Holding Hands 

 

【767】Reorganize String (2019年2月17日,谷歌tag,M)

给了一个字符串 S, 重新排列字符串,使得任意相邻的两个字母都不相同。

题解:还是heap + 贪心,相似题,【358】Rearrange String k Distance Apart ,【621】Task Scheduler 

 1 class Solution {
 2 public:
 3     string reorganizeString(string S) {
 4         const int n = S.size();
 5         unordered_map<char, int> mp;
 6         for (auto& c : S) { mp[c]++; }
 7         priority_queue<pair<int, char>> pq;
 8         for (auto& p : mp) {
 9             pq.push(make_pair(p.second, p.first));
10         }
11         string res = "";
12         while (!pq.empty()) {
13             auto p = pq.top(); pq.pop();
14             if (!res.empty() && p.second == res.back()) {
15                 auto temp = p;
16                 if (pq.empty()) {return "";}
17                 p = pq.top(); pq.pop();
18                 pq.push(temp);
19             }
20             res += string(1, p.second);
21             p.first--; 
22             if (p.first > 0) {
23                 pq.push(p);
24             }
25         }
26         return res;
27     }
28 };
View Code

 

【842】Split Array into Fibonacci Sequence 

【860】Lemonade Change 

【861】Score After Flipping Matrix 

【870】Advantage Shuffle 

【874】Walking Robot Simulation 

【881】Boats to Save People 

以上是关于LeetCode贪心 greedy(共38题)的主要内容,如果未能解决你的问题,请参考以下文章

贪心Greedy

LeetCode设计题 design(共38题)

LeetCode设计题 design(共38题)

LeetCode设计题 design(共38题)

HDU-4221 Greedy? 贪心 从元素的相对位置开始考虑

每周算法贪心算法(Greedy Algorithm)