贪心算法精选30道编程题
文章目录
- 贪心算法精选30道编程题
- 贪心算法简介
-
- [1. 柠檬⽔找零(easy)](#1. 柠檬⽔找零(easy))
- [2. 将数组和减半的最少操作次数(medium)](#2. 将数组和减半的最少操作次数(medium))
- [3. 最⼤数(medium)](#3. 最⼤数(medium))
- [4. 摆动序列(medium)](#4. 摆动序列(medium))
- [5. 最⻓递增⼦序列(medium)](#5. 最⻓递增⼦序列(medium))
- [6. 递增的三元⼦序列(medium)](#6. 递增的三元⼦序列(medium))
- [7. 最⻓连续递增序列(easy)](#7. 最⻓连续递增序列(easy))
- [8. 买卖股票的最佳时机(easy)](#8. 买卖股票的最佳时机(easy))
- [9. 买卖股票的最佳时机 Ⅱ(medium)](#9. 买卖股票的最佳时机 Ⅱ(medium))
- [10. K 次取反后最⼤化的数组和(easy)](#10. K 次取反后最⼤化的数组和(easy))
- [11. 按⾝⾼排序(easy)](#11. 按⾝⾼排序(easy))
- [12. 优势洗牌(⽥忌赛⻢)(medium)](#12. 优势洗牌(⽥忌赛⻢)(medium))
- [13. 最⻓回⽂串(easy)](#13. 最⻓回⽂串(easy))
- [14. 增减字符串匹配(easy)](#14. 增减字符串匹配(easy))
- [15. 分发饼⼲(easy)](#15. 分发饼⼲(easy))
- [16. 最优除法(medium)](#16. 最优除法(medium))
- [17. 跳跃游戏 Ⅱ(medium)](#17. 跳跃游戏 Ⅱ(medium))
- [18. 跳跃游戏(medium)](#18. 跳跃游戏(medium))
- [19. 加油站(medium)](#19. 加油站(medium))
- [20. 单调递增的数字(medium)](#20. 单调递增的数字(medium))
- [21. 坏了的计算器(medium)](#21. 坏了的计算器(medium))
- [22. 合并区间(medium)](#22. 合并区间(medium))
- [23. ⽆重叠区间(medium)](#23. ⽆重叠区间(medium))
- [24. ⽤最少数量的箭引爆⽓球(medium)](#24. ⽤最少数量的箭引爆⽓球(medium))
- [25. 整数替换(medium)](#25. 整数替换(medium))
- [26. 俄罗斯套娃信封问题(hard)](#26. 俄罗斯套娃信封问题(hard))
- [27. 可被三整除的最⼤和(medium)](#27. 可被三整除的最⼤和(medium))
- [28. 距离相等的条形码(medium)](#28. 距离相等的条形码(medium))
- [29. 重构字符串(medium)](#29. 重构字符串(medium))
- 整体源代码总结
贪心算法简介

1. 柠檬⽔找零(easy)
代码如下(示例):
c
class Solution {
public:
bool lemonadeChange(vector<int>& bills)
{
int five = 0, ten = 0;
for (auto x : bills)
{
if (x == 5) five++;
else if (x == 10)
{
if (five > 0) five--, ten++;
else return false;
}
else if (x == 20)
{
if (five && ten) five--, ten--;
else if (five >= 3) five -= 3;
else return false;
}
}
return true;
}
};
2. 将数组和减半的最少操作次数(medium)



代码如下(示例):
c
class Solution {
public:
int halveArray(vector<int>& nums)
{
priority_queue<double> heap;
double sum = 0.0;
for (auto x : nums)
{
heap.push(x);
sum += x;
}
sum /= 2.0;
int count = 0;
while (sum > 0)
{
double ret = heap.top() / 2.0;
sum -= ret;
heap.pop();
count++;
heap.push(ret);
}
return count;
}
};
3. 最⼤数(medium)



代码如下(示例):
c
class Solution
{
public:
string largestNumber(vector<int>& nums)
{
// 优化:把所有的数转化成字符串
vector<string> strs;
for (int x : nums) strs.push_back(to_string(x));
// 排序
sort(strs.begin(), strs.end(), [](const string& s1, const string& s2)
{
return s1 + s2 > s2 + s1;
});
// 提取结果
string ret;
for (auto& s : strs) ret += s;
if (ret[0] == '0') return "0";
return ret;
}
};
4. 摆动序列(medium)

代码如下(示例):
c
class Solution
{
public:
int wiggleMaxLength(vector<int>& nums)
{
int n = nums.size();
if (n < 2) return n;
int ret = 0, left = 0;
for (int i = 0; i < n - 1; i++)
{
int right = nums[i + 1] - nums[i]; // 计算接下来的趋势
if (right == 0) continue; // 如果⽔平,直接跳过
if (right * left <= 0) ret++; // 累加波峰或者波⾕
left = right;
}
return ret + 1;
}
};
5. 最⻓递增⼦序列(medium)



代码如下(示例):
c
class Solution {
public:
int lengthOfLIS(vector<int>& nums)
{
int n = nums.size();
vector<int> ret;
ret.push_back(nums[0]);
for (int i = 1; i < n; i++)
{
if (nums[i] > ret.back()) // 如果能接在最后⼀个元素后⾯,直接放
{
ret.push_back(nums[i]);
}
else
{
// ⼆分插⼊位置
int left = 0, right = ret.size() - 1;
while (left < right)
{
int mid = (left + right) >> 1;
if (ret[mid] < nums[i]) left = mid + 1;
else right = mid;
}
ret[left] = nums[i]; // 放在 left 位置上
}
}
return ret.size();
}
};
6. 递增的三元⼦序列(medium)



代码如下(示例):
c
class Solution {
public:
bool increasingTriplet(vector<int>& nums)
{
int n = nums.size();
int a = nums[0], b = INT_MAX;
for (int i = 1; i < n; i++)
{
if (nums[i] > b) return true;
else if (nums[i] > a) b = nums[i];
else a = nums[i];
}
return false;
}
};
7. 最⻓连续递增序列(easy)
解题思路:贪心+双指针

代码如下(示例):
c
class Solution {
public:
int findLengthOfLCIS(vector<int>& nums)
{
int ret = 0, n = nums.size();
for (int i = 0; i < n; )
{
int j = i + 1;
while (j < n && nums[j] > nums[j - 1]) j++;
ret = max(ret, j - i);
i = j;
}
return ret;
}
};
8. 买卖股票的最佳时机(easy)



代码如下(示例):
c
class Solution {
public:
int maxProfit(vector<int>& prices)
{
int ret = 0;
for (int i = 0, priceMin = INT_MAX; i < prices.size(); i++)
{
ret = max(ret, prices[i] - priceMin);
priceMin = min(priceMin, prices[i]);
}
return ret;
}
};
9. 买卖股票的最佳时机 Ⅱ(medium)
代码如下(示例):
c
class Solution
{
public:
int maxProfit(vector<int>& p)
{
// 实现⽅式⼀:双指针
int ret = 0, n = p.size();
for (int i = 0; i < n; i++)
{
int j = i;
while (j + 1 < n && p[j + 1] > p[j]) j++; // 找上升的末端
ret += p[j] - p[i];
i = j;
}
return ret;
}
};
class Solution
{
public:
int maxProfit(vector<int>& prices)
{
// 实现⽅式⼆:拆分成⼀天⼀天
int ret = 0;
for (int i = 1; i < prices.size(); i++)
{
if (prices[i] > prices[i - 1])
ret += prices[i] - prices[i - 1];
}
return ret;
}
};
10. K 次取反后最⼤化的数组和(easy)


代码如下(示例):
c
class Solution
{
public:
int largestSumAfterKNegations(vector<int>& nums, int k)
{
int m = 0, minElem = INT_MAX, n = nums.size();
for (auto x : nums)
{
if (x < 0) m++;
minElem = min(minElem, abs(x)); // 求绝对值最⼩的那个数
}
// 分类讨论
int ret = 0;
if (m > k)
{
sort(nums.begin(), nums.end());
for (int i = 0; i < k; i++) // 前 k ⼩个负数,变成正数
{
ret += -nums[i];
}
for (int i = k; i < n; i++) // 后⾯的数不变
{
ret += nums[i];
}
}
else
{
// 把所有的负数变成正数
for (auto x : nums) ret += abs(x);
if ((k - m) % 2) // 判断是否处理最⼩的正数
{
ret -= minElem * 2;
}
}
return ret;
}
};
11. 按⾝⾼排序(easy)



代码如下(示例):
c
class Solution
{
public:
vector<string> sortPeople(vector<string>& names, vector<int>& heights)
{
// 1. 创建⼀个下标数组
int n = names.size();
vector<int> index(n);
for (int i = 0; i < n; i++) index[i] = i;
// 2. 对下标进⾏排序
sort(index.begin(), index.end(), [&](int i, int j)
{
return heights[i] > heights[j];
});
// 3. 提取结果
vector<string> ret;
for (int i : index)
{
ret.push_back(names[i]);
}
return ret;
}
};
12. 优势洗牌(⽥忌赛⻢)(medium)

代码如下(示例):
c
class Solution
{
public:
vector<int> advantageCount(vector<int>& nums1, vector<int>& nums2)
{
int n = nums1.size();
// 1. 排序
sort(nums1.begin(), nums1.end());
vector<int> index2(n);
for (int i = 0; i < n; i++) index2[i] = i;
sort(index2.begin(), index2.end(), [&](int i, int j)
{
return nums2[i] < nums2[j];
});
// 2. ⽥忌赛⻢
vector<int> ret(n);
int left = 0, right = n - 1;
for (auto x : nums1)
{
if (x > nums2[index2[left]]) ret[index2[left++]] = x;
else ret[index2[right--]] = x;
}
return ret;
}
};
13. 最⻓回⽂串(easy)



代码如下(示例):
c
class Solution
{
public:
int longestPalindrome(string s)
{
// 1. 计数 - ⽤数组模拟哈希表
int hash[127] = { 0 };
for (char ch : s) hash[ch]++;
// 2. 统计结果
int ret = 0;
for (int x : hash)
{
ret += x / 2 * 2;
}
return ret < s.size() ? ret + 1 : ret;
}
};
14. 增减字符串匹配(easy)



代码如下(示例):
c
class Solution {
public:
vector<int> diStringMatch(string s)
{
int left = 0, right = s.size();
vector<int> ret;
for (auto ch : s)
{
if (ch == 'I') ret.push_back(left++);
else ret.push_back(right--);
}
ret.push_back(left);
return ret;
}
};
15. 分发饼⼲(easy)

代码如下(示例):
c
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s)
{
// 先排序
sort(g.begin(), g.end());
sort(s.begin(), s.end());
// 利⽤双指针找答案
int ret = 0, n = s.size();
for (int i = 0, j = 0; i < g.size() && j < n; i++, j++)
{
while (j < n && s[j] < g[i]) j++; // 找饼⼲
if (j < n) ret++;
}
return ret;
}
};
16. 最优除法(medium)
A一定在分子上,B一定在分母上!把后面所有的数都放在分子上即可~

代码如下(示例):
c
class Solution
{
public:
string optimalDivision(vector<int>& nums)
{
int n = nums.size();
// 先处理两个边界情况
if (n == 1)
{
return to_string(nums[0]);
}
if (n == 2)
{
return to_string(nums[0]) + "/" + to_string(nums[1]);
}
string ret = to_string(nums[0]) + "/(" + to_string(nums[1]);
for (int i = 2; i < n; i++)
{
ret += "/" + to_string(nums[i]);
}
ret += ")";
return ret;
}
};
17. 跳跃游戏 Ⅱ(medium)



代码如下(示例):
c
class Solution
{
public:
int jump(vector<int>& nums)
{
int left = 0, right = 0, maxPos = 0, ret = 0, n = nums.size();
while (left <= right) // 保险的写法,以防跳不到 n - 1 的位置
{
if (maxPos >= n - 1) // 先判断⼀下是否已经能跳到最后⼀个位置
{
return ret;
}
// 遍历当成层,更新下⼀层的最右端点
for (int i = left; i <= right; i++)
{
maxPos = max(maxPos, nums[i] + i);
}
left = right + 1;
right = maxPos;
ret++;
}
return -1; // 跳不到的情况
}
};
18. 跳跃游戏(medium)


代码如下(示例):
c
class Solution
{
public:
bool canJump(vector<int>& nums)
{
int left = 0, right = 0, maxPos = 0, n = nums.size();
while (left <= right)
{
if (maxPos >= n - 1)
{
return true;
}
for (int i = left; i <= right; i++)
{
maxPos = max(maxPos, nums[i] + i);
}
left = right + 1;
right = maxPos;
}
return false;
}
};
19. 加油站(medium)


代码如下(示例):
c
class Solution
{
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost)
{
int n = gas.size();
for (int i = 0; i < n; i++) // 依次枚举所有的起点
{
int rest = 0; // 标记⼀下净收益
int step = 0;
for (; step < n; step++) // 枚举向后⾛的步数
{
int index = (i + step) % n; // 求出⾛ step 步之后的下标
rest = rest + gas[index] - cost[index];
if (rest < 0) break;
}
if (rest >= 0) return i;
i = i + step; // 优化
}
return -1;
}
};
20. 单调递增的数字(medium)



代码如下(示例):
c
class Solution
{
public:
int monotoneIncreasingDigits(int n)
{
string s = to_string(n); // 把数字转化成字符串
int i = 0, m = s.size();
// 找第⼀个递减的位置
while (i + 1 < m && s[i] <= s[i + 1]) i++;
if (i + 1 == m) return n; // 判断⼀下特殊情况
// 回推
while (i - 1 >= 0 && s[i] == s[i - 1]) i--;
s[i]--;
for (int j = i + 1; j < m; j++) s[j] = '9';
return stoi(s);
}
};
21. 坏了的计算器(medium)

代码如下(示例):
c
class Solution {
public:
int brokenCalc(int startValue, int target)
{
int ret = 0;
while (startValue < target)
{
if (target % 2 == 0) target /= 2;
else target += 1;
ret++;
}
return ret + startValue - target;
}
};
22. 合并区间(medium)



代码如下(示例):
c
class Solution
{
public:
vector<vector<int>> merge(vector<vector<int>>& intervals)
{
// 1. 先按照左端点排序
sort(intervals.begin(), intervals.end());
// 2. 合并区间
int left = intervals[0][0], right = intervals[0][1];
vector<vector<int>> ret;
for (int i = 1; i < intervals.size(); i++)
{
int a = intervals[i][0], b = intervals[i][1];
if (a <= right) // 有重叠部分
{
// 合并 - 求并集
right = max(right, b);
}
else // 没有重叠部分
{
ret.push_back({ left, right }); // 加⼊到结果中
left = a;
right = b;
}
}
// 别忘了最后⼀个区间
ret.push_back({ left, right });
return ret;
}
};
23. ⽆重叠区间(medium)



代码如下(示例):
c
class Solution
{
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals)
{
// 1. 按照左端点排序
sort(intervals.begin(), intervals.end());
// 2. 移除区间
int ret = 0;
int left = intervals[0][0], right = intervals[0][1];
for (int i = 1; i < intervals.size(); i++)
{
int a = intervals[i][0], b = intervals[i][1];
if (a < right) // 有重叠部分
{
ret++; // 删掉⼀个区间
right = min(right, b);
}
else // 没有重叠部分
{
right = b;
}
}
return ret;
}
};
24. ⽤最少数量的箭引爆⽓球(medium)



代码如下(示例):
c
class Solution
{
public:
int findMinArrowShots(vector<vector<int>>& points)
{
// 1. 按照左端点排序
sort(points.begin(), points.end());
// 2. 求互相重叠区间的数量
int right = points[0][1];
int ret = 1;
for (int i = 1; i < points.size(); i++)
{
int a = points[i][0], b = points[i][1];
if (a <= right) // 有重叠部分
{
right = min(right, b);
}
else // ⽆重叠部分
{
ret++;
right = b;
}
}
return ret;
}
};
25. 整数替换(medium)



代码如下(示例):
c
// 解法一:
class Solution {
public:
long long integerReplacement(long long n)
{
return dfs(n);
}
long long dfs(long long n)
{
if (n == 1) return 0;
if (n % 2 == 0) return 1 + dfs(n / 2);
else return 1 + min(dfs(n + 1), dfs(n - 1));
}
};
// 解法二:
class Solution {
public:
int integerReplacement(int n) {
int ret = 0;
while (n > 1)
{
// 分类讨论
if (n % 2 == 0)
{
ret++;
n /= 2;
}
else
{
if (n == 3)
{
ret += 2;
n = 1;
}
else if (n % 4 == 1)
{
ret += 2;
n /= 2;
}
else
{
ret += 2;
n = n / 2 + 1;
}
}
}
return ret;
}
};
26. 俄罗斯套娃信封问题(hard)



代码如下(示例):
c
class Solution
{
public:
int maxEnvelopes(vector<vector<int>>& e)
{
// 解法⼀:动态规划
// 预处理
sort(e.begin(), e.end());
int n = e.size();
vector<int> dp(n, 1);
int ret = 1;
for (int i = 1; i < n; i++)
{
for (int j = 0; j < i; j++)
{
if (e[i][0] > e[j][0] && e[i][1] > e[j][1])
{
dp[i] = max(dp[i], dp[j] + 1);
}
}
ret = max(ret, dp[i]);
}
return ret;
}
};
class Solution
{
public:
int maxEnvelopes(vector<vector<int>>& e)
{
// 解法⼆:重写排序 + 贪⼼ + ⼆分
sort(e.begin(), e.end(), [&](const vector<int>& v1, const vector<int>&
v2)
{
return v1[0] != v2[0] ? v1[0] < v2[0] : v1[1] > v2[1];
});
// 贪⼼ + ⼆分
vector<int> ret;
ret.push_back(e[0][1]);
for (int i = 1; i < e.size(); i++)
{
int b = e[i][1];
if (b > ret.back())
{
ret.push_back(b);
}
else
{
int left = 0, right = ret.size() - 1;
while (left < right)
{
int mid = (left + right) / 2;
if (ret[mid] >= b) right = mid;
else left = mid + 1;
}
ret[left] = b;
}
}
return ret.size();
}
};
27. 可被三整除的最⼤和(medium)



代码如下(示例):
c
class Solution
{
public:
int maxSumDivThree(vector<int>& nums)
{
const int INF = 0x3f3f3f3f;
int sum = 0, x1 = INF, x2 = INF, y1 = INF, y2 = INF;
for (auto x : nums)
{
sum += x;
if (x % 3 == 1)
{
if (x < x1) x2 = x1, x1 = x;
else if (x < x2) x2 = x;
}
else if (x % 3 == 2)
{
if (x < y1) y2 = y1, y1 = x;
else if (x < y2) y2 = x;
}
}
// 分类讨论
if (sum % 3 == 0) return sum;
else if (sum % 3 == 1) return max(sum - x1, sum - y1 - y2);
else return max(sum - y1, sum - x1 - x2);
}
};
28. 距离相等的条形码(medium)


代码如下(示例):
c
class Solution
{
public:
vector<int> rearrangeBarcodes(vector<int>& b)
{
unordered_map<int, int> hash; // 统计每个数出现的频次
int maxVal = 0, maxCount = 0;
for (auto x : b)
{
if (maxCount < ++hash[x])
{
maxCount = hash[x];
maxVal = x;
}
}
int n = b.size();
vector<int> ret(n);
int index = 0;
// 先处理出现次数最多的那个数
for (int i = 0; i < maxCount; i++)
{
ret[index] = maxVal;
index += 2;
}
// 处理剩下的数
hash.erase(maxVal);
for (auto& [x, y] : hash)
{
for (int i = 0; i < y; i++)
{
if (index >= n) index = 1;
ret[index] = x;
index += 2;
}
}
return ret;
}
};
29. 重构字符串(medium)


代码如下(示例):
c
class Solution
{
public:
string reorganizeString(string s)
{
int hash[26] = { 0 };
char maxChar = ' ';
int maxCount = 0;
for(auto ch : s)
{
if(maxCount < ++hash[ch - 'a'])
{
maxChar = ch;
maxCount = hash[ch - 'a'];
}
}
// 先判断⼀下
int n = s.size();
if(maxCount > (n + 1) / 2) return "";
string ret(n, ' ');
int index = 0;
// 先处理出现次数最多的那个字符
for(int i = 0; i < maxCount; i++)
{
ret[index] = maxChar;
index += 2;
}
hash[maxChar - 'a'] = 0;
for(int i = 0; i < 26; i++)
{
for(int j = 0; j < hash[i]; j++)
{
if(index >= n) index = 1;
ret[index] = 'a' + i;
index += 2;
}
}
return ret;
}
};
整体源代码总结
代码如下(示例):
c
#include<iostream>
using namespace std;
#include<string>
#include<vector>
//class Solution {
//public:
// bool lemonadeChange(vector<int>& bills)
// {
// int five = 0, ten = 0;
// for (auto x : bills)
// {
// if (x == 5) five++;
// else if (x == 10)
// {
// if (five > 0) five--, ten++;
// else return false;
// }
// else if (x == 20)
// {
// if (five && ten) five--, ten--;
// else if (five >= 3) five -= 3;
// else return false;
// }
// }
// return true;
// }
//};
#include<queue>
//class Solution {
//public:
// int halveArray(vector<int>& nums)
// {
// priority_queue<double> heap;
// double sum = 0.0;
// for (auto x : nums)
// {
// heap.push(x);
// sum += x;
// }
// sum /= 2.0;
// int count = 0;
// while (sum > 0)
// {
// double ret = heap.top() / 2.0;
// sum -= ret;
// heap.pop();
// count++;
// heap.push(ret);
// }
// return count;
// }
//};
//class Solution
//{
//public:
// string largestNumber(vector<int>& nums)
// {
// // 优化:把所有的数转化成字符串
// vector<string> strs;
// for (int x : nums) strs.push_back(to_string(x));
// // 排序
// sort(strs.begin(), strs.end(), [](const string& s1, const string& s2)
// {
// return s1 + s2 > s2 + s1;
// });
// // 提取结果
// string ret;
// for (auto& s : strs) ret += s;
// if (ret[0] == '0') return "0";
// return ret;
// }
//};
//class Solution
//{
//public:
// int wiggleMaxLength(vector<int>& nums)
// {
// int n = nums.size();
// if (n < 2) return n;
// int ret = 0, left = 0;
// for (int i = 0; i < n - 1; i++)
// {
// int right = nums[i + 1] - nums[i]; // 计算接下来的趋势
// if (right == 0) continue; // 如果⽔平,直接跳过
// if (right * left <= 0) ret++; // 累加波峰或者波⾕
// left = right;
// }
// return ret + 1;
// }
//};
//class Solution {
//public:
// int lengthOfLIS(vector<int>& nums)
// {
// int n = nums.size();
// vector<int> ret;
// ret.push_back(nums[0]);
// for (int i = 1; i < n; i++)
// {
// if (nums[i] > ret.back()) // 如果能接在最后⼀个元素后⾯,直接放
// {
// ret.push_back(nums[i]);
// }
// else
// {
// // ⼆分插⼊位置
// int left = 0, right = ret.size() - 1;
// while (left < right)
// {
// int mid = (left + right) >> 1;
// if (ret[mid] < nums[i]) left = mid + 1;
// else right = mid;
// }
// ret[left] = nums[i]; // 放在 left 位置上
// }
// }
// return ret.size();
// }
//};
//class Solution {
//public:
// bool increasingTriplet(vector<int>& nums)
// {
// int n = nums.size();
// int a = nums[0], b = INT_MAX;
// for (int i = 1; i < n; i++)
// {
// if (nums[i] > b) return true;
// else if (nums[i] > a) b = nums[i];
// else a = nums[i];
// }
// return false;
// }
//};
//class Solution {
//public:
// int findLengthOfLCIS(vector<int>& nums)
// {
// int ret = 0, n = nums.size();
// for (int i = 0; i < n; )
// {
// int j = i + 1;
// while (j < n && nums[j] > nums[j - 1]) j++;
// ret = max(ret, j - i);
// i = j;
// }
// return ret;
// }
//};
//class Solution {
//public:
// int maxProfit(vector<int>& prices)
// {
// int ret = 0;
// for (int i = 0, priceMin = INT_MAX; i < prices.size(); i++)
// {
// ret = max(ret, prices[i] - priceMin);
// priceMin = min(priceMin, prices[i]);
// }
// return ret;
// }
//};
//class Solution
//{
//public:
// int maxProfit(vector<int>& p)
// {
// // 实现⽅式⼀:双指针
// int ret = 0, n = p.size();
// for (int i = 0; i < n; i++)
// {
// int j = i;
// while (j + 1 < n && p[j + 1] > p[j]) j++; // 找上升的末端
// ret += p[j] - p[i];
// i = j;
// }
// return ret;
// }
//};
//class Solution
//{
//public:
// int maxProfit(vector<int>& prices)
// {
// // 实现⽅式⼆:拆分成⼀天⼀天
// int ret = 0;
// for (int i = 1; i < prices.size(); i++)
// {
// if (prices[i] > prices[i - 1])
// ret += prices[i] - prices[i - 1];
// }
// return ret;
// }
//};
//class Solution
//{
//public:
// int largestSumAfterKNegations(vector<int>& nums, int k)
// {
// int m = 0, minElem = INT_MAX, n = nums.size();
// for (auto x : nums)
// {
// if (x < 0) m++;
// minElem = min(minElem, abs(x)); // 求绝对值最⼩的那个数
// }
// // 分类讨论
// int ret = 0;
// if (m > k)
// {
// sort(nums.begin(), nums.end());
// for (int i = 0; i < k; i++) // 前 k ⼩个负数,变成正数
// {
// ret += -nums[i];
// }
// for (int i = k; i < n; i++) // 后⾯的数不变
// {
// ret += nums[i];
// }
// }
// else
// {
// // 把所有的负数变成正数
// for (auto x : nums) ret += abs(x);
// if ((k - m) % 2) // 判断是否处理最⼩的正数
// {
// ret -= minElem * 2;
// }
// }
// return ret;
// }
//};
//class Solution
//{
//public:
// vector<string> sortPeople(vector<string>& names, vector<int>& heights)
// {
// // 1. 创建⼀个下标数组
// int n = names.size();
// vector<int> index(n);
// for (int i = 0; i < n; i++) index[i] = i;
// // 2. 对下标进⾏排序
// sort(index.begin(), index.end(), [&](int i, int j)
// {
// return heights[i] > heights[j];
// });
// // 3. 提取结果
// vector<string> ret;
// for (int i : index)
// {
// ret.push_back(names[i]);
// }
// return ret;
// }
//};
//class Solution
//{
//public:
// vector<int> advantageCount(vector<int>& nums1, vector<int>& nums2)
// {
// int n = nums1.size();
// // 1. 排序
// sort(nums1.begin(), nums1.end());
// vector<int> index2(n);
// for (int i = 0; i < n; i++) index2[i] = i;
// sort(index2.begin(), index2.end(), [&](int i, int j)
// {
// return nums2[i] < nums2[j];
// });
// // 2. ⽥忌赛⻢
// vector<int> ret(n);
// int left = 0, right = n - 1;
// for (auto x : nums1)
// {
// if (x > nums2[index2[left]]) ret[index2[left++]] = x;
// else ret[index2[right--]] = x;
// }
// return ret;
// }
//};
// LINUX
//struct shmid_ds {
// struct ipc_perm shm_perm; /* operation perms */
// int shm_segsz; /* size of segment
// (bytes) */
// __kernel_time_t shm_atime; /* last attach time
// */
// __kernel_time_t shm_dtime; /* last detach time
// */
// __kernel_time_t shm_ctime; /* last change time
// */
// __kernel_ipc_pid_t shm_cpid; /* pid of creator */
// __kernel_ipc_pid_t shm_lpid; /* pid of last operator */
// unsigned short shm_nattch; /* no. of current
// attaches */
// unsigned short shm_unused; /* compatibility */
// void shm_unused2; / ditto - used by
// DIPC * /
// void shm_unused3; / unused * /
//};
//class Solution
//{
//public:
// int longestPalindrome(string s)
// {
// // 1. 计数 - ⽤数组模拟哈希表
// int hash[127] = { 0 };
// for (char ch : s) hash[ch]++;
// // 2. 统计结果
// int ret = 0;
// for (int x : hash)
// {
// ret += x / 2 * 2;
// }
// return ret < s.size() ? ret + 1 : ret;
// }
//};
//class Solution {
//public:
// vector<int> diStringMatch(string s)
// {
// int left = 0, right = s.size();
// vector<int> ret;
// for (auto ch : s)
// {
// if (ch == 'I') ret.push_back(left++);
// else ret.push_back(right--);
// }
// ret.push_back(left);
// return ret;
// }
//};
//class Solution {
//public:
// int findContentChildren(vector<int>& g, vector<int>& s)
// {
// // 先排序
// sort(g.begin(), g.end());
// sort(s.begin(), s.end());
// // 利⽤双指针找答案
// int ret = 0, n = s.size();
// for (int i = 0, j = 0; i < g.size() && j < n; i++, j++)
// {
// while (j < n && s[j] < g[i]) j++; // 找饼⼲
// if (j < n) ret++;
// }
// return ret;
// }
//};
//class Solution
//{
//public:
// string optimalDivision(vector<int>& nums)
// {
// int n = nums.size();
// // 先处理两个边界情况
// if (n == 1)
// {
// return to_string(nums[0]);
// }
// if (n == 2)
// {
// return to_string(nums[0]) + "/" + to_string(nums[1]);
// }
// string ret = to_string(nums[0]) + "/(" + to_string(nums[1]);
// for (int i = 2; i < n; i++)
// {
// ret += "/" + to_string(nums[i]);
// }
// ret += ")";
// return ret;
// }
//};
//class Solution
//{
//public:
// int jump(vector<int>& nums)
// {
// int left = 0, right = 0, maxPos = 0, ret = 0, n = nums.size();
// while (left <= right) // 保险的写法,以防跳不到 n - 1 的位置
// {
// if (maxPos >= n - 1) // 先判断⼀下是否已经能跳到最后⼀个位置
// {
// return ret;
// }
// // 遍历当成层,更新下⼀层的最右端点
// for (int i = left; i <= right; i++)
// {
// maxPos = max(maxPos, nums[i] + i);
// }
// left = right + 1;
// right = maxPos;
// ret++;
// }
// return -1; // 跳不到的情况
// }
//};
//class Solution
//{
//public:
// bool canJump(vector<int>& nums)
// {
// int left = 0, right = 0, maxPos = 0, n = nums.size();
// while (left <= right)
// {
// if (maxPos >= n - 1)
// {
// return true;
// }
// for (int i = left; i <= right; i++)
// {
// maxPos = max(maxPos, nums[i] + i);
// }
// left = right + 1;
// right = maxPos;
// }
// return false;
// }
//};
//class Solution
//{
//public:
// int canCompleteCircuit(vector<int>& gas, vector<int>& cost)
// {
// int n = gas.size();
// for (int i = 0; i < n; i++) // 依次枚举所有的起点
// {
// int rest = 0; // 标记⼀下净收益
// int step = 0;
// for (; step < n; step++) // 枚举向后⾛的步数
// {
// int index = (i + step) % n; // 求出⾛ step 步之后的下标
// rest = rest + gas[index] - cost[index];
// if (rest < 0) break;
// }
// if (rest >= 0) return i;
// i = i + step; // 优化
// }
// return -1;
// }
//};
//class Solution
//{
//public:
// int monotoneIncreasingDigits(int n)
// {
// string s = to_string(n); // 把数字转化成字符串
// int i = 0, m = s.size();
// // 找第⼀个递减的位置
// while (i + 1 < m && s[i] <= s[i + 1]) i++;
// if (i + 1 == m) return n; // 判断⼀下特殊情况
// // 回推
// while (i - 1 >= 0 && s[i] == s[i - 1]) i--;
// s[i]--;
// for (int j = i + 1; j < m; j++) s[j] = '9';
// return stoi(s);
// }
//};
//class Solution {
//public:
// int brokenCalc(int startValue, int target)
// {
// int ret = 0;
// while (startValue < target)
// {
// if (target % 2 == 0) target /= 2;
// else target += 1;
// ret++;
// }
// return ret + startValue - target;
// }
//};
//class Solution
//{
//public:
// vector<vector<int>> merge(vector<vector<int>>& intervals)
// {
// // 1. 先按照左端点排序
// sort(intervals.begin(), intervals.end());
// // 2. 合并区间
// int left = intervals[0][0], right = intervals[0][1];
// vector<vector<int>> ret;
// for (int i = 1; i < intervals.size(); i++)
// {
// int a = intervals[i][0], b = intervals[i][1];
// if (a <= right) // 有重叠部分
// {
// // 合并 - 求并集
// right = max(right, b);
// }
// else // 没有重叠部分
// {
// ret.push_back({ left, right }); // 加⼊到结果中
// left = a;
// right = b;
// }
// }
// // 别忘了最后⼀个区间
// ret.push_back({ left, right });
// return ret;
// }
//};
//class Solution
//{
//public:
// int eraseOverlapIntervals(vector<vector<int>>& intervals)
// {
// // 1. 按照左端点排序
// sort(intervals.begin(), intervals.end());
// // 2. 移除区间
// int ret = 0;
// int left = intervals[0][0], right = intervals[0][1];
// for (int i = 1; i < intervals.size(); i++)
// {
// int a = intervals[i][0], b = intervals[i][1];
// if (a < right) // 有重叠部分
// {
// ret++; // 删掉⼀个区间
// right = min(right, b);
// }
// else // 没有重叠部分
// {
// right = b;
// }
// }
// return ret;
// }
//};
//class Solution
//{
//public:
// int findMinArrowShots(vector<vector<int>>& points)
// {
// // 1. 按照左端点排序
// sort(points.begin(), points.end());
// // 2. 求互相重叠区间的数量
// int right = points[0][1];
// int ret = 1;
// for (int i = 1; i < points.size(); i++)
// {
// int a = points[i][0], b = points[i][1];
// if (a <= right) // 有重叠部分
// {
// right = min(right, b);
// }
// else // ⽆重叠部分
// {
// ret++;
// right = b;
// }
// }
// return ret;
// }
//};
//class Solution {
//public:
// int integerReplacement(int n) {
//
// int ret = 0;
// while (n > 1)
// {
// // 分类讨论
// if (n % 2 == 0)
// {
// ret++;
// n /= 2;
// }
// else
// {
// if (n == 3)
// {
// ret += 2;
// n = 1;
// }
// else if (n % 4 == 1)
// {
// ret += 2;
// n /= 2;
// }
// else
// {
// ret += 2;
// n = n / 2 + 1;
// }
// }
// }
// return ret;
// }
//};
//// 解法一:
//class Solution {
//public:
// long long integerReplacement(long long n)
// {
// return dfs(n);
// }
// long long dfs(long long n)
// {
// if (n == 1) return 0;
// if (n % 2 == 0) return 1 + dfs(n / 2);
// else return 1 + min(dfs(n + 1), dfs(n - 1));
// }
//};
//class Solution
//{
//public:
// int maxEnvelopes(vector<vector<int>>& e)
// {
// // 解法⼀:动态规划
// // 预处理
// sort(e.begin(), e.end());
// int n = e.size();
// vector<int> dp(n, 1);
// int ret = 1;
// for (int i = 1; i < n; i++)
// {
// for (int j = 0; j < i; j++)
// {
// if (e[i][0] > e[j][0] && e[i][1] > e[j][1])
// {
// dp[i] = max(dp[i], dp[j] + 1);
// }
// }
// ret = max(ret, dp[i]);
// }
// return ret;
// }
//};
//
//
//class Solution
//{
//public:
// int maxEnvelopes(vector<vector<int>>& e)
// {
// // 解法⼆:重写排序 + 贪⼼ + ⼆分
// sort(e.begin(), e.end(), [&](const vector<int>& v1, const vector<int>&
// v2)
// {
// return v1[0] != v2[0] ? v1[0] < v2[0] : v1[1] > v2[1];
// });
// // 贪⼼ + ⼆分
// vector<int> ret;
// ret.push_back(e[0][1]);
// for (int i = 1; i < e.size(); i++)
// {
// int b = e[i][1];
// if (b > ret.back())
// {
// ret.push_back(b);
// }
// else
// {
// int left = 0, right = ret.size() - 1;
// while (left < right)
// {
// int mid = (left + right) / 2;
// if (ret[mid] >= b) right = mid;
// else left = mid + 1;
// }
// ret[left] = b;
// }
// }
// return ret.size();
// }
//};
//class Solution
//{
//public:
// int maxSumDivThree(vector<int>& nums)
// {
// const int INF = 0x3f3f3f3f;
// int sum = 0, x1 = INF, x2 = INF, y1 = INF, y2 = INF;
// for (auto x : nums)
// {
// sum += x;
// if (x % 3 == 1)
// {
// if (x < x1) x2 = x1, x1 = x;
// else if (x < x2) x2 = x;
// }
// else if (x % 3 == 2)
// {
// if (x < y1) y2 = y1, y1 = x;
// else if (x < y2) y2 = x;
// }
// }
// // 分类讨论
// if (sum % 3 == 0) return sum;
// else if (sum % 3 == 1) return max(sum - x1, sum - y1 - y2);
// else return max(sum - y1, sum - x1 - x2);
// }
//};
//class Solution
//{
//public:
// vector<int> rearrangeBarcodes(vector<int>& b)
// {
// unordered_map<int, int> hash; // 统计每个数出现的频次
// int maxVal = 0, maxCount = 0;
// for (auto x : b)
// {
// if (maxCount < ++hash[x])
// {
// maxCount = hash[x];
// maxVal = x;
// }
// }
// int n = b.size();
// vector<int> ret(n);
// int index = 0;
// // 先处理出现次数最多的那个数
// for (int i = 0; i < maxCount; i++)
// {
// ret[index] = maxVal;
// index += 2;
// }
// // 处理剩下的数
// hash.erase(maxVal);
// for (auto& [x, y] : hash)
// {
// for (int i = 0; i < y; i++)
// {
// if (index >= n) index = 1;
// ret[index] = x;
// index += 2;
// }
// }
// return ret;
// }
//};
//class Solution
//{
//public:
// string reorganizeString(string s)
// {
// int hash[26] = { 0 };
// char maxChar = ' ';
// int maxCount = 0;
// for (auto ch : s)
// {
// if (maxCount < ++hash[ch - 'a'])
// {
// maxChar = ch;
// maxCount = hash[ch - 'a'];
// }
// }
// // 先判断⼀下
// int n = s.size();
// if (maxCount > (n + 1) / 2) return "";
// string ret(n, ' ');
// int index = 0;
// // 先处理出现次数最多的那个字符
// for (int i = 0; i < maxCount; i++)
// {
// ret[index] = maxChar;
// index += 2;
// }
// hash[maxChar - 'a'] = 0;
// for (int i = 0; i < 26; i++)
// {
// for (int j = 0; j < hash[i]; j++)
// {
// if (index >= n) index = 1;
// ret[index] = 'a' + i;
// index += 2;
// }
// }
// return ret;
// }
//};