个人总结,可读性有点差,各位只需要把这些都题自己去做做进行。
文章目录
1-leetcode-232-用栈实现队列
思路:每次进栈的时候将栈顶,通过辅助栈转换成队头。只需要在push的时候操作。
class MyQueue {
public:
/** Initialize your data structure here. */
stack<int> stk1;
stack<int> stk2;
MyQueue() {
//队列是先进先出,用两个栈,每次出队的时候先腾空其他的所有
}
/** Push element x to the back of queue. */
void push(int x) {
stk1.push(x);
}
/** Removes the element from in front of queue and returns that element. */
int pop() {
if (!stk1.empty()){
int n = stk1.size()-1; //需要移动的数量
for (int i = 0; i < n; i++){
int temp = stk1.top();
stk2.push(temp);
stk1.pop();
}
int res = stk1.top();
stk1.pop();
while (!stk2.empty()){
int temp = stk2.top();
stk2.pop();
stk1.push(temp);
}
return res;
}
return -1;
}
/** Get the front element. */
int peek() {
if (!stk1.empty()){
int n = stk1.size()-1; //需要移动的数量
for (int i = 0; i < n; i++){
int temp = stk1.top();
stk2.push(temp);
stk1.pop();
}
int peek = stk1.top();
stk1.pop();
stk2.push(peek);
while (!stk2.empty()){
int temp = stk2.top();
stk2.pop();
stk1.push(temp);
}
return peek;
}
return -1;
}
/** Returns whether the queue is empty. */
bool empty() {
return stk1.empty();
}
};
2-leetcode-225. 用队列实现栈
思路,push栈的时候,通过两个队列,将新进的栈顶元素放到队头
class MyStack {
public:
/** Initialize your data structure here. */
queue<int> qu1;
queue<int> help;
MyStack() {
//栈是后进先出,两个队列实现,在压栈的时候实现每次取出来压到队头做栈顶
// 两个队列,让队头作栈顶
}
/** Push element x onto stack. */
void push(int x) {
while (!qu1.empty()){
int top = qu1.front();
qu1.pop();
help.push(top);
}
qu1.push(x);
while (!help.empty()){
int top = help.front();
qu1.push(top);
help.pop();
}
}
/** Removes the element on top of the stack and returns that element. */
int pop() {
int p = qu1.front();
qu1.pop();
return p;
}
/** Get the top element. */
int top() {
return qu1.front();
}
/** Returns whether the stack is empty. */
bool empty() {
return qu1.empty();
}
};
3-leetcode-155-最小栈
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
思路,构建一个辅助栈,保存当前位置的最小值
构建的时候要初始化一个最小值
class MinStack {
public:
/** initialize your data structure here. */
stack<int> min_stk;
stack<int> data_stk;
MinStack() {
min_stk.push(INT_MAX); // 这里一定不能忘了
}
void push(int x) {
int now_min = min_stk.top();
if (x < now_min){
min_stk.push(x);
}
else {
min_stk.push(now_min);
}
data_stk.push(x);
}
void pop() {
if (!data_stk.empty()){
data_stk.pop();
min_stk.pop();
}
}
int top() {
if (!data_stk.empty()){
return data_stk.top();
}
return -1;
}
int getMin() {
if (!data_stk.empty()){
return min_stk.top();
}
return -1;
}
};
4括号相关题目
1-leetcode-20. 有效的括号(√写的没这个好)
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
思路:将最新的左括号存入栈中,如果遍历到右括号,判断和栈顶的左括号是否匹配
- 如果栈是空的 且收到了 } ] ) 就是错的
- 如果结束了栈还不是空的,说明错了
class Solution {
public:
bool isValid(string s) {
stack<char> stk;
if (s == "") return true;
for (auto & x : s){
if (x == '(' || x == '[' || x == '{'){
//压栈
stk.push(x);
}
else {
//对比并出栈
if (stk.empty()) return false;
char top = stk.top();
stk.pop();
bool one = (top == '(' && x == ')');
bool two = (top == '[' && x == ']');
bool three = (top == '{' && x == '}');
if ((one || two || three) != true){
return false;
}
}
}
return stk.empty();
}
};
2-leetcode-22-括号的生成(×)
错误原因:回溯的条件不是if else 而是,如果left > 0就可以一直选( 进行填充
if (left > 0){ //如果 (的个数 大于 )的个数,进行 '('的递归
helpfunc(left-1, right, path + '(');
}
if (left < right) {
helpfunc(left, right-1, path + ')');
}
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
类似于一个全排列问题但是又限制料件
- 右括号必须在左括号之后使用
- 类似于一个不记录的回溯,通过return回溯
class Solution {
public:
string path;
vector<string> res;
vector<string> generateParenthesis(int n) {
//递归实现,本来是自由组合,多了个什么条件让你想一想
int left_count = n;
int right_count = n;
dfs(path, left_count, right_count, 2 * n);
return res;
}
void dfs(string path, int left, int right){
// 终止条件,如果这个存储的结果等于2n可以保存
if (path.size() == size && left == 0 && right == 0){
res.push_back(path);
return;
}
if (left > 0){
dfs(path + '(', left-1, right);
}
if (left < right){ //左括号的可用次数必须比右括号少
dfs(path + ')', left, right-1);
}
}
};
3-leetcode-32-最长有小括号(×)
给定只包含 ‘(’ ')'的字符串,求可以组成的有效括号字符长度有多少
思路:
利用一个栈去存储左括号,当遇见右括号的时候就出栈,并用当前的下标索引减去当前栈顶的下标索引,这样可以得到以此结尾的能组成的长度。
为了保证 (()))不会出现问题,也就是说栈底必须存储)来告诉括号,之前的和他不能在生成额配对了,在这里(索引)停止。
为了防止()) 这里栈先插入一个-1
class Solution {
public:
int longestValidParentheses(string s) {
stack<int> stk;
int res = 0;
stk.push(-1); // basecase ())
for (int i = 0; i < s.size(); i++){
if (s[i] == '('){
stk.push(i);
}
else {
stk.pop();
if (!stk.empty()){
// 不为空就记录和更新最大值
int a = stk.top();
res = max(res, i - a);
}else {
//为空说明当前结尾的括号最多到这个 ) 处,记录这里的下标表示终止
stk.push(i);
}
}
}
return res;
}
};
4-leetcode-678有效的括号字符串(√)
给定一个只包含三种字符的字符串:( ,) 和 *,写一个函数来检验这个字符串是否为有效字符串。有效字符串具有如下规则:
任何左括号 ( 必须有相应的右括号 )。
任何右括号 ) 必须有相应的左括号 ( 。
左括号 ( 必须在对应的右括号之前 )。
- 可以被视为单个右括号 ) ,或单个左括号 ( ,或一个空字符串。
一个空字符串也被视为有效字符串。
思路:比较取巧,建立两个栈
- 存储左括号
- 存储星号
- )优先和左括号和星号判断
最后在判断左括号是否能够与将星号转换出来的 ) 进行配对
- 左括号的下标必须小于 * 的下标
- 结尾左括号的个数必须为0
class Solution {
public:
bool checkValidString(string s) {
stack<char> left;
stack<char> star;
for (int i = 0; i < s.size(); i++){
if (s[i] == '('){
left.push(i);
}
else if (s[i] == '*'){
star.push(i);
}
else if (s[i] == ')'){
if (!left.empty()){
left.pop();
}
else if(!star.empty()){
star.pop();
}
else {
return false;
}
}
}
//判断星号是不是可以
while(!left.empty() && !star.empty()){
if (left.top() > star.top()){
return false;
}
star.pop();
left.pop();
}
return left.empty();
}
};
5-leetcode-636-函数的独占时间(×)
错误原因;
可以直接按照括号操作来做,但是res要减去之前别的进程消耗的duration。“括号”的存储用pair,同时记录id和起始时间。
题目:
给出一个非抢占单线程CPU的 n 个函数运行日志,找到函数的独占时间。
每个函数都有一个唯一的 Id,从 0 到 n-1,函数可能会递归调用或者被其他函数调用。
日志是具有以下格式的字符串:function_id:start_or_end:timestamp。例如:“0:start:0” 表示函数 0 从 0 时刻开始运行。“0🔚0” 表示函数 0 在 0 时刻结束。
函数的独占时间定义是在该方法中花费的时间,调用其他函数花费的时间不算该函数的独占时间。你需要根据函数的 Id 有序地返回每个函数的独占时间。
思路:利用栈,因为是单线程,最先结束的一定是之前刚刚开始的,相当于一个括号问题。
- 所以如果遇到start就存入栈中,当遇到一个end时,对应的start一定是上栈顶对应的任务,可以计算出这个时间。出栈。
- 当计算完成一个duration时, 此时的新栈顶是没有占用当前的这段duration,这时,需要减去duration,因为在收到自己结束的命令时刻时,中间别的程序占用的都需要减去。
class Solution {
public:
vector<int> exclusiveTime(int n, vector<string>& logs) {
stack<pair<int, int>> st;
vector<int> res(n, 0);
for (auto & s: logs){
int pos1 = s.find(':');
//cout << s_last << endl;
int pos2 = s.find(':', pos1+1);
int id = stoi(s.substr(0, pos1));
int time = stoi(s.substr(pos2+1));
string state = s.substr(pos1+1, pos2-pos1-1);
cout << id << ' ' << time << ' ' << state <<endl;
if (state == "start"){
//压栈
st.push(make_pair(id, time));
}
else {
//如果这个时间点有结束的,肯定是栈顶的程序id结束的,可以直接计算出当前id的执行时间
//但是当前时间节点下 总共运行到时间点time,但是又duration多的时间是别人执行的,需要减去
int duration = time - st.top().second+1;
st.pop();
res[id] += duration;
if (!st.empty()){
res[st.top().first] -= duration;
}
}
}
return res;
}
};
另外这里需要再重视一下sting对应的一些库函数。
1 int pos1 = string.find(‘x’) //找到x第一次出现的下标
2 int pos2 = string.find(‘x’, po1+1); //找到x第二次出现的下标
3 string in = string.substr(pos1, pos2 - pos1 - 1);
// 找到x x 中间的字符串
4 int a = stoi(“123”); a == 123
// string to int = stoi;
6-leetcode-739-数组中元素与下一个比它大的元素距离(√单调栈)
请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。
例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。
思路:单调栈,将一个降序,比当前元素小的存入栈中,这样就不会遗漏,栈顶永远是当前的最小元素,当遇到比最小元素大的时候,取出来逐一比较,将其放置在res
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& T) {
// 题目本身的意思是找到第一个比当前数值大的数的下标,并存入数组中
//建立一个栈来存储,如果这个数没有大于任何一个数,那么就将他存入栈中
vector<int> res(T.size(),0);
stack<int> stk;
for (int i = 0; i < T.size(); i++){
while (!stk.empty() && T[i] > T[stk.top()]){
int index = stk.top();
res[index] = i - index;
stk.pop();
}
stk.push(i);
}
return res;
}
};
7-1-leetcode-下一个更大元素 I(√)
给定两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。找到 nums1 中每个元素在 nums2 中的下一个比其大的值。
nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1 。
思路:先用一个单调栈存储nums2中的每个小标对应的大的数据,在输入hash表,方便nums1的查找
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
//先用一个单调栈来存储
//找到nums2中比他大的第一个数
stack<int> stk;
vector<int> res(nums2.size(), -1);
for (int i = 0; i < nums2.size(); i++){
int num = nums2[i];
while (!stk.empty() && nums2[i] > nums2[stk.top()]){
res[stk.top()] = nums2[i];
stk.pop();
}
stk.push(i);
}
unordered_map<int, int> my_map;
for (int i = 0; i < nums2.size(); i++){
my_map[nums2[i]] = res[i];
}
vector<int> res2;
for (int i = 0; i < nums1.size(); i++){
res2.push_back(my_map.at(nums1[i]));
}
return res2;
}
};
7-2-leetcode-503-循环数组中比当前元素大的下一个元素(×)
错误原因:
- 如何处理循环数组下标的遍历? i % n
- 如果找到了一圈以后就不再压栈
和上一题不一样的是这是一个循环数组,循环数组遍历的方式不一样。而且如果之前更新过了,就不需要再更新了,只需要取出来就行。
两种方法处理循环队列:
- 如果这个结果已经被更新了,就不再更新
- 如果i > n 说明已经不需要将新遍历的递减数组存入栈中
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
//找到一个比当前值大的数,将这个数本身存入结果中
// 因为是循环,所以我们需要两次循环才行。
stack<int> stk;
int n = nums.size() ;
vector<int> res(nums.size(), -1);
for (int i = 0; i < n * 2; i++){
//当前的值
int num = nums[i % n];
while (!stk.empty() && num > nums[stk.top()]){
int index = stk.top();
stk.pop();
res[index] = num;
}
if (i < n){
stk.push(i);
}
//stk.push(i%n);
}
return res;
}
};
7-3-556下一个更大元素 III
给定一个32位正整数 n,你需要找到最小的32位整数,其与 n 中存在的位数完全相同,并且其值大于n。如果不存在这样的32位整数,则返回-1
1121 -> 1211
34765 -> 35467
思路:从后向前先找到不是递增的第一个点,index;
总这个index向后搜索,找到比他大的数里头最小的一个数。最后进行reverse
class Solution {
public:
int nextGreaterElement(int n) {
vector<int> nums;
while (n != 0){
nums.push_back((n % 10));
n /= 10;
}
reverse(nums.begin(),nums.end());
// 获得字符串
int index = -1;
for (int i = nums.size()-1; i >= 1; i--){
// 12321
if (nums[i-1] < nums[i]){
index = i-1;
break;
}
}
if (index == -1){
return -1;
}
// 找到比他大的数里 最小的数
int per = nums.size()-1;
for (int i = index+1; i < nums.size(); i++){
if (nums[i] <= nums[index]){
break;
}
per = i;
}
swap(nums, per, index);
//翻转index 之后的数
int res = str_reserve(nums, index);
return res;
}
int str_reserve(vector<int> nums, int start){
long long res = 0;
for (int i = 0; i <= start; i++){
res = res * 10 + nums[i];
}
for (int i = nums.size()-1; i > start; i--){
res = res * 10 + nums[i];
}
if (res > INT_MAX) return -1;
return res;
}
void swap(vector<int> &str, int i, int j){
int temp = str[j];
str[j] = str[i];
str[i] = temp;
}
};