队列&栈篇
第一题: 用两个栈实现队列
题目解读:
假设输入12345,由于要以队列的规则弹出,那么弹出后的返回值也是12345。
如果是以栈的规则,插入的时候是12345,弹出的时候就是54321。
那么,我们可以以这样的方式来:
- 假设是stack1专门用来存放插入的数据12345的栈
- 将stack1中弹出的54321插入到stack2中,再将stack2中的数据弹出。这样得到的就是12345。
注意: 弹出时,当 stack2 不为空,弹出 stack2 栈顶元素,如果 stack2 为空,将 stack1 中的全部数逐个出栈入栈 stack2,再弹出 stack2 栈顶元素
代码部分:
/答题模板
class Solution
{
public:
void push(int node) {
}
int pop() {
}
private:
stack<int> stack1;
stack<int> stack2;
};
class Solution
{
public:
void push(int node) {
stack1.push(node); //注意这个push一次只插入一个元素
}
int pop() {
//注意这个pop一次只弹出一个元素
if (stack2.empty()) {
//为空,那么将栈1元素加入栈2
while (!stack1.empty()) {
stack2.push(stack1.top());
stack1.pop();
}
}
int node = stack2.top();
stack2.pop();
return node;
}
private:
stack<int> stack1;
stack<int> stack2;
};
第二题: 包含min函数的栈
解题思路:
原本我自己想了一个方法,在给出的自测用例下可以运行,但在提交后的7个用例中只通过了两个,一下找不到原因,故后面还是采用官方的代码,自己写的代码也给出了。
官方的解题思路:
- 首先需要一个正常栈normal,用于栈的正常操作,然后需要一个辅助栈minval,专门用于获取最小值。
- 假设要输入的数为4231,先输入4。对于normal,此时normal.push(),normal中的数为【4】;对于minval,因为此时minval为空,那么直接minval.push()即可,minval中的数为【4】。
- 再输入2,对于normal,继续normal.push(),normal中的数为【4,2】;对于minval,将此时插入的数和minval的栈顶元素进行比较,如果此时插入的数更小,那么minval.push(),否则,插入一个和当前栈顶元素一样的数,以保持和normal栈的大小一致。由于2<栈顶元素4,因此此时minval中的数为【4,2】。
- 输入3,对于normal,继续normal.push(),normal中的数为【4,2,3】;对于minval,由于3>栈顶元素2,因此此时minval插入一个和当前栈顶元素一样的数,以保持和normal栈的大小一致,即minval中的数为【4,2,2】。
- 输入1,normal中的数为【4,2,3,1】;对于minval,由于1<栈顶元素2,因此minval.push(),minval中的数为【4,2,2,1】。
- 对于pop操作,当正常栈normal弹出一个元素时,minval也相应弹出一个元素,以保证两者的大小一致。
代码部分:
/答题模板
class Solution {
public:
void push(int value) {
}
void pop() {
}
int top() {
}
int min() {
}
};
/自己写的
class Solution {
public:
stack<int> stack1,stack2;
void push(int value) {
stack1.push(value);
}
void pop() {
stack1.pop();
}
int top() {
return stack1.top();
}
int min() {
int a=10000;
stack2=stack1;
while(!stack1.empty()){
if(a>stack1.top()){
a=stack1.top();
stack1.pop();
}
}
stack1=stack2;
return a;
}
};
/官方代码
class Solution {
public:
stack<int> normal, minval;
void push(int value) {
normal.push(value);
if (minval.empty()) {
//如果里面一开始没有元素就不需要比较
minval.push(value);
}
else {
//当前插入的元素需要和minval中的栈顶进行比较
if (value <= minval.top()) {
minval.push(value);
}
else {
minval.push(minval.top());
}
}
}
void pop() {
//弹出栈顶元素
normal.pop();
minval.pop();
}
int top() {
//返回栈顶元素
return normal.top();
}
int min() {
//返回minval的栈顶元素
return minval.top();
}
};
第三题: 栈的压入弹出序列
题目分析:
这题还是有点难度的。
首先栈的弹出序列是指在插入元素的途中我们可以弹出若干元素, 直到栈空为止, 因此一个压入序列可以对应多个弹出序列。
当入栈顺序为(1,2,3,4,5),我们可以有好几种弹出顺序,如以下所示:
- 弹出:5->4->3->2->1
【push(1)->push(2)->push(3)->push(4)->push(5)->pop(5)->pop(4)->pop(3)->pop(2)->pop(1)】 - 弹出:4->5->3->2->1
【push(1)->push(2)->push(3)->push(4)->pop(4)->push(5)->pop(5)->pop(3)->pop(2)->pop(1)】 - 弹出:3->4->5->2->1
【push(1)->push(2)->push(3)->pop(3)->push(4)->pop(4)->push(5)->pop(5)->pop(2)->pop(1)】 - 弹出:3->5->4->2->1
【push(1)->push(2)->push(3)->pop(3)->push(4)->push(5)->pop(5)->pop(4)->pop(2)->pop(1)】 - 还有几种情况就不一一列出了。
解题思路:
根据题意, 我们可以用栈来模拟整个过程,
- 首先同时用指针i指向pushV的第一个位置, 指针j指向popV的第一个位置。
- 每次我们先将指针i所指向的元素压入栈中, 然后i向后移动一步, 之后再检查当前栈顶, 若对应上了弹出序列中j所指向的元素, 则弹出元素, j向后移动, 再继续检查, 直到栈空或栈顶元素和j所指元素不等为止
- 若压入完所有的元素后指针j不等于序列长度或栈不为空, 说明不能成功对应弹出序列, 返回false, 否则返回true
代码部分:
/答题模板
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
}
};
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
int n = pushV.size(); //记录下输入数据的大小
stack<int> stk; // 使用STL中的栈容器
int i = 0, j = 0;
while(i < n){
stk.push(pushV[i++]); // 首先将pushV[i]入栈,然后i向后移动一位
while(!stk.empty() && stk.top() == popV[j]){
// 不断检查栈顶
stk.pop();
++j;
}
}
return n == j; // 判断这两者是否相等
}
};
第四题: 反转单词序列
解题思路:
方法一: 利用栈先入后出的特点
定义了一个栈,作为中转,它的作用是将输入的数反向后赋给我们要返回的字符串res,我们采用的是从后往前读取的思想,从前往后也可以。
以nowcoder. am l为例,从后往前先扫到l,入栈,由于下一个数是 ’ ’ ,因此我们先弹出l保存到res中,然后将 ’ ’ 压入到temp中,接着压入m和a,由于下一个数又是 ’ ’ ,这时,我们先弹出a到res中,再弹出m到res中,然后将 ’ ’ 压入到temp中,这样res中就完成了am l的反转存储。按照这种方式,最终能完成nowcoder. am l的反转。
大概的思路流程如下图所示:
方法二: 双指针
解题思路: 利用两个指针标识一个单词。
主要思路:
- 初始化:
left = str.length() - 1;right = str.length() - 1; string res="";
- 从尾往头遍历,使用left表示一个单词的起始位置,right表示一个单词的终止位置。
- 当遇到字符只有left向前移动,right不动,当遇到空格时,left不动,并直接返回left和right之间的数,即截取[left,right] 子串保存到res中。
res=res+str[left,right]+" "
,然后将right指向left的位置。 - 重复以上操作,直到left指到首元素。
代码部分:
/答题模板
class Solution {
public:
string ReverseSentence(string str) {
}
};
/方法一
class Solution {
public:
string ReverseSentence(string str) {
stack<char> temp; //用来暂存每个单词
string res=""; //用来存储最终结果
for(int i=str.length()-1;i>=0;i--){
//从后往前开始扫描
if(str[i]!=' ')//当遇到非空的字符时
temp.push(str[i]);//压栈
else{
//当遇到的空的字符串时
while(!temp.empty()){
//当栈非空时
res.push_back(temp.top());//依次把栈顶元素全部放入res中,并弹出
temp.pop();
}
res.push_back(' ');//因为遇到了空的字符串,所以while循环弹栈之后要把遇到的空字符压栈。
}
}
while(!temp.empty()){
//循环结束时由于最后一个字母不是空,所以最后一个单词里面的字母都没有弹出来,这里我们手动弹出
res.push_back(temp.top());//将最后一个单词加入到res中并弹出。
temp.pop();
}
return res;
}
};
/方法二
class Solution {
public:
string ReverseSentence(string str) {
int left = str.length() - 1;
int right = str.length() - 1;
string res="";
if (str.empty()){
//如果为空,那么直接返回空
return res;
}
else{
while (left > 0)
{
if(str[left]!=' ')--left;
else{
res = res + str.substr(left+1, right - left) + " ";
--left;
right = left;
}
}
res = res + str.substr(left, right - left+1);
return res;
}
}
};
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size) {
vector<int> res,res_max;
if(num.size()<size){
return res;
}
else{
int try_number=num.size()-size+1; //记录下滑动的次数
for(int i=0;i<try_number;i++){
for(int j=0;j<size;j++){
res_max.push_back(num[i+j]); //第i次插入size个元素
}
res.push_back(*max_element(res_max.begin(), res_max.end()));
res_max.clear();
}
return res;
}
}
};
第五题:滑动窗口的最大值
解题思路:
第一步: 计算出要滑动的次数,通过观察,我们可以发现滑动的次数try_number=num.size()-size+1;
第二步: 以下两种情况,直接返回 [ ]
- 窗口大小为0
- 窗口大小比输入的序列大小大
第三步: 从头到尾开始滑动,每次滑动时,把窗口中的数依次保存到数组1中,然后把数组1中最大的数返回到最后要输出的数组res中,再重置数组1,以便留给下一个窗口继续用。
代码部分:
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size) {
}
};
class Solution {
public:
vector<int> maxInWindows(const vector<int>& num, unsigned int size) {
vector<int> res,res_max;
if(num.size()<size||size<1){
return res;
}
else{
int try_number=num.size()-size+1; //记录下滑动的次数
for(int i=0;i<try_number;i++){
for(int j=0;j<size;j++){
res_max.push_back(num[i+j]); //第i次插入size个元素
}
res.push_back(*max_element(res_max.begin(), res_max.end()));
res_max.clear();
}
return res;
}
}
};
注: 以上题目都是在牛客网的剑指offer题库中刷的,大部分是自己做的,也有不会做看别人精华解题思路,然后总结的。如果侵犯了您的权益,请私聊我。
最后,觉得本文内容对你有所帮助的话,感谢点赞收藏!
导航链接:剑指—链表篇(C++)
导航链接:剑指—算法—动态规划篇(C++)