声明:若未特殊标出,则默认是leedcode原题。
3、2208. 将数组和减半的最少操作次数:
class Solution
{
public:
int halveArray(vector& nums)
{
priority_queue heap;
double sum = 0.0;
for(auto x : nums)
{
heap.push(x);
sum += x;
}
sum /= 2.0;
int count = 0;
while(sum > 0)
{
double t = heap.top() / 2.0;
heap.pop();
sum -= t;
count++;
heap.push(t);
}
return count;
}
};
4、179. 最大数:
class Solution
{
public:
string largestNumber(vector& nums)
{
//优化:将所有数按字典序排序
vector strs;
for(auto 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";
else return ret;
}
};
补充知识:全序关系满足:完全性,反对称性,传递性。
5、376. 摆动序列:
class Solution
{
public:
int wiggleMaxLength(vector& nums)
{
int n = nums.size(), len = 0, left = 0;
if(n < 2) return n;
for(int i = 0; i < n - 1; i++)
{
int right = nums[i + 1] - nums[i];
if(right == 0) continue;
if(0 >= left * right) len++;
left = right;
}
return len + 1;
}
};
6、300. 最长递增子序列:
①回顾dp的做法:
状态表示:dp[i]表示:以i的位置的元素为结尾的所有子序列中,最长递增子序列的长度。
状态转移方程:dp[i]=max(dp[j]+1)(j
②贪心优化:O(N^2)
存什么:所有长度为x的递增子序列中,最后一个元素的最小值。
存哪里:所有大于等于nums[i]的最小值的位置。
③二分优化:O(NlogN)
细节:边界情况。
class Solution
{
public:
int lengthOfLIS(vector& nums)
{
int n = nums.size();
vector 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];
}
}
return ret.size();
}
};
7、334. 递增的三元子序列:
法1:同上题。
法2:
class Solution
{
public:
bool increasingTriplet(vector& nums)
{
int a = nums[0], b = INT_MAX;
for(int i = 1; i < nums.size(); i++)
{
if(nums[i] > b) return true;
else if(nums[i] > a) b = nums[i];
else a = nums[i];
}
return false;
}
};
8、674. 最长连续递增序列:
解法:贪心+双指针。
class Solution
{
public:
int findLengthOfLCIS(vector& nums)
{
int ret = 1, 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;
}
};
9、121. 买卖股票的最佳时机:
class Solution
{
public:
int maxProfit(vector& prices)
{
int ret = 0;
for(int i = 0, prevmin = INT_MAX; i < prices.size(); i++)
{
ret = max(ret, prices[i] - prevmin);
prevmin = min(prevmin, prices[i]);
}
return ret;
}
};
10、122. 买卖股票的最佳时机 II:
class Solution
{
public:
int maxProfit(vector& p)
{
// 实现方式1:
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;
// // 实现方式2:拆分成一天一天
// int ret = 0;
// for(int i = 1; i < p.size(); i++)
// {
// if(p[i] > p[i - 1])
// ret += p[i] - p[i - 1];
// }
// return ret;
}
};
11、1005. K 次取反后最大化的数组和:
分情况讨论:设整个数组中负数的个数是m个: 12、2418. 按身高排序: 解法一:创建二元组: 13、870. 优势洗牌: 算法原理:田忌赛马: 14、409. 最长回文串: 15、942. 增减字符串匹配: 算法原理: 16、455. 分发饼干: 17、553. 最优除法: 算法原理: 18、45. 跳跃游戏 II: 20、134. 加油站: 算法原理: 21、738. 单调递增的数字: 解法一:暴力枚举 O(NlogN) 解法二:贪心(找规律) O(N) 22、991. 坏了的计算器: 解法一:正向推导。 解法二:正难则反(没有小数) 23、56. 合并区间: 解法: 24、435. 无重叠区间: 解法:排序(左端点)+贪心策略。 25、452. 用最少数量的箭引爆气球: 解法: 26、397. 整数替换: 解法一:模拟(递归+记忆化搜索)。 解法二:贪心 27、354. 俄罗斯套娃信封问题: 解法一:动态规划:(超时): 解法二:重写排序+贪心+二分: 28、1262. 可被三整除的最大和: 解法一:动态规划。 解法二:正难则反+贪心+分类讨论: 29、1054. 距离相等的条形码: 解法:贪心+模拟 O(N) 30、767. 重构字符串: 解法:贪心+模拟 O(N)
①m>k:把前k小负数,转化为正数。
②m==k:把所有的负数全部转化成正数。
③mclass Solution
{
public:
int largestSumAfterKNegations(vector
①创建一个数组pair
②对新的数组排序;
③按照顺序把名字提取出来即可。
解法二:利用哈希表存下映射关系
①先用哈希表存下映射关系<身高,名字>;
②对身高数组排序;
③根据排序后的结果,往哈希表里找名字即可。
解法三:对下标排序:
①创建一个下标数组;
②仅需对下标数组排序;
③根据下标数组排序后的结果,找到原数组的信息。class Solution
{
public:
vector
①如果比不过,就去拖累对面最强的那一个;
②如果能比过,那就直接比。class Solution
{
public:
vector
class Solution
{
public:
int longestPalindrome(string s)
{
// 1、计数 - 用数组模拟哈希表
int hash[127] = { 0 };
for(auto ch : s) hash[ch]++;
// 2、统计结果
int ret = 0;
for(auto x : hash) ret += x / 2 * 2;
return ret == s.size() ? ret : ret + 1;
}
};
①当遇到“I”:选择当前最小的那个数;
②当遇到“D”:选择当前最大的那个数。class Solution
{
public:
vector
class Solution
{
public:
int findContentChildren(vector
解法一:暴力解法->递归->记忆化搜索->动态规划;
解法二:贪心:除了前两个数以外,其余的数全放在分子上即可。class Solution
{
public:
string optimalDivision(vector
class Solution
{
public:
int jump(vector
解法一:暴力解法->枚举:
①依次枚举所有的起点;
②从起点开始,模拟一遍加油的流程即可。
解法二:优化->找规律(贪心):O(N)class Solution
{
public:
int canCompleteCircuit(vector
①从大到小的顺序,枚举[n, 0]区间内的数字;
②判断数字是否是“单调递增的”。
①如果高位单调递增的话,我们不去修改;
②从左往右,找到第一个递减的位置,从这个位置向前推,推到相同区域的最左端,使其减小1,后面的数全部修改成9。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 - 1] == s[i]) i--;
s[i]--;
for(int j = i + 1; j < m; j++) s[j] = '9';
return stoi(s);
}
};
①end<=begin:begin-end次+1操作;
②end>begin:奇数时只能+1,偶数时可+1可除2(除法更优)。class Solution
{
public:
int brokenCalc(int startValue, int target)
{
// 正难则反 + 贪心
int ret = 0;
while(target > startValue)
{
if(target % 2 == 0) target /= 2;
else target += 1;
ret++;
}
return ret + startValue - target;
}
};
①先按照左端点排序(能够合并的区间都是连续的);
②如何合并?求并集。class Solution
{
public:
vector
①按照左端点排序;
②移除最少区间<==>保留更多区间。class Solution
{
public:
int eraseOverlapIntervals(vector
①先按照左端点排序(能够重叠的区间都是连续的);
②提出贪心策略:一支箭应该引爆更多气球->将互相重叠的所有区间都拿出来引爆。
如何求出互相重叠的区间?求交集。class Solution
{
public:
int findMinArrowShots(vector
class Solution
{
unordered_map
补充:二进制
①偶数:二进制表示中最后一位0;
②奇数:二进制表示中最后一位1;
③/2操作:二进制表示中统一右移一位。class Solution
{
unordered_map
乱序->有序->按照左端点排序->最长递增子序列。
①状态表示:dp[i]表示:以i位置的信封为结尾的所有的套娃序列中,最长的套娃序列的长度。
②状态转移方程:max(dp[j]+1)
0<=je[j][0]&&e[i][1]>e[j][1]
③初始化:全初始化为1。
④填表顺序:从左往右。
⑤返回值:dp表里的最大值class Solution
{
public:
int maxEnvelopes(vector
①重写排序
当左端点不同的时候:左端点从小到大排序。
当左端点相同的时候:右端点从大到小排序。
②最长递增子序列。class Solution
{
public:
int maxEnvelopes(vector
先把所有的数累加在一起->根据累加和,删除一些数。
①sum%3=0,不删:
(x:标记%3=1的尽可能小的数,y:标记%3=2的尽可能小的数)
②sum%3=1,max(sum-x1,sum-y1-y2)
③sum%3=2,max(sum-y1,sum-x1-x2)class Solution
{
public:
int maxSumDivThree(vector
①每次处理一批相同的数。摆放的时候,每次隔一个格子。
②先处理出现次数最多的那个数,剩下的数顺序无所谓。class Solution
{
public:
vector
①每次处理一批相同的字符。摆放的时候,每次隔一个格子。
②先处理出现次数最多的那个字符,剩下的数顺序无所谓。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;
}
};