数据结构-第三章 栈和队列

2023-11-04

Stack and Queue

  1. 栈和队列是逻辑上的结构,在物理上可以用数组和链表来实现。

1. 栈

  1. A stack is a list in which insertions and deletions take place at the same end. This end is called the top. The other end of the list is called the bottom. It is also called a LIFO(last-in-first-out) list. 栈是一个插入和删除都发生在最后面的链表,这个最后面被我们称作top,而链表的另一边被我们称作bottom.这个链表被我们成为LIFO(后进先出)列表
  2. 理论上有方法:push,pop

1.1. 栈的模型

AbstractDataType Stack{
    instances
        list of elements;
        one end is called the  bottom;
        the other is the top;
    operations
        Create():Create an empty stack;
        IsEmpty():Return true if stack is empty,return  false otherwise
        IsFull ():Return true if stack if full,return false otherwise;
        Top():return top element of the stack;
        Add(x): add element x to the stack;
        Delete(x):Delete top element from stack and put it in x;
}

1.2. 栈的实现

1.2.1. 单链表实现

  1. 最后面是栈底,而表头是栈顶。
public class StackLi {
    public StackLi(){ topOfStack = null; }
    public boolean isFull(){ return false; }
    public boolean isEmpty(){ return topOfStack = = null; }
    public void makeEmpty(){ topOfStack = null; }
    public void push(object x)
    public object top()
    public void pop() throws Underflow;
    public object topAndPop()
    private  ListNode  topOfStack;
}

单链表栈的方法实现

public void push(object x){
    topOfStack = new ListNode(x, topOfStack);
}
public object top(){
    if(isEmpty())
        return null;
    return topOfStack.element;
}
public void pop() throws Underflow {
    if(isEmpty())
        throw new Underflow();
    topOfStack = topOfStack.next;
}

public object topAndPop(){
    if(isEmpty())
        return null;
    object topItem = topOfstack.element;
    topOfStack = topOfStack.next;
    return topItem;
}

1.2.2. 数组实现栈

  1. 数组靠后的部分(如上图)是栈顶。

栈的数组实现

public class stackAr {
    public StackAr()
    public StackAr(int capacity)
    public boolean isEmpty(){ return topOfStack = = -1; }
    public boolean isFull(){ return topOfStack = = theArray.length – 1; } 
    public void makeEmpty(){ topOfStack = -1; }
    public void push(object x) throws  overflow public object top()
    public void pop() throws  Underflow public object topAndPop()
    private object [] theArray;
    private int topOfStack;
    static final int DEFAULT_CAPACITY = 10;
}

数组实现的栈的方法实现

public StackAr(){
    this(DEFAULT_CAPACITY);
}
public StackAr(int capacity) {   
    theArray = new object [capacity];
    topOfStack = -1;
}
public void push(object x) throws Overflow {
    if (isfull())
        throw new Overflow();
    theArray[++topOfStack] = x;
}
public object top() {
    if( isEmpty())
        return null;
    return theArray[ topOfStack ];
}
public void pop() throws Underflow {
    if(isEmpty())
        throw new Underflow( );
    theArray[ topOfStack-- ] = null;
}
public object topAndPop() {
    if(isEmpty())
        return null;
    object topItem = top( );
    theArray[ topOfStack-- ] = null;
    reurn topItem;
}

1.3. 栈的特点

  1. 无论是什么方法,时间复杂度都是O(1)

1.4. 创新的栈的实现

  1. 从两头向中间存储栈

1.5. 栈的使用

1.5.1. Balancing Symbols(Parenthesis Matching) 括号匹配

//c++
#include <iostream.h>
#include <string.h>
#include <stdio.h>
#include "stack.h"
const int Maxlength = 100; // max expression length
using namespace std;
void PrintMatchedPairs(char *expr) {
    Stack<int> s(Maxlength);
    int j, length = strlen(expr);
    for ( int i = l; i <= length; i++) {
        if (expr[i-1]=="(")
            s.Add(i);
        else if (expr[i-1]==")")
            try {
                s.Delete(j);//进栈的是括号的位置
                cout << j <<" "<<i<< endl;}
            catch (OutOfBounds)
                {cout << "No match for right parenthesis" << "at"<< i << endl;}
    }
    while ( !s.IsEmpty ()){
        s.Delete(j);
        cout<< "No match for left parenthesis at " << j << endl;
        }
}
void static main(void) {
    char expr[MaxLength];
    cout<< "type an expression of length at most" <<MaxLength<<endl;
    cin.getline(expr, MaxLength);
    cout<<"the pairs of matching parentheses in"<<endl;
    puts(expr);
    cout<<"are"<<endl;
    printMatcnedPairs(expr);
}
//复杂度O(n)

1.5.2. 例二:Expression Evaluation

  1. 中缀表达式变成后缀表达式
  2. 根据操作的元数来决定弹出几个来进行计算
  3. 分量是指除了运算符以外的值
enum Boolean {False, True};

#include<math.h>
#include <stack.h>
#include<iostream.h>

class calculator {
    public:
        calculator(int sz):s (sz) { }
        void Run();
        void clear();
    private:
        void AddOperand (double value) ; 
        Boolean Get2Operands(double & left, double & right) ;
        void DoOperator (char op) ;
        stack <double> s ;} 
void calculator::AddOperand(double value) {
    s.Push(value);
}
Boolean calculator::Get2Operands(double &left, double &right){
    if (s.IsEmpty()){
        cerr<<"Missing Operand!"<<endl;
        return false;
    }
    right = s.Pop();
    if (s.IsEmpty()){
        cerr<<"Missing Operand!"<<endl;
        return false;
    }
    left = s.Pop();
    return true;
}
void calculator::DoOperator(char op) { 
    double left, right;
    Boolean  result;
    result = Get2Operands(left, right);
    if (result==true)
        Switch (op) {  
            case "+":s.Push (left + right) ; break;
            case "-":s.Push (left - right) ; break;
            case "*":s.Push (left * right) ; break;
            case "/" :
                if (right == 0.0) 
                    {cerr << "divide by 0!" << endl; s.Clear (); }
                    else s.Push(left / right) ;break;
            case "^" :
                s.Push (power(left, right) ); break;
        }
    else
        s.Clear();
}
void Calculator::Run() {
    char ch;
    double newoperand;
    while (cin>>ch,ch!= "#") {
        switch (ch) {
            case "+":
            case "-":
            case "*":
            case "/":
            case "^":
                DoOperator(ch);
                break;
            default:
                cin.Putback(ch);//cin只能放回一个字符
                cin >> newoperand;
                AddOperand(newoperand);
                break;
        }
    }
}

void Calculator::clear() {
    s.MakeEmpty();
}
#include <Calculator.h>
void main (){
    Calculator CALC(10);
    CALC.Run();
}

中缀表达式转换后缀表达式

  1. 基本想法:
    • 遇到操作数(运算分量)直接输出。
    • 遇到操作符:当前的操作符一定是不输出的,如果当前运算符低于栈顶预算符优先级低,则输出,一直到当前运算符高于栈顶运算符优先级
  2. 括号比较麻烦:需要单独处理
    • 左半括号需要压栈,也就是只要保证任何一个优先级低就行,也就是我们希望左半括号永远不要输出出来,在遇到右半括号的时候出去左半括号。
  3. 每一个符号都有两个优先级,加减乘除的两个优先级都是一样的,而括号的优先级不是。
    • 经过计算和推断,我们可以得知一个优先级体系是不能满足
void postfix (expression e) {  
    Stack <char> s;
    char ch,  y ;
    s.MakeEmpty ();
    s.Push ("#");
    while (cin.get ( ch ) , ch != "#") {
        if (isdigit ( ch )) cout << ch;
        else if (ch ==")")
            for (y=s.Pop();y!="(";y=s.Pop())  
                cout << y ;
            else {
                for (y = s.Pop ();isp(y)>icp (ch);y=s.Pop( ))
                    cout<<y;
                s.Push (y);
                s.Push (ch);
            }
    }
    while (!s.IsEmpty ()){
        y = s.Pop ();
        cout <<y ;
    }
}

2. 队列

  1. 两头开,从一边进入,从一边出。
  2. A queue is a linear list in which additions and deletions take place at different ends. It is also called a first-in-first-out list. The end at which new elements are added is called the rear. The end from which old elements are deleted is called the front. 一个队列是一个添加和删除在不同端发生的线性链表,这个队列

2.1. Queue Model

AbstractDataType Queue {
    instances
        ordered list of elements;one end is called the front; the other is the rear;
    operations
        Create(): Create an empty queue; 
        IsEmpty(): Return true if queue is empty,return  false otherwise; 
        IsFull(): return true if queue is full, return false  otherwise; 
        First(): return first element of the queue;
        Last(): return last element of the queue;
        Add(x): add element x to the queue;
        Delete(x): delete front element from the queue  and put it in x; }

2.2. 队列的数组实现

  1. 我们可以用移动队列前后指针来减少方式数据中元素的移动。
  2. 将数据看成一个圈,当队列队尾指针到数组尾部时,调整到数组头部。
    • back = (back + 1) % theArray.length
    • rear = (rear + 1) % theArray.length

2.3. 队列的单链表实现

template<class T>class LinkedQueue {//T是任意的类型
    public:
        LinkedQueue(){front=back=0;}
        ~LinkedQueue();//无法调用析构函数,在delete对象的时候可以直接释放
        bool IsEmpty()const{return ((front)?false:true);}
        bool IsFull()const;
        T First()const;
        T Last()const;
        LinkedQueue<T>&Add(const T& x);
        LinkedQueue<T>& Delete(T& x);
    private:
        Node<T>*front;
        Node<T>*back;
    };       
  1. NULL是定义在0中

2.3.1. 一些方法的实现



2.4. 应用

2.4.1. 杨辉三角

  1. 一行打印,另一行进队完成
#include <stdio.h>
#include <iostream.h>
#include "queue.h"
void YANGHUI(int n) {
    Queue<int> q;
    q.makeEmpty();
    q.Enqueue(1);
    q.Enqueue(1);
    int s=0;
    for (int i=1; i<=n;i++) {
        cout << endl;
        for (int k=1;k<=10-i;k++)
            cout <<" ";
        q.Enqueue(0);
        for (int j=1;j<=i+2;j++) {
            int t = q.Dequeue();
            q.Enqueue(s+t);
            s = t;
            //0不需要进行打印
            if (j!=i+2) cout<< s <<" ";
        }
    }
}
  1. 队列数组的java实现
public class Yanghui {
    public static void main(String args[ ] ) {
        int n = 10;
        int mat[][] = new int [n][];//申请第一维的存储空间
        int i, j;
        for ( i = 0; i < n; i++) {
            mat[i] = new int [i+1];//申请第二维的存储空间 ,每次长度不同
            mat[i][0] = 1;mat[i][i] = 1;
            for ( j = 1;j < i; j++)
                mat[i][j] = mat[i-1][j-1] + mat[i-1][j];
            }
            for ( i = 0; i< mat.length; i++) {
                for ( j = 0; j < n-i; j++) System.out.print("   ");
                for ( j = 0; j < mat[i].length; j++)
                    System.out.print("   " + mat[i][j])
                System.out.println( );
            }
        }
    }

2.4.2. 寻找路径 Wire Routing

  1. 步骤:
    1. 搜索过程
      • 先从位置a(3,2)开始, 把a可到达的相邻方格都表为1( 表示与a相距为1). 注意: 具体实现时, 将a位置置为2, 其它相邻方格为a位置的值+1
      • 然后把标记为1的方格可到达的相邻方格都标记为2( 表示与a相距为2).
      • 标记过程继续进行下去, 直至到达b或找不到可到达的相邻方格为止. 本例中, 当到达b时, b上的表记为9(实现时为9+2=11)
    2. 构造a—>b的路径. 从b回溯到a
      • 从b出发, 并将b的位置保存在path中. 首先移动到比b的编号小1的相邻 位置上(5,6)
      • 接着再从当前位置继续移动到比当前标号小1的相邻位置上.
      • 重复这一过程, 直至到达a.
  2. 代码实现
bool FindPath(Position start, Position finish, int & PathLen, Position * & path) {
    if (( start.row == finish.row) &&(start.col == finish.col)) {
        PathLen = 0;
        return true;
    }//已经到达
    //m是格子大小
    for (int i = 0; i<= m+1; i++) {
        grid[0][i] = grid[m+1][i] = 1;
        grid[i][0] = grid[i][m+1] = 1;
    }//四维的地方设置为1

    Position offset[4];//四个方向
    offset[0].row = 0; offset[0].col = 1;//行不动,列加1
    offset[1].row = 1;  offset[1].col = 0;//行加一,列不动
    offset[2].row = 0;  offset[2].col = -1;//行不动,列减一
    offset[3].row = -1; offset[3].col = 0;//行减一,列不动

    int NumOfNbrs = 4;
    Position here, nbr;
    here.row = start.row;
    here.col = start.col;//将当前位置置为开始的位置
    grid[start.row][start.col] = 2;

    LinkedQueue<Position>Q;
    do{
        //label neighbors of here
        for ( int i = 0; i< NumOfNbrs; i++) {
            nbr.row = here.row + offset[i].row;
            nbr.col = here.col + offset[i].col;//给相邻的格子标号,当前格子出对,周围的格子进队
            if (grid[nbr.row][nbr.col] == 0) {
                grid[nbr.row][nbr.col] = grid[here.row][here.col] + 1;
                if ((nbr.row == finish.row) &&(nbr.col == finish.col))
                    break;
                Q.Add(nbr);//周围的格子进队
            }// end of if
        }// end of for
        if (( nbr.row == finish.row) && (nbr.col == finish.col))
            break;
        if (Q.IsEmpty()) return false;
        Q.Delete(here);
    } while(true);

    PathLen = grid[finish.row][finish.col]-2;
    path = new Position [PathLen];
    //trace backwards from finish
    here = finish;
    for ( int j = PathLen-1; j >= 0; j--) {
        path[j] = here;//当前的结尾问题,四方向往回找
        for ( int i = 0; i < NumOfNbrs; i++) { 
            nbr.row = here.row + offset[i].row;
            nbr.col = here.col + offset[i].col;
            if ( grid[nbr.row][nbr.col] == j+2) break;
        }
        here = nbr;
    }
    return true;
}
  1. 代码复杂度
    • 网格编号过程:O(m*m)
    • 重构过程O(Pathlen)

3. 2009年全国考研统考题

  1. 设将n(n>1)个整数存放到一维数组R中,试设计一个在时间和空间两方面尽可能有效的算法,将R中保有的序列循环左移P(0﹤P﹤n)个位置,即将R中的数据由(X0,X1,…,Xn-1)变换为(Xp,Xp+1,…,Xn-1,X0,X1,…,Xp-1)
    • (1)给出算法的基本设计思想。
    • (2)根据设计思想,采用C或C++或JAVA语言表述算法,关键之处给出注释。
    • (3)说明你所设计算法的时间复杂度和空间复杂度
  2. 方法一:先存p个数据,然后移动后面的数据,再次拷贝
  3. 方法二:存下X0,移动Xp,然后放置xp中应该放的东西,然后调整即可。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构-第三章 栈和队列 的相关文章

  • IDDPM论文阅读

    论文链接 Improved Denoising Diffusion Probabilistic Models 文章目录 摘要 引言 去噪扩散概率模型 定义 实际训练 对数似然改善 可学习的
  • Linux-Shell技巧-参数化alias

    shell脚本提供了改写命令方式 alias 但是alias改写常用的是直接改写方式 比如如下操作 alias ll ls alt alias g gvim 但通常情况下 有的明林需要传递参数 或者用户可以自定义话一些常用的路径 但有些文件

随机推荐

  • docker-/var/lib/docker数据迁移

    docker默认目录是 var lib docker 位于系统盘上 占用空间比较大 计划迁移到新挂在的盘上 第一步 在新盘上创建文件夹 mkdir p data docker lib 第二步 复制文件到新目录 rsync avz var l
  • 数据结构与算法(二十)快速排序、堆排序(四)

    数据结构与算法 三 软件设计 十九 https blog csdn net ke1ying article details 129252205 排序 分为 稳定排序 和 不稳定排序 内排序 和 外排序 内排序指在内存里 外排序指在外部存储空
  • electron-vue工程创建

    创建工程 创建一个工作文件夹用于存放所有Electron工程 设为 D work 打开CMD cd到 D work 文件夹下 然后执行命令 创建 electron vue 工程 创建过程会多次提示输入信息 vue init simulate
  • 【2011集训队出题】Digit

    Description 在数学课上 小T又被老师发现上课睡觉了 为了向全班同学证明小T刚才没有好好听课 数学老师决定出一道题目刁难一下小T 如果小T答不出 那么 情节就按照俗套的路线发展下去了 小T显然无法解决这么复杂的问题 可怜的小T只能
  • 联想拯救者系统重装?不求人教程

    前阵子帮人重装了联想原装系统 今天总结一下步骤 造福那些想给女同志装联想原装系统而无从下手的工具人 哈哈哈哈哈 进入正题 既然要重装系统 就绕不开做启动盘 要做一个玉洁冰清 小声BB 纯净无插件 的启动盘 U盘启动盘制作工具的选择就很关键
  • 爬虫毕设(三):爬取动态网页

    动态网页分析 按照上一篇的分析 直接使用XPath找到该标签 然后通过parse提取出数据 在写入到item中就完事了 但是 当信心满满的写完代码后却发现 控制台输入了一个简简单单的 小问号你是否有很多朋友 一顿操作猛如虎 一看输出数据无
  • 在Arduino中使用DS18B20温度传感器(基于OneWire和DallasTemperature库)

    文章目录 目的 快速使用 相关库引入 使用步骤 使用演示 演示一 演示二 演示三 相关库说明 OneWire库 DallasTemperature库 总结 目的 DS18B20是常用的温度传感器 功能够用 使用简单 本文将用Arduino第
  • 一周Hard (2021.12.20-2021.12.26)

    由于除了刷题外还有些个人的事情 所以大概本周的安排是这样的 本周前两天先研究下关于在周赛272中遇到的问题 打算思考明白并给出一个详细的证明 对于周赛272让我重新拎起LIS 打算对相应的题目进行学习 LC 673 另外大概是要重新学习DP
  • php 基于ICMP协议实现一个ping命令

    php 基于ICMP协议实现一个ping命令 网络协议是什么 ICMP 协议 什么是ICMP ICMP 的主要功能 ICMP 在 IPv4 和 IPv6 的封装 Wireshark抓包 ICMP 请求包分析 PHP构建 ICMP 数据包 p
  • FileInputStream 和 FileOutputStream

    1 什么是流 流是一连串流动的字符 是一组有序的数据序列 是以先进先出方式发送信息的通道 将数据从一个地方带到另一个地方 在 java 中所有数据都是使用流读写的 同时可以通过流进行文件的读写操作 2 流的分类 按照流向 可以分为输入流和输
  • 设计模式之访问器模式(Visitor)的C++实现

    1 访问器模式的提出 在软件开发过程中 早已发布的软件版本 由于需求的变化 需要给某个类层次结构增加新的方法 如果在该基类和子类中都添加新的行为方法 将给代码原有的结构带来破坏 同时 也违反了修改封闭 扩展开放的原则 访问器模式可以实现不改
  • Jira项目管理

    目录 需求管理 项目权限管理 sql jira看板设计 sprint需求看板 ALL需求看板 sprint研发看板 需求管理 版本 创建面板 创建 修复版本 只能管理同一个项目下的需求集 Epics 史诗 可以管理跨项目 有不同项目关键字
  • java常混淆知识,Java中==和equals区别

    在Java语言中 和equals都是用来比较两个对象是否相等的操作符 但是它们的比较方式和比较结果有所不同 操作符 操作符用于比较两个对象的引用是否相等 即这两个对象是否是同一个对象的引用 如果两个对象的引用相同 则返回true 否则返回f
  • scala学习-11-package object

    1 概述 Scala 2 8提供包对象 package object 的新特性 什么是包对象呢 按我的理解 根据Scala 一切皆对象 设计哲学 包 package 也是一种对象 既然是对象 那么就应该有属性和方法 也可以在包对象内声明某个
  • Sublime Text3 快速格式化代码

    英文版 打开Sublime软件 PreFerences gt Key Bindings User 如图 添加代码 keys alt shift f command reindent 保存即可 alt shift f 可以自己改为任意键的组合
  • 深入学习jquery源码之is()与not()

    深入学习jquery源码之is 与not is expr obj ele fn 概述 根据选择器 DOM元素或 jQuery 对象来检测匹配元素集合 如果其中至少有一个元素符合这个给定的表达式就返回true 如果没有元素符合 或者表达式无效
  • three.js ThreeBSP(多个模型组合:差集、交集、并集 附带demo) - 05

    文章目录 一 什么是模型运算 1 函数属性介绍 2 代码示例 二 模型组合demo 需要在我的第一节中找到对应的库 或者私信我 2 代码效果 2 1并集效果 2 2 差集效果 2 3 交集效果 一 什么是模型运算 我所理解的ThreeBSP
  • InfluxDB 的 InfluxQL 基本介绍与使用

    前言 本文主要介绍 InfluxDB 的 InfluxQL 的基本概念与用法并且包含了一些需要注意的点 由于 InfluxDB 2 x 不使用 InfluxQL 进行查询 如您的版本大于 2 x 请查找其他资料 主要为以下内容 SELECT
  • Linux-升级CMake版本(Ubuntu18.4)

    一 简介 在一些场景中 因为CMake版本过低而无法编译 此时就需要升级CMake的版本 二 升级 卸载 先卸载旧的cmake sudo apt get autoremove cmake 安装 切换文件夹 cd usr src 下载cmak
  • 数据结构-第三章 栈和队列

    Stack and Queue 栈和队列是逻辑上的结构 在物理上可以用数组和链表来实现 1 栈 A stack is a list in which insertions and deletions take place at the sa