NC115 栈和排序
中等
通过率:35.86%
时间限制:1秒
空间限制:256M
知识点:栈、排序
描述
给你一个 1 到 n 的排列和一个栈,并按照排列顺序入栈
你要在不打乱入栈顺序的情况下,仅利用入栈和出栈两种操作,输出字典序最大的出栈序列
排列:指 1 到 n 每个数字出现且仅出现一次
数据范围: 1≤n≤5×104,排列中的值都满足 1≤val≤n
进阶:空间复杂度 O(n) ,时间复杂度 O(n)
示例1
输入:
[2,1,5,3,4]
返回值:
[5,4,3,1,2]
说明:
操作 栈 结果
2 入栈;[2] []
1 入栈;[2\1] []
5 入栈;[2\1\5] []
5 出栈;[2\1] [5]
3 入栈;[2\1\3] [5]
4 入栈;[2\1\3\4] [5]
4 出栈;[2\1\3] [5,4]
3 出栈;[2\1] [5,4,3]
1 出栈;[2] [5,4,3,1]
2 出栈;[] [5,4,3,1,2]
示例2
输入:
[1,2,3,4,5]
返回值:
[5,4,3,2,1]
思路一:(注释+打印细节版)
cpp
#include <stack>
#include <vector>
class Solution
{
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 栈排序
* @param a int整型vector 描述入栈顺序
* @return int整型vector
*/
vector<int> solve(vector<int>& a)
{
int *bit = new int[a.size()+1](); // 这里的默认值都是0吗???
vector<int> v;
stack<int> s;
int max = a.size(); // 此时未出栈的最大值(已知数是1-n,并且每个数字出现且仅出现一次) // 1-n 有n个数,所以a里的所有值个数就是n
for(const auto&e: a)
{
cout << "我要准备插入栈(还没有插入)" << e << endl;
// 如果等于所有数的最大值,就连栈也不入,直接放到返回的vector里
if(max == e)
{
cout << "发现" << e <<"是最大值,不插入栈,直接放入队列"<< endl;
v.push_back(e);
cout << "往v里插入"<<e <<endl;
max--;
cout<< "最大值更新为" <<max<<endl;
continue;
}
// 走到这里就说明此时准备插入的数据不是剩余数的最大值,
// 但是先得判断此时栈顶元素是不是最大的,是最大的就哟先让最大的出栈
// 这时还得考虑到出完栈的栈顶是不是还是最大的,也要判断
// 栈内此时也可能没有元素,所以也需要判断一下
if(s.size() != 0)
{
cout << e<<"不是最大值,要先判断栈顶是不是最大值"<< endl;
while(s.size()!=0 &&s.top() == max) // 直到栈顶不是最大值,才结束循环
{
cout << "栈顶是最大值"<<s.top()<< "进入队列并出栈"<< endl;
v.push_back(s.top()); // 如果此时栈顶是最大值,就放到vector里
cout << "往v里插入"<<s.top() <<endl;
bit[s.top()] = 0; // 出栈就置0
s.pop(); // 然后删除掉栈顶
max--; // 要记得最大值--
cout<< "最大值更新为" <<max<<endl;
}
// 可以出的最大 值都出完了,接下来看看此时的最大值在前面出现过没有,出现过,就出不了,只能优先出比他小一位的,如果小一位的还在前面,就在匹配更小的
cout << "现在v里有的:";
for(auto e:v)
{
cout << e << " ";
}
cout <<endl;
// cout << "为什么不进去"<< "因为不是超找a,而死栈里的" <<endl;
while(bit[max]) // 这里判断能不能找到前面是否出现过此时的max,存在了就只能--
{
max--;
cout << "max--"<< " -> "<< max<<endl;
// 在减的过程中,可能栈顶的值刚好相等,这是就要把栈顶的值出栈,出了栈必须置0,并且max--,逻辑要清晰,一步错就炸了
if(s.top()==max)
{
cout<< "可以出栈的最大值刚好又是栈顶"<<max<<endl;
v.push_back(s.top());
cout <<s.top() <<"出栈,入队列";
s.pop();
bit[max] = 0;
max--;
cout<< "最大值更新为" <<max<<endl;
}
}
// 如果减完刚好又是栈顶是max,下次循环会判断到的
}
// 现在再思考一下,有没有可能删除完栈顶最大的元素,会不会现在要插入的正好就是最大值,理一下逻辑,假设此时的最大值插入了进去,下一次循环,是不是插入的肯定不是最大值,但是会先进行栈顶元素是不是最大值的判断,就完美实现逻辑,所以这里直接插入即可。
// 插入
cout << e<< "插入栈(正式插入了)" << endl;
bit[e] = 1; // 谁进入了栈就置1,到时候出栈也得置0
s.push(e);
}
cout << "走到这里了"<<endl;
while(s.size())
{
cout << s.top() << "入队列并出栈"<<endl;
v.push_back(s.top());
s.pop();
}
return v;
}
};
// 现在的问题 次大值在非常靠前,拿不出来,现在就得考虑把次次大值拿出来
无注释版:
cpp
#include <stack>
#include <vector>
class Solution
{
public:
vector<int> solve(vector<int>& a)
{
int *bit = new int[a.size()+1]();
vector<int> v;
stack<int> s;
int max = a.size();
for(const auto&e: a)
{
if(max == e)
{
v.push_back(e);
max--;
continue;
}
if(s.size() != 0)
{
while(s.size()!=0 &&s.top() == max)
{
v.push_back(s.top());
bit[s.top()] = 0;
s.pop();
max--;
}
while(bit[max])
{
max--;
if(s.top()==max)
{
v.push_back(s.top());
s.pop();
bit[max] = 0;
max--;
}
}
}
bit[e] = 1;
s.push(e);
}
while(s.size())
{
v.push_back(s.top());
s.pop();
}
return v;
}
};
突然想起,也能维护个大堆去同步维护栈里已有的元素,来代替bit数组。但是效率不如bit数组高。总之也是一种方案吧。
思路二(注释+打印细节版):
cpp
#include <stack>
#include <vector>
class Solution
{
public:
vector<int> solve(vector<int>& a)
{
bool hash[50010] = {false};
vector<int> v;
stack<int> s;
int max = a.size();
for(auto e : a)
{
// 入栈
s.push(e); cout << e <<"入栈了"<<endl;
hash[e] = true;
// 先更新要出栈的最大值
while(hash[max]) // 这里就是现在剩余元素的最大值,已经出现过了,就没必要往后再找了,优先让最大值出栈 最大值出栈越早,那么他所在的位置越靠前,值就越大
{
cout <<"更新最大值"<< max<<"->";
max--;
cout <<max<<endl;
}
cout << "出比"<<max<<"大等的值"<<endl;
// 出栈
while(s.size() && s.top()>=max)
{
cout << s.top() <<"出栈"<<endl;
v.push_back(s.top());
s.pop();
}
}
return v;
}
};
无注释版:
cpp
#include <stack>
#include <vector>
class Solution
{
public:
vector<int> solve(vector<int>& a)
{
bool hash[50010] = {false};
vector<int> v;
stack<int> s;
int max = a.size();
for(auto e : a)
{
s.push(e);
hash[e] = true;
while(hash[max])
{
max--;
}
while(s.size() && s.top()>=max)
{
v.push_back(s.top());
s.pop();
}
}
return v;
}
};
结语:思路一可以锻炼代码逻辑控制能力,思路一做出来对思路二的理解也容易了许多,优先选思路二~
思路一编写过程中,通过每次错误示例去分析哪里没有思考全面,去思考哪里的逻辑有疏漏,对题的理解是否到位,逐渐完善得到最终代码。