• 数据结构之栈


    • 用数组实现一个顺序栈
    • 用链表实现一个链式栈
    • 编程模拟实现一个浏览器的前进、后退功能

    用数组实现一个顺序栈

    class Stack(object):
        def __init__(self):
            self.stack=[]
        
        def push(self,item):
            self.stack.append(item)
            
        def pop(self):
            return self.stack.pop()
        
        def peek(self):
            if len (self.stack)>=1:
                return self.stack[-1]
            else :
                return '栈空'
    #测试用例
    if __name__=='__main__':
        st=Stack()
        st.push(1)
        st.push(2)
        print(st.pop())
        print(st.peek())
        print(st.pop())

    #include <iostream>


    const int StackSize=100;

    //

    template <class Datatype>

    class SeqStack {

    private:

        Datatype data[StackSize];

        int top;

    public:

        SeqStack(){top=-1;};    //将项顶指针置为-1

        ~SeqStack(){}

        void Push(Datatype x);

        Datatype Pop();

        Datatype GetTop();//取栈顶元素实现

        int Empty(); //判断是否为空

        void printlist(); //打印

    };


    template <class Datatype>

    int SeqStack<Datatype>::Empty()

    {

        if (top==-1) {

            return 1;

        }

        else return 0;

    }

    //入栈操作

    template <class Datatype>

    void SeqStack<Datatype>::Push(Datatype x)

    {

        if (top==StackSize-1) throw "栈空间已满,无法再添加";

            data[++top]=x;

    }

    //出栈操作

    template <class Datatype>

    Datatype SeqStack<Datatype>::Pop()

    {

        if (top==-1) throw "栈已经为空";

        

            Datatype x;

           x=data[top--];

            return x;

        

    }


    template <class Datatype>

    Datatype SeqStack<Datatype>::GetTop()

    {

        if (top==-1) throw "这是空栈";

        return data[top];

            

    }

    template <class Datatype>

    void  SeqStack<Datatype>::printlist()

    {

        if (top==-1) throw "这是空栈";

        for (int i=0; i<=top; i++) {

            std::cout<<data[i]<<" ";

        }

    }


    int main(int argc, const char * argv[]) {

        SeqStack<int> Student = SeqStack<int>();

        std::cout<<"is empty?"<<Student.Empty();

        for (int i=0; i<10; i++) {

            Student.Push(i);

        }

        std::cout<<"is empty?"<<Student.Empty();

        std::cout<<"The top word is"<<Student.GetTop();

        Student.printlist();

        std::cout<<"Pop the pop word,and it's "<<Student.Pop()<<" ";

     std::cout<<"The top word is"<<Student.GetTop()<<" ";

        return 0;

    }
    主要操作函数如下:

            InitStack(SqStack &s) 参数:顺序栈s 功能:初始化  时间复杂度O(1)
            Push(SqStack &s,SElemType e) 参数:顺序栈s,元素e 功能:将e入栈 时间复杂度:O(1)
            Pop(SqStack &s,SElemType &e) 参数:顺序栈s,元素e 功能:出栈,e接收出栈元素值 时间复杂度O(1)
            GetTop(SqStack s,SElemType &e) 参数:顺序栈s,元素e 功能:得到栈顶元素 时间复杂度O(1)
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    using namespace std;
    #define Status int
    #define SElemType int
    #define MaxSize 100
    //栈数据结构
    typedef struct Stack
    {
        SElemType *base;//栈底指针 不变
        SElemType *top;//栈顶指针 一直在栈顶元素上一个位置
        int stacksize;//栈可用的最大容量
    }SqStack;
    //**************************************基本操作函数************************************//
    //初始化函数
    Status InitStack(SqStack &s)
    {
        s.base=new SElemType[MaxSize];//动态分配最大容量
        if(!s.base)
        {
            printf("分配失败 ");
            return 0;
        }
        s.top=s.base;//栈顶指针与栈底相同 王道上top起初在base下面,感觉很别扭,top应该高于或等于base
        s.stacksize=MaxSize;
        return 1;
    }
    //入栈
    Status Push(SqStack &s,SElemType e)
    {
        if(s.top-s.base==s.stacksize) return 0;//栈满
        *(s.top++)=e;//先入栈,栈顶指针再上移 注意与王道上的不同,具体问题具体分析
        return 1;    
    }
    //出栈 用e返回值
    Status Pop(SqStack &s,SElemType &e)
    {
        if(s.top==s.base) return 0;//栈空
        e=*--s.top;//先减减 指向栈顶元素,再给e
        return 1;    
    }
    //得到栈顶元素,不修改指针
    bool GetTop(SqStack s,SElemType &e) //严蔚敏版59页有问题,应该用e去获得,函数返回bool类型去判断
    {
        if(s.top==s.base) return false;//栈空            
        else e=*--s.top;
        return true;
            
    }
    //********************************功能实现函数**************************************//
    //菜单
    void menu()
    {
       printf("********1.入栈      2.出栈********* ");
       printf("********3.取栈顶    4.退出********* ");
    }
    //入栈功能函数 调用Push函数
    void PushToStack(SqStack &s)
    {
        int n;SElemType e;int flag;
        printf("请输入入栈元素个数(>=1): ");
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
         printf("请输入第%d个元素的值:",i+1);
         scanf("%d",&e);
         flag=Push(s,e);
         if(flag)printf("%d已入栈 ",e);
         else {printf("栈已满!!! ");break;}
        }
    }
    //出栈功能函数 调用Pop函数
    void PopFromStack(SqStack &s)
    {
        int n;SElemType e;int flag;
        printf("请输入出栈元素个数(>=1): ");
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
         flag=Pop(s,e);
         if(flag)printf("%d已出栈 ",e);
         else {printf("栈已空!!! ");break;}
        }
    }
    //取栈顶功能函数 调用GetTop
    void GetTopOfStack(SqStack &s)
    {
        SElemType e;bool flag;
        flag=GetTop(s,e);
        if(flag)printf("栈顶元素为:%d ",e);
        else printf("栈已空!!! ");
    }
    //主函数
    int main()
    {
     SqStack s;int choice;
     InitStack(s);
     while(1)
     {
      menu();
      printf("请输入菜单序号: ");
      scanf("%d",&choice);
      if(choice==4) break;
      switch(choice)
      {
      case 1:PushToStack(s);break;
      case 2:PopFromStack(s);break;
      case 3:GetTopOfStack(s);break;
      default:printf("输入错误!!! ");
      }
     }
     return 0;
    }

    用链表实现一个链式栈

    链式栈

    基于链表实现链式栈,对栈做以下操作:

    • 出栈
    • 入栈
    • 取栈顶元素
    • 初始化
    • 销毁

    基于栈后进先出的结构,入栈可以用头插来实现,出栈用头删来实现即可。

    初始化

    void LinkStackInit(LinkStack** phead)
    {
        if(phead==NULL)
        {
            return;//非法输入
        }
        *phead=NULL;
    }



    void LinkStackDestroy(LinkStack** phead)
    {
        if(phead==NULL)
        {
            return;
        }
        LinkStack* cur=*phead;
        for(;cur!=NULL;cur=cur->next)
        {
            free(cur);
        }
        *phead=NULL;
    }

    void LinkStackPush(LinkStack** phead,LinkStackType value)
    {
        if(phead==NULL)
        {
            return;//非法输入
        }
        LinkStack* new_node=CreatNode(value);
        new_node->next=*phead;
        *phead=new_node;
    }
    出栈

    void LinkStackPop(LinkStack** phead)
    {
        if(phead==NULL)
        {
            return;
        }
        if(*phead==NULL)
        {
            return;//空栈
        }
        LinkStack* to_delete=*phead;
        *phead=(*phead)->next;
        free(to_delete);
    }

    package stack;
    
    public class StackByLinkedlist {
    
        ListNode popNode;
    
        public boolean push(String item) {
            ListNode temp = popNode;
            popNode = new ListNode(item);
            popNode.next = temp;
            return true;
        }
    
        public String pop() {
            if (popNode == null) {
                return null;
            }
            String result = popNode.val;
            popNode = popNode.next;
            return result;
        }
    
        public String peek() {
            if (popNode == null) {
                return null;
            }
            String result = popNode.val;
    
            return result;
        }
    
        public boolean empty() {
            return popNode == null;
        }
    
    
        class ListNode {
            String val;
            ListNode next;
    
            ListNode(String x) {
                val = x;
            }
        }
    
    }

    编程模拟实现一个浏览器的前进、后退功能
    #include<iostream>
    #include<string>
    #include<stack>
    using namespace std;
    int main(){
     stack<string> backward,forward;
     string now,b,c="csw.jlu.edu.cn";
     while(1){
      cin>>b;
      if(b=="VISIT"){
       cin>>now;
       cout<<now<<endl;
       backward.push(c);
       while(!forward.empty()){
        forward.pop();
       }
      }
      if(b=="BACK"){
       if(backward.size()!=0){
        c=backward.top();
        backward.pop();
        cout<<c<<endl;
        forward.push(now);
        now=c;
       }
       else{
        cout<<"Ignored"<<endl;
        c="csw.jlu.edu.cn";
       }
      }
      if(b=="FORWARD"){
       if(forward.size()!=0){
        c=forward.top();
        forward.pop();
        cout<<c<<endl;
        backward.push(now);
        now=c;
       }
       else{
        cout<<"Ignored"<<endl;
       }
      }
      if(b=="QUIT"){
       break;
      }
      if(b!="VISIT"&&b!="BACK"&&b!="FORWARD"&&b!="QUIT"){
       cout<<"输入错误!"<<endl;
      }
     }
    }
    我们浏览网页,会发现“前进”和“后退”是 Web 浏览器的常用功能,实现该功能的一种方式是使用两个栈(backward 栈和forward 栈)来存储用户访问的网址,用户的不同操作对应的具体实现方法如下:

        后退(BACK):如果backward 栈为空,则该命令被忽略。否则,将当前页面压入forward栈,并从backward 栈中弹出一个页面作为当前页面。
        前进(FORWARD):如果forward 栈为空,则该命令被忽略。否则,将当前页面压入backward 栈,并从forward 栈中弹出一个页面作为当前页面。
        访问某网址(VISIT <URL>):将当前页面压入backward 栈,并将此次访问的网页作为当前页面,清空forward 栈。

    二、整体思路

    根据栈结构后进先出的顺序,我们很容易想到,可以把用户当前浏览的网页入栈,当用户在新界面点击回退按钮的时候,直接让栈顶元素出栈,让浏览器加载,不就实现了后退功能了吗?前进的功能也类似,于是乎,我们想到用两个栈分别存储在用户当前浏览网页之前/之后的所有页面地址。理想的结构(BackStack回退栈,ForwardStack前进栈),如下图所示:

     四、页面按钮实现的前进后退

        <input type=button value=刷新 onclick="window.location.reload()">
        <input type=button value=前进 onclick="window.history.go(1)">
        <input type=button value=后退 onclick="window.history.go(-1)">
        <input type=button value=前进 onclick="window.history.forward()">
        <input type=button value=后退 onclick="window.history.back()">
        <input type=button value=后退并刷新  onclick="window.history.go(-1);window.location.reload()">

    下面是history源码,有没有什么发现呢?

        interface History {
            readonly length: number;
            scrollRestoration: ScrollRestoration;
            readonly state: any;
            back(distance?: any): void;
            forward(distance?: any): void;
            go(delta?: any): void;
            pushState(data: any, title?: string, url?: string | null): void;
            replaceState(data: any, title?: string, url?: string | null): void;
        }

    我们浏览网页,经常会用到前进,后退的功能。比如依次浏览了a-b-c 三个页面,这时点后退,可以回到 b 页面,再次点击回到 a 页面。如果这时,又进入 新页面d ,那就无法通过前进,后退功能回到b、c页面了。

    这样一个功能,要如何实现呢?

    其实这样一个功能,可以借助栈来实现。

    什么是栈?它是一个数据结构,数据先进后出,后进先出。打一个比方,放一叠盘子,放的时候,都放最上面,取的时候也从最上面取。典型的后进先出。

    栈,可以用数组来实现,也可以用链表来实现。前者叫顺序栈,后者是链式栈。

    为什么会有栈这种数据结构,直接用数组或是链表不就好了么?栈有严格的限制,只能从栈顶压入数据,从栈顶取数据,可以满足特定场景的需求,而链表或是数组暴露了太多的接口,容易出错。
    如何实现一个栈

    public class ArrayStack{
        private String [] items; // 数组
        private int count; // 栈中元素个数
        private int n; //栈的大小
        
        // 初始化数组,申请一个大小为 n 的数组空间
        public ArrayStack(int n){
            items = new String [n];
            this.n = n;
            count = 0;
        }
        // 入栈操作
        public boolean push(String item){
            if(count == n){
                return false;
            }
            items[count-1] = item;
            count++;
            return true;
        }
        // 出栈操作
        public String pop(){
        if(count == 0){
            return null;
        }
        String temp = items[count-1];
        count--;
        return temp;
        }
    }


    栈的实际应用

    栈作为一个基础的数据结构,经典的应用场景是函数调用。

    我们知道,操作系统,为每个线程分配了独立的内存空间,这些内存空间组织成栈的结构,来存储函数调用产生的临时变量。每次进入一个函数,将临时变量作为栈桢入栈,当函数调用完成,有结果返回时,函数对应的栈桢出栈。便于理解,我们看一看下面的一段代码

    int main() {
        int a = 1;
        int ret = 0;
        int res = 0;
        ret = add(3, 5);
        res = a+ret;
        printf("%d", res);
        return 0;
    }
    int add(int x, int y){
        int sum = 0;
        sum = x + y;
        return sum;
    }



    为了清晰地看到这个函数调用的过程中,出栈、入栈的操作,贴一张图
    在这里插入图片描述
    栈在表达式求值中的应用

    再看一个常见的应用场景,编译器如何利用栈来实现表达式求值。

    我们用一个简单的四则运算来说明这个问题,比如3+5*8-6,对于计算机来说,如何理解这个表达式,是个不太容易的事儿。

    事实上,编译器是通过两个栈来实现的,其中一个保存数字,暂且叫A,一个保存运算符暂且叫B。从左到右遍历表达式,当遇到数字,直接压入A栈,当遇到运符时,就与B栈中栈顶元素进行比较。

    如果比栈顶元素的优先级高,就将运算符压入栈,如果相同或者低,从B栈中取出栈顶元素,从A栈的栈顶取出两个数字,进行计算,再把计算结果压入A栈,继续比较。如下图在这里插入图片描述

    解答开篇的那个问题

    我们使用两个栈,X和Y,首次浏览的页面,依次压入栈X。当点击回退时,依次从X栈中取出数据,并依次放入Y栈。点击前进时,从Y栈中取出数据,并依次放入X栈。当X栈中没有数据时,就说明不能再回退了;当Y中没有数据时,就说明不能再前进了。

    比如你依次浏览了a b c 三个页面,我们依次把 a b c 压入栈,这时两个栈是这个样子
    在这里插入图片描述
    浏览器通过后退按钮,回退到页面 a 时,我们把 c b 依次压入Y栈,这时两个栈是这个样子
    在这里插入图片描述
    这时候,你又通过前进按钮,又回到 b 页面,这时两个栈的数据是这个样子
    在这里插入图片描述

    这时,你由 b 页面打开新的页面 d,那么你就不能再通过后退回到 c 页面,Y栈数据要清空。这时两个栈是这个样子
    https://blog.csdn.net/every__day/article/details/83753385

    实现浏览器的前进后退功能

    使用两个栈 X 和 Y,把首次浏览的页面依次压入栈 X,当点击后退按钮时,再依次从栈 X 中出栈,并将出栈的数据依次放入栈 Y。当点击前进按钮时,依次从栈 Y 中取出数据,放入栈 X 中。当栈 X 中没有数据时,那就说明没有页面可以继续后退浏览了。当栈 Y 中没有数据,那就说明没有页面可以点击前进按钮浏览了。

  • 相关阅读:
    嵌入式(C)笔试题
    经典C语言编程注意点
    深拷贝和浅拷贝(一)
    拷贝构造函数
    树与二叉树的概念
    线性数据结构
    排序算法_冒泡排序
    排序算法_选择排序
    机器学习_支持向量机
    数据库_11_1~10总结回顾+奇怪的NULL
  • 原文地址:https://www.cnblogs.com/hrnn/p/13338873.html
Copyright © 2020-2023  润新知