1.颜色分类
题目描述:0,1,2分别代表红色,白色,蓝色。给定一个数组是由这三种颜色组成
要求给这个数组排好序(红白蓝),不能使用库函数sort。
示例:
输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]
进阶:
一个直观的解决方案是使用计数排序的两趟扫描算法。
首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。
你能想出一个仅使用常数空间的一趟扫描算法吗?
思路:荷兰国旗问题
三个指针解决,一个指向最前面0的区域最左边,一个指向最后边2的区域的最左边,还有一个当前正在遍历的指针。
1.声明指针
2.只要当前指针不超过p2指针。
3.如果当前指针指向的元素为0,交换p0,并++
4.如果为2,交换p2,p2--就可以,当前指针不动。
5.其他情况(指向1),当前指针右移。
class Solution {
public:
void sortColors(vector& nums) {
int p0 = 0;
int cur = 0;
int p2 = nums.size()-1;
while(cur<=p2){
if(nums[cur] == 0) swap(nums[p0++],nums[cur++]);
else if(nums[cur] == 2) swap(nums[cur],nums[p2--]);
else cur++;
}
}
};
2.除法求值 (暂时放过)
题目描述:输入vector
示例 :
给定 a / b = 2.0, b / c = 3.0
问题: a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ?
返回 [6.0, 0.5, -1.0, 1.0, -1.0 ]
思路:并查集B站:[视频]小旭讲解 LeetCode 399. Evaluate Division 并查集,讲的并不好
dfs和bfs(1.build构造初始化图2.dfs或者bfs进行遍历)
题目描述:给定一个数组,第i个元素就表示在第i天股票的价格。设计一个算法算出最大利润,你可以多次买卖一只股票,但是不能同时参加多笔交易,买之前出售掉之前的股票。卖出股票后无法在第二天买入股票。冷冻期为1天。
示例:
输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
思路:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/solution/yi-ge-fang-fa-tuan-mie-6-dao-gu-piao-wen-ti-by-lab/
1种方法秒杀6到股票题。
根据上面的状态转移方程,秒杀题目;
第一题,k =1。直接套状态转移方程,根据base case,可做化简,因为k已经恒为1,所以k对状态转移已经没有影响。
dp[i][0] = max(dp[i-1][0],dp[i-1][1]+prices[i])
dp[i][1] = max(dp[i-1][1],-prices[i])
针对本题:
每次sell要等一天才能继续交易,融入状态方程。
dp[i][0] = max(dp[i-1][0],dp[i-1][1]+prices[i])
dp[i][1] = max(dp[i-1][1],dp[i-2][0]-prices[i])
解题步骤:
1.定义base case : int dp_i_0 = 0;int dp_i_1 = -price[0];int dp_p_0 = 0;
2.对每一天,写入状态转移方程
class Solution {
public:
int maxProfit(vector& prices) {
int n = prices.size();
if (0 == n) return 0;
int dp_i_0 = 0;
int dp_i_1 = -prices[0];
int dp_p_0 = 0;
int temp;
for (int i = 0; i < n; i++)
{
temp = dp_i_0;
dp_i_0 = max(dp_i_0, dp_i_1 + prices[i]);
dp_i_1 = max(dp_i_1, dp_p_0 - prices[i]);
dp_p_0 = temp;
}
return dp_i_0;
}
};
4.字符串解码
题目描述:给定一个字符串,k[string]表示string重复了k次,k保证为正整数,认为输入的字符串总是有效的。
示例:
s = "3[a]2[bc]", 返回 "aaabcbc".
s = "3[a2[c]]", 返回 "accaccacc".
s = "2[abc]3[cd]ef", 返回 "abcabccdcdcdef".
思路:看到括号匹配,首先就应该想到栈。制作一个类似于分配律的计算器。如3[a2[c]b] 使用一次分配律-> 3[accb] 再使用一次分配律->accbaccbaccb
2遍心得:1.用两个栈,一个存数字(重复的次数),另一个栈存已经遍历过的正确的字符串。
2.其实就是遍历字符串,每个字符有四种类型,每一种怎么处理写清楚就好了。
解题步骤:
1.针对s中的每一个元素
2.如果该元素是数字,更新num,num = num*10+s[i]-'0';
3.如果是字母,把它加到结果字符串后面;
4.如果当前元素是'[',把num压入栈nums中,num置0,把结果字符串压入strs栈,res置为""。
5.否则(只剩遇到']'了,操作与他匹配的‘[’中间的字符,使用分配律):定义次数为nums栈顶元素。nums出栈。
6.重复上一步求的次数,strs栈的栈顶元素+=结果字符串
7.结果字符串 = strs栈的栈顶元素。
8.strs出栈
class Solution {
public:
string decodeString(string s) {
int num = 0;
string res = "";
stack nums;
stack strs;
for(int i = 0;i='0'&&s[i]<='9') num = num*10+s[i]-'0';
else if((s[i]>='a'&&s[i]<='z')||(s[i]>='A'&&s[i]<='Z')) res += s[i];
else if(s[i] == '['){
nums.push(num);
num = 0;
strs.push(res);
res = "";
}
else{
int times = nums.top();
nums.pop();
for(int i = 0;i
5.任务调度器
题目描述:给定一个用字符数组表示对额cpu需要执行的任务列表,每个任务都可以在1个单位时间内执行完。
然而,两个相同的任务之间必须要有长度为n的冷却时间。因此至少又连续n个单位时间内cpu在执行不同的任务,或在待命状态。返回值是计算完成所有任务所需要的最短时间。
示例 1:
输入: tasks = ["A","A","A","B","B","B"], n = 2
输出: 8
执行顺序: A -> B -> (待命) -> A -> B -> (待命) -> A -> B.
注:
任务的总个数为 [1, 10000]。
n 的取值范围为 [0, 100]。
解题步骤:
1.计算每个任务出现的次数
2.找出出现次数最多的任务,假设出现次数为x
3.计算至少需要的时间(x-1)*(n+1),记为min_time....我觉得不应该叫至少需要的时间,这种叫法完全不对,这个时间是除了最后执行那次调度之外,全部时间之和。
4.计算出现次数为x的任务总数count,计算最终结果为min_time+count
特殊情况:如果返回的结果小于数组长度,返回数组长度。AAABBBCCDD
class Solution {
public:
struct com_by_value{
bool operator()(pair& a,pair& b){ return a.second>b.second;}
};
int leastInterval(vector& tasks, int n) {
map zyw;
for(int i = 0;i> paixu(zyw.begin(),zyw.end());
sort(paixu.begin(),paixu.end(),[](pair& a,pair& b){ return a.second>b.second;});
int k = paixu[0].second;
int count = 0;
for(auto ch:paixu){
if(k == ch.second) count++;
}
int res = count+(n+1)*(k-1);
return res>tasks.size()?res:tasks.size();
}
};
6.单词拆分 此题比较难
题目描述:给定一个非空字符串和一个非空单词列表的字典,判定s是否可以被空格拆分成一个或多个在字典中出现的单词。
拆分时可以重复使用字典中的单词。可以假设字典中没有重复的单词。
示例 1:
输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。
示例 2:
输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"。
注意你可以重复使用字典中的单词。
示例 3:
输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false
思路:字典树加速匹配速度,记忆化搜索减少重复计算
1.定义字典树节点数据结构,一个标志位,一个map,map
1.对字典中的每个元素,把root赋给新创建的node
2.对每个元素中的每个字符,如果node的next中遇到没有出现过的字符:那就node->next[c] = 新建TrieNode.
3.node指向node的next
4.把node的标志位置为true
5.返回func,里面三个参数,字符串,开始位置,结束位置。
6.如果开始等于结束,true
7.如果memo容器中start位置元素不为0,如果>0返回true,<0返回false
8.对start到end中的每个元素,如果node的next中s[i]元素没出现过,break
9.node指向下一个
10.如果node的标志位是true,并且递归i+1,end也为真,那就把memo[start]置为1,返回true
10.memo[start]置为-1,返回false
class Solution {
public:
struct TrieNode{
bool flag;
map next;
TrieNode():flag(false) {}
};
vector memo;
TrieNode* root;
bool func(string& s,int start,int end){
if(start == end) return true;
if(memo[start] != 0){ return memo[start]>0;}
auto node = root;
for(int i = start;inext.count(s[i]) == 0) break;
node = node->next[s[i]];
if(node->flag&&func(s,i+1,end)) {
memo[start] = 1;
return true;
}
}
memo[start] = -1;
return false;
}
bool wordBreak(string s, vector& wordDict) {
root= new TrieNode();
auto node = root;
memo = vector(s.size(),0);
//TrieNode* node = new TrieNode();
for(auto ch:wordDict){
node = root;
for(auto k:ch){
if(node->next.count(k) == 0) node->next[k] = new TrieNode();
node = node->next[k];
}
node->flag = true;
}
return func(s,0,s.size());
}
};