贪心算法
- 分发饼干
贪心策略:给胃口最小的孩子分发尺寸最小的饼干。
int findContentChildren(vector<int>& g, vector<int>& s) {
std::sort(g.begin(), g.end());
std::sort(s.begin(), s.end());
int i = 0, j = 0;
while (i < g.size() && j < s.size()) {
if (g[i] <= s[j])
i++;
j++;
}
return i;
}
- 分发糖果
贪心策略:遍历两次,每次遍历时只考虑并更新相邻一侧的大小关系。
int candy(vector<int>& ratings) {
int size = ratings.size();
vector<int> count(size, 1);
for (int i = 1; i < size; ++i) {
if (ratings[i] > ratings[i - 1]) {
count[i] = count[i - 1] + 1;
}
}
for (int i = size - 2; i >= 0; --i) {
if (ratings[i] > ratings[i + 1]) {
count[i] = max(count[i], count[i + 1] + 1); // 不能破坏已有的关系
}
}
return std::accumulate(count.begin(), count.end(), 0);
}
- 无重叠区间
贪心策略:优先保留右端点小且不相交的区间。
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
// 按右端点从小到大进行排序
sort(intervals.begin(), intervals.end(),
[&](const vector<int>& a, const vector<int>& b) {
return a[1] < b[1];
});
int count = 0;
int end = intervals[0][1];
for (int i = 1; i < intervals.size(); ++i) {
if (intervals[i][0] < end)
count++;
else
end = intervals[i][1];
}
return count;
}
- 种花问题
解法1:修改flower数组
bool canPlaceFlowers(vector<int>& flowerbed, int n) {
flowerbed.push_back(0); // 为了方便遍历,在前面都加上一个0
flowerbed.insert(flowerbed.begin(), 0);
int size = flowerbed.size();
for (int i = 1; i < size - 1; ++i) {
if (flowerbed[i - 1] == 0 && flowerbed[i] == 0 &&
flowerbed[i + 1] == 0) {
flowerbed[i] = 1;
n--;
}
}
return n <= 0;
}
解法2:不修改flower数组
bool canPlaceFlowers(vector<int>& flowerbed, int n) {
int size = flowerbed.size();
for (int i = 0; i < size; ++i) {
if ((i == 0 || flowerbed[i - 1] == 0) && flowerbed[i] == 0 &&
(i == size - 1 || flowerbed[i + 1] == 0)) {
n--; // 说明flower[i]可以种花
i++; // 这里没有修改flower[i]的值为1,所以必须跳过下一个,否则遍历下一个元素时会认为前面元素为0
}
}
return n <= 0;
}
- 用最少数量的箭引爆气球
> 根据区间左端点进行排序,然后取交集
int findMinArrowShots(vector<vector<int>>& points) {
int size = points.size();
sort(points.begin(), points.end(),
[&](const vector<int>& a, const vector<int>& b) {
return a[0] < b[0];
});
int count = 1;
vector<int> interval(points[0]);
for (int i = 1; i < size; ++i) {
if (points[i][0] <= interval[1]) {
interval[1] = min(points[i][1],interval[1]);
} else {
interval[1] = points[i][1];
count++;
}
interval[0] = points[i][0];
}
return count;
}
- 划分字母区间
先记录每个字符最后一次出现的下标,然后在遍历时不断更新当前区间的右端点,如果遍历到了右端点则说明当前区间遍历结束。
vector<int> partitionLabels(string s) {
vector<int> result;
vector<int> index(26, -1);
for (int i = 0; i < s.length(); ++i) {
index[s[i] - 'a'] = i;
}
int start = 0;
int end = -1;
for (int i = 0; i < s.length(); ++i) {
end = max(index[s[i] - 'a'], end);
if (i == end) {
result.push_back(i - start + 1);
start = i + 1;
}
}
return result;
}
- 买卖股票的最佳时机 II
贪心策略:只比较前后两天的大小
int maxProfit(vector<int>& prices) {
int result = 0;
for (int i = 1; i < prices.size(); ++i) {
if (prices[i] > prices[i - 1]) {
result += prices[i] - prices[i - 1];
}
}
return result;
}
- 根据身高重建队列
先根据hi从大到小排列,然后根据ki从小到大排列(这个很关键,保证后面插入的元素不会破坏已有的排列),然后依次插入新的集合中
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
vector<vector<int>> result;
sort(people.begin(), people.end(),
[&](const vector<int>& a, const vector<int>& b) {
return a[0] != b[0] ? a[0] > b[0] : a[1] < b[1];
});
for (auto& x : people) {
result.insert(result.begin() + x[1], x);
}
return result;
}
- 非递减数列
贪心策略:每次保证连续3个元素都非递减数列
bool checkPossibility(vector<int>& nums) {
int count = 0;
for (int i = 1; i < nums.size() - 1; ++i) {
if (nums[i - 1] > nums[i]) {
if (nums[i] > nums[i + 1])
return false;
if (nums[i] < nums[i + 1]) {
count++;
}
} else if (nums[i] > nums[i + 1]) {
if (nums[i - 1] > nums[i + 1]) {
nums[i + 1] = nums[i];
} else {
nums[i] = nums[i + 1];
}
count++;
}
if (count > 1)
return false;
}
return true;
}