• 1.浏览器的返回功能可以用栈来实现,当前浏览的页面我们叫它为栈顶元素,跳转到一个新页面我们叫元素入栈,点击“返回”按钮我们叫元素出栈,当然出栈前提是栈里要有元素,比如在浏览器里,如果已经返回到了最开始的页面,那就无法返回了。栈有一个重要的性质,就是先进后出,First In Last Out(FILO)。

    什么是先进后出?在浏览器的例子里,先打开的页面的一定是到后面才会被返回到,后面打开的页面一定是先被返回到的。这个过程,我们可以用栈这种数据结构来模拟实现。

    2.栈的性质:

      新元素插入栈顶(新打开的页面会加到栈顶)

      栈顶元素先出(最后打开的页面会先被返回到)

      栈为空不可以出栈(在最开始的页面不可以返回上一级)

    3.入栈,出栈,访问栈顶元素等操作

     1 #include<iostream>
     2 #include<string>
     3 using namespace std;
     4 template<class Type>class Stack{
     5   private:               
     6     Type *urls;                  //定义一个Type类型的指针变量,用来存储浏览过网页的地址
     7     int max_size,top_index;    //栈里最多可以有的元素个数和栈顶元素的位置
     8   public:
     9     Stack(int length_input){        //构造函数
    10         urls=new Type[length_input];
    11         max_size=length_input;
    12         top_index=-1;               //栈顶元素初始化为-1
    13     }
    14     ~Stack(){                      //析构函数
    15         delete[]urls;
    16     }
    17     bool push(const Type &element){     //入栈操作,为节省内存,在参数名前加一个引用,在参数类型前加一个const
    18         if(top_index>=max_size-1){      //特殊情况,栈顶元素的位置已经大于栈的容量
    19             return false;
    20         }
    21         top_index++;                   //普通情况,栈顶位置先加一,在把元素插入栈顶
    22         urls[top_index]=element;
    23         return true;
    24     }
    25    bool pop(){                        //出栈操作
    26         if(top_index<0){               //栈为空,不能出栈
    27             return false;
    28         }
    29         top_index--;                   //否则栈顶元素位置向前移动一位,表示出栈
    30         return true;
    31     }
    32    Type top(){                   //访问栈顶元素
    33         assert(top_index>=0);     //宏断言,判断栈是否为空,为空则结束程序
    34         return urls[top_index];   //不为空则返回栈顶元素
    35     }
    36    bool empty(){              //判断栈是否为空
    37         if(top_index<0){
    38             return true;
    39         }
    40         else{
    41             return false;
    42         }
    43     }
    44 };
    45    int main() {
    46     int n,num;
    47     cin>>n;
    48     Stack<int> stack(n);
    49     for(int i=1;i<=n;i++){
    50         cin>>num;
    51         stack.push(num);
    52     }
    53     while(!stack.empty()){           //实现数列的翻转
    54         cout<<stack.top()<<" ";
    55         stack.pop();
    56     }
    57     return 0;
    58 }

    4.栈的应用:表达式的求值

     1 #include<iostream>
     2 #include<string>
     3 #include<cassert>
     4 using namespace std;
     5 template<class Type> class Stack {
     6 private:
     7     Type *urls;
     8     int max_size, top_index;
     9 public:
    10     Stack(int length_input) {
    11         urls = new Type[length_input];
    12         max_size = length_input;
    13         top_index = -1;
    14     }
    15     ~Stack() {
    16         delete[] urls;
    17     }
    18     bool push(const Type &element) {
    19         if (top_index >= max_size - 1) {
    20             return false;
    21         }
    22         top_index++;
    23         urls[top_index] = element;
    24         return true;
    25     }
    26     bool pop() {
    27         if (top_index < 0) {
    28             return false;
    29         }
    30         top_index--;
    31         return true;
    32     }
    33     Type top() {
    34         assert(top_index >= 0);
    35         return urls[top_index];
    36     }
    37     bool empty() {
    38         if (top_index < 0) {
    39             return true;
    40         } else {
    41             return false;
    42         }
    43     }
    44 };
    45 bool precede(char a, char b) {                          //判断两个运算符a和b的优先级,如果a的优先级高则返回true
    46     if (a == '*') {
    47         return true;
    48     } else {
    49         return false;
    50     }
    51 }
    52 int operate(char theta, int a, int b) {                  //根据运算符theta计算两个数a和b的值,并返回计算结果
    53     if (theta == '+') {
    54         return a + b;
    55     } else {
    56         return a * b;
    57     }
    58 }
    59 void calc(Stack<int> &numbers, Stack<char> &operators) {                      //根据运算符栈顶的运算符,计算数字栈顶的两个数的结果,并把结果加入到数字栈里
    60     int a=numbers.top();
    61     numbers.pop();
    62     int b=numbers.top();
    63     numbers.pop();
    64     numbers.push(operate(operators.top(),a,b));
    65     operators.pop();
    66 }
    67 int main() {
    68     int n;                                                                        //输入的表达式长度
    69     cin>>n;
    70     Stack<int> numbers(n);
    71     Stack<char> operators(n);
    72     string buffer;                                                                //输入的表达式
    73     cin>>buffer;
    74     int i=0;
    75     while(i<n){
    76         if(isdigit(buffer[i])){                                                  //是数字则化成int型后加入到数字栈
    77             numbers.push(buffer[i]-'0');
    78             i++;
    79         }
    80         else{                                                                      //是运算符的情况
    81             if(operators.empty()||precede(buffer[i],operators.top())){             //运算符栈为空或当前运算符的优先级大于栈顶的符号的优先级
    82                 operators.push(buffer[i]);
    83                 i++;
    84             }
    85             else{
    86                 calc(numbers,operators);
    87             }
    88         }
    89         
    90     }
    91     while(!operators.empty()){
    92         calc(numbers,operators);
    93     }
    94     cout<<numbers.top()<<endl;
    95     return 0;
    96 }

    5.单调栈

           单调栈就是栈内元素单调递增或者单调递减的栈,这一点和单调队列很相似,但是单调栈只能在栈顶操作。

    借用拿号排队的场景来说明下。现在有很多人在排队买蒜味可乐,每个人手里都拿着号,越靠前的人手里的号越小,但是号不一定是连续的。一个人拿了号后并没有去排队,而是跑去约会了。等他回来后,发现队伍已经排得很长了,他不能直接插入到队伍里,不然人家以为他是来插队的。他只能跑到队伍最后,挨个询问排队人手里的号,蒜头君认为号比他大的人都是“插队”的,蒜头君就会施魔法把这些人变消失,直到蒜头君找到号比他小的为止。

    在上面这个场景里,大家排的队伍就像是单调栈,因为大家手里拿的号是单调递增的。蒜头君找自己位置的这个过程就是元素加入单调栈的过程。新加入的元素如果加到栈顶后,栈里的元素不再是单调递增了,那么我们就删除加入前的栈顶元素,就像施魔法把“插队”的人变消失一样。直到新元素加入后,栈依然是单调递增时,我们才把元素加进栈里。

    给定一个包含若干个整数的数组,我们从第 1 个元素开始依次加入单调栈里,并且加入后更新单调栈。那么单调栈有这样的性质:对于单调递增的栈,如果此时栈顶元素为 b,加入新元素 a 后进行更新时,如果 a 大于 b,说明 a 在数组里不能再往左扩展了,也就是说,如果从 a 在数组中的位置开始往左边遍历,则 b 一定是第一个比 a 大的元素;如果 a 小于 b,说明在数组里,a 前面至少有一个元素不能扩展到 a 的位置,也就是对于这些元素来说,a 是其在数组右侧第一个比它小的元素。

    单调栈的维护是 O(n) 级的时间复杂度,因为所有元素只会进入栈一次,并且出栈后再也不会进栈了。

          单调栈的性质:

    • 使用单调栈可以找到元素向左遍历第一个比他大的元素
    • 元素加入栈前,会在栈顶端把破坏栈单调性的元素都删除
    • 单调栈里的元素具有单调性

         eg:我们来看看这样一道题:地上从左到右竖立着 n 块木板,从 1 到 n 依次编号,如下图所示。我们知道每块木板的高度,在第 n 块木板右侧竖立着一块高度无限大的木板,现对每块木板依次做如下的操作:对于第 i 块木板,我们从其右侧开始倒水,直到水的高度等于第 i 块木板的高度,倒入的水会淹没 ai 块木板(如果木板左右两侧水的高度大于等于木板高度即视为木板被淹没),求 n 次操作后,所有 ai 的和是多少。如图上所示,在第 4 块木板右侧倒水,可以淹没第 5 块和第 6 块一共 2 块木板,a4 = 2。

            

    答案:

          我们来分析下,什么时候水的高度会等于第 i 块木板的高度 hi 呢,一定是水往右边蔓延遇到了一块高度大于等于 hi 的木板 j,ai 就等于木板 i 和木板 j 之间的木板数。于是,问题就变成了寻找在第 i 个数右边第一个比它大的数。可以暴力求解,从 1 循环到 n,对每块木板再往右循环一遍,这样的时间复杂度是 O(n2) 的。有没有高效一点的做法呢?我们回想下单调栈的性质,可以在某点左右扩展出一段连续区间,且该点在区间里始终保证是最值。是不是和这题很像呀,而且这道题只要看点右侧扩展出来的区间即可。那么,接下来我们就用单调栈来解这道题吧。

           

     1 #include<iostream>
     2 #include<cassert>
     3 using namespace std;
     4 class Node {
     5 public:
     6     int id, height;
     7 };
     8 template<class Type> class Stack {
     9 private:
    10     Type *urls;
    11     int max_size, top_index;
    12 public:
    13     Stack(int length_input) {
    14         urls = new Type[length_input];
    15         max_size = length_input;
    16         top_index = -1;
    17     }
    18     ~Stack() {
    19         delete[] urls;
    20     }
    21     bool push(const Type &element) {
    22         if (top_index >= max_size - 1) {
    23             return false;
    24         }
    25         top_index++;
    26         urls[top_index] = element;
    27         return true;
    28     }
    29     bool pop() {
    30         if (top_index < 0) {
    31             return false;
    32         }
    33         top_index--;
    34         return true;
    35     }
    36     Type top() {
    37         assert(top_index >= 0);
    38         return urls[top_index];
    39     }
    40     bool empty() {
    41         if (top_index < 0) {
    42             return true;
    43         } else {
    44             return false;
    45         }
    46     }
    47 };
    48 int main() {
    49     int n,ans=0;
    50     cin>>n;
    51     Stack<Node> stack(n);
    52     Node temp;
    53     for(int i=1;i<=n;i++){
    54         cin>>temp.height;
    55         temp.id=i;
    56         while(!stack.empty()&&stack.top().height<=temp.height){
    57             ans=ans+i-stack.top().id-1;
    58             stack.pop();
    59         }
    60         stack.push(temp);
    61     }
    62     while(!stack.empty()){
    63         ans=ans+n+1-stack.top().id-1;
    64         stack.pop();
    65     }
    66     cout<<ans<<endl;
    67     return 0;
    68 }
  • 相关阅读:
    (零 ) 天猫精灵接入Home Assistant-总说明
    (二 -2) 天猫精灵接入Home Assistant-自动发现Mqtt设备
    (二 -1) 天猫精灵接入Home Assistant-控制Mqtt设备
    如何在关闭ssh连接的情况下,让程序继续运行?
    hass连接设备
    (一 ) 天猫精灵接入Home Assistant-服务器搭建
    (一) 天猫精灵接入Home Assistant- hass对接天猫精灵
    (3)HomeAssistant 连接MQTT
    (2)HomeAssistant 参数配置
    (1)HomeAssistant 安装开始
  • 原文地址:https://www.cnblogs.com/Reindeer/p/5671913.html
Copyright © 2020-2023  润新知