剑指---队列&栈篇(C++)

队列&栈篇

第一题: 用两个栈实现队列

在这里插入图片描述


题目解读:
假设输入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)】
  • 还有几种情况就不一一列出了。

解题思路:
根据题意, 我们可以用栈来模拟整个过程,

扫描二维码关注公众号,回复: 13285638 查看本文章
  • 首先同时用指针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中,接着压入ma,由于下一个数又是 ’ ’ ,这时,我们先弹出ares中,再弹出mres中,然后将 ’ ’ 压入到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++)

猜你喜欢

转载自blog.csdn.net/qq_40077565/article/details/121293314