第2章第1节 栈

栈作为一种受限的线性表,同样可以划分为顺序结构存储的栈(这里简称顺序栈)和链式结构存储的栈(这里简称链栈)。

一.顺序栈

1.1定义

栈的顺序存储称为顺序栈,是利用一组地址连续的存储单元存放从栈底到栈顶的元素,同时附设一个指针(top)指示当前栈顶的位置。如图所示:

顺序栈

栈的顺序存储类型则可以描述为:

typedef struct{
    ElemType data[MaxSize];
    int top;
}SqStack;

这里对顺序栈的判断条件进行说明。

  • 栈空条件:top==-1
  • 栈满条件:top==MaxSize-1
  • 栈长:top+1

1.2基本操作

1.2.1初始化操作

完成对栈顶指针的复位操作。

void InitStack(SqStack* s)
{
    s->top=-1;
}

1.2.2判空操作

如果栈为空,则返回0;如果栈不为空,则返回-1。

int StackEmpty(SqStack* s)
{
    if(s->top==-1){
        return 0;
    }else{
        return -1;
    }
}

1.2.3入栈操作

首先判断栈是否已满,如果栈已经满了,则返回-1;如若没有满,则在栈顶中插入元素,并返回0。

int Push(SqStack* s, ElemType x)
{
    if(s->top==MaxSize-1){
        return -1;
    }
    s->data[++s->top]=x;
    return 0;
}

1.2.4出栈操作

首先判断栈是否为空,如果已经为空,则返回-1;如果不为 空,则取得栈顶元素,并返回0。

int Pop(SqStack* s, ElemType* x)
{
    if(s->top==-1){
        return -1;
    }
    *x=s->data[s->top--];
    return 0;
}

1.2.5读栈顶元素

首先判断栈是否为空,如果为空,则返回-1;如果不为空,则返回栈顶元素,并返回0。

int GetTop(SqStack* s, ElemType *x)
{
    if(s->top==-1){
        return -1;
    }
    *x=s->data[s->top];
    return 0;
}

二.共享栈

2.1定义

利用栈底位置相对不变的特性,可以让两个顺序栈共享一个一维数据空间,将两个栈的栈底分别设置在共享空间的两端,两个栈顶向共享空间中间延伸。如图所示:

共享栈

则共享栈的数据结构可以描述为:

typedef struct{
    ElemType data[MaxSize];
    int top[2];
}SqStack;

这里对共享栈的判断条件进行说明。

  • 栈空条件:0号栈【top[0]==-1】;1号栈【top[1]==MaxSize】
  • 栈满条件:top1-top0=1

2.2基本操作

2.2.1初始化操作

完成对栈顶指针的复位操作,按照上述规定,0#栈为空,则top[0]==-1;若1#栈为空,则top[1]==MaxSize;

void InitStack(SqStack *s)
{
    s->top[0]=-1;
    s->top[1]=MaxSize;
}

2.2.2判空操作

同样根据上述规定,0#栈为空,则top[0]==-1;若1#栈为空,则top[1]==MaxSize;那么若两个栈均为空则返回0;如果栈不为空,则返回-1。

int StackEmpty(SqStack* s)
{
    if(s[0]->top==-1&&s[1]->top==MaxSize){
        return 0;
    }else{
        return -1;
    }
}

1.2.3入栈操作

先确定栈号是否合法,然后查看是对0#栈还是1#栈进行操作,入栈操作和顺序栈的入栈操作并无太大不同。选定之后进行入栈操作。这里应该注意此共享栈是否已满,如果已满则不能进行入栈操作。如若入栈成功则返回0;入栈失败则返回-1。

int Push(SqStack*s, ElemType x, int n)
{
    if(n<0||n>1){
        printf("The stack number is false!\n");
        return -1;
    }
    if(s->top[1]-s->top[0]==1){
        printf("The stack is full!\n");
        return -1;
    }
    switch(n){
        case 0:s->data[++s->top[0]]=x;break;
        case 1:s->data[--s->top[1]]=x;break;
    }
    return 0;
}

2.2.4出栈操作

先确定栈号是否合法,然后查看是对0#栈还是1#栈进行操作,出栈操作和顺序栈的出栈操作并无太大不同。选定之后进行出栈操作。如果出栈成功返回0;出栈失败返回-1。

int Pop(SqStack *s, ElemType* x,int n)
{
    if(n<0||n>1){
        printf("The stack number is false!\n");
        return -1;
    }
    switch(n){
        case 0:
            if(s->top[0]==-1){
                printf("The stack[0] is empty!\n");
            }
            *x=s->data[s->top[0]--];
            break;
        case 1:
            if(s->top[1]==MaxSize){
                printf("The stack[1] is empty!\n");
            }
            *x=s->data[s->top[1]++];
            break;
    }
    return 0;
}

三.链栈

3.1定义

采用链式存储的栈称为链栈。
链栈的优点便是便于多个栈共享存储空间和提高其效率,而且不存在栈满上溢的情况。通常采用单链表实现,并规定所有的操作都是在单链表的表头进行的,这里规定链栈没有头节点。如图所示:

链栈

链栈的数据结构的描述如下:

typedef struct LinkNode{
    ElemType data;
    struct LinkNode *next;
}LinkStack;

因为采用链式存储时的栈与第1章的单链表操作类似,这里便不再详细说明。但应当注意区分带头结点的单链表与不带头结点的单链表操作略有不同。



注:在栈的操作中,经常会求到对于n个不同的元素进栈,则出栈序列的个数为多少?
对于这种题,我们可以引入卡特兰(Catalan)数进行解决。答案为: 1n+1(2nn)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
深入java虚拟机第二版 第1 Java体系结构介绍 1.1 为什么使用Java 1.2 网络带来的挑战和机遇 1.3 体系结构 1.3.1 Java虚拟机 1.3.2 类装载器的体系结构 1.3.3 Java class文件 1.3.4 Java API 1.3.5 Java程序设计语言 1.4 Java体系结构的代价 1.5 结论 1.6 资源页 第2 平台无关 2.1 为什么要平台无关 2.2 Java的体系结构对平台无关的支持 2.2.1 Java平台 2.2.2 Java语言 2.3.3 Java class文件 . 2.2.4 可伸缩性 2.3 影响平台无关性的因素 2.3.1 Java平台的部署 2.3.2 Java平台的版本 2.3.3 本地方法 2.3.4 非标准运行时库 2.3.5 对虚拟机的依赖 2.3.6 对用户界面的依赖 2.3.7 Java平台实现中的bug 2.3.8 测试 2.4 平台无关的七个步骤 2.5 平台无关性的策略 2.6 平台无关性和网络移动对象 2.7 资源页 第3 安全 3.1 为什么需要安全性 3.2 基本沙箱 3.3 类装载器体系结构 3.4 class文件检验器 3.4.1 第一趟:class文件的结构检查 3.4.2 第二趟:类型数据的语义检查 3.4.3 第三趟:字码验证 3.4.4 第四趟:符号引用的验证 3.4.5 二进制兼容 3.5 Java虚拟机中内置的安全特性 3.6 安全管理器和Java API 3.7 代码签名和认证 3.8 一个代码签名示例 3.9 策略 3.10 保护域 3.11 访问控制器 3.11.1 implies()方法 3.11.2 检查示例 3.11.3 一个回答“是”的检查 3.11.4 一个回答“不”的检查 3.11.5 doPrivileged()方法 3.11.6 doPrivileged()的一个无效使用 3.12 Java安全模型的不足和今后的发展 方向 3.13 和体系结构无关的安全性 3.14 资源页 第4 网络移动性 4.1 为什么需要网络移动性 4.2 一种新的软件模式 4.3 Java体系结构对网络移动性的支持 4.4 applet:网络移动性代码的示例 4.5 Jini服务对象:网络移动对象的示例 4.5.1 Jini是什么 4.5.2 Jini如何工作 4.5.3 服务对象的优点 4.6 网络移动性:Java设计的中心 4.7 资源页 第5 Java虚拟机 5.1 Java虚拟机是什么 5.2 Java虚拟机的生命周期 5.3 Java虚拟机的体系结构 5.3.1 数据类型 5.3.2 字长的考量 5.3.3 类装载器子系统 5.3.4 方法区 5.3.5 堆 5.3.6 程序计数器 5.3.7 Java 5.3.8 帧 5.3.9 本地方法 5.3.10 执行引擎 5.3.11 本地方法接口 5.4 真实机器 5.5 一个模拟:“Eternal Math” 5.6 随书光盘 5.7 资源页 第6 Java class文件 6.1 Java class文件是什么 6.2 class文件的内容 6.3 特殊字符串 6.3.1 全限定名 6.3.2 简单名称 6.3.3 描述符 6.4 常量池 6.4.1 CONSTANT_Utf8_info表 6.4.2 CONSTANT_Integer_info表 6.4.3 CONSTANT_Float_info表 6.4.4 CONSTANT_Long_info表 6.4.5 CONSTANT_Double_info表 6.4.6 CONSTANT_Class_info表 6.4.7 CONSTANT_String_info表 6.4.8 CONSTANT_Fieldref_info表 6.4.9 CONSTANT_Methodref_info表 6.4.10 CONSTANT_InterfaceMethodref_ info表 6.4.11 CONSTANT_NameAndType_info 表 6.5 字段 6.6 方法 6.7 属性 6.7.1 属性格式 6.7.2 Code属性 6.7.3 ConstantValue属性 6.7.4 Deprecated属性 6.7.5 Exceptions属性 6.7.6 InnerClasses属性 6.7.7 LineNumberTable属性 6.7.8 LocalVariableTable属性 6.7.9 SourceFile属性 6.7.10 Synthetic属性 6.8 一个模拟:“Getting Loaded” 6.9 随书光盘 6.10 资源页 第7 类型的生命周期 7.1 类型装载、连接与初始化 7.1.1 装载 7.1.2 验证 7.1.3 准备 7.1.4 解析 7.1.5 初始化 7.2 对象的生命周期 7.2.1 类实例化 7.2.2 垃圾收集和对象的终结 7.3 卸载类型 7.4 随书光盘 7.5 资源页 第8 连接模型 8.1 动态连接和解析 8.1.1 解析和动态扩展 8.1.2 类装载器与双亲委派模型 8.1.3 常量池解析 8.1.4 解析CONSTANT_Class_info入口 8.1.5 解析CONSTANT_Fieldref_info 入口 S.1.6 解析CONSTANT_Methodref_info 入口 8.1.7 解析CONSTANT_Interface- Methodref_info入口 8.1.8 解析CONSTANT_String_info入口 8.1.9 解析其他类型的入口 8.1.10 装载约束 8.1.11 编译时常量解析 8.1.12 直接引用 8.1.13 _quick指令 8.1.14 示例:Salutation程序的连接 8.1.15 示例:Greet程序的动态扩展 8.1.16 使用1.1版本的用户自定义类装 载器 8.1.17 使用1.2版本的用户自定义类装 载器 8.1.18 示例:使用forName()的动态扩展 8.1.19 示例:卸载无法触及的greeter类 8.1.20 示例:类型安全性与装载约束 8.2 随书光盘 8.3 资源页 第9 垃圾收集 9.1 为什么要使用垃圾收集 9.2 垃圾收集算法 9.3 引用计数收集器 9.4 跟踪收集器 9.5 压缩收集器 9.6 拷贝收集器 9.7 按代收集的收集器 9.8 自适应收集器 9.9 火车算法 9.9.1 车厢、火车和火车站 9.9.2 车厢收集 9.9.3 记忆集合和流行对象 9.10 终结 9.11 对象可触及性的生命周期 9.11.1 引用对象 9.11.2 可触及性状态的变化 9.11.3 缓存、规范映射和临终清理 9.12 一个模拟:“Heap of Fish” 9.12.1 分配鱼 9.12.2 设置引用 9.12.3 垃圾收集 9.12.4 压缩堆 9.13 随书光盘 9.14 资源页 第10 和局部变量操作 10.1 常量入操作 10.2 通用操作 10.3 把局部变量压入 10.4 弹出顶部元素,将其赋给局部变量 10.5 wide指令 10.6 一个模拟:“Fibonacci Forever” 10.7 随书光盘 10.8 资源页 第11 类型转换 11.1 转换操作码 11.2 一个模拟:“Conversion Diversion” 11.3 随书光盘 11.4 资源页 第12 整数运算 12.1 二进制补码运算 12.2 Inner Int:揭示Java int类型内部性质 的applet 12.3 运算操作码 12.4 一个模拟:“Prime Time” 12.5 随书光盘 12.6 资源页 第13 逻辑运算 13.1 逻辑操作码 13.2 一个模拟:“Logical Results” 13.3 随书光盘 13.4 资源页 第14 浮点运算 14.1 浮点数 14.2 Inner Float:揭示Java float类型内部 性质的applet 14.3 浮点模式 14.3.1 浮点值集合 14.3.2 浮点值集的转换 14.3.3 相关规则的本质 14.4 浮点操作码 14.5 一个模拟:“Circle of Squares” 14.6 随书光盘 14.7 资源页 第15 对象和数组 15.1 关于对象和数组的回顾 15.2 针对对象的操作码 15.3 针对数组的操作码 15.4 一个模拟:“Three—Dimensional Array” 15.5 随书光盘 15.6 资源页 第16 控制流 16.1 条件分支 16.2 五条件分支 16.3 使用表的条件分支 16.4 一个模拟:“Saying Tomato” 16.5 随书光盘 16.6 资源页 第17 异常 17.1 异常的抛出与捕获 17.2 异常表 17.3 一个模拟:“Play Ball!” 17.4 随书光盘 17.5 资源页 第18 finally子句 18.1 微型子例程 18.2 不对称的调用和返回 18.3 一个模拟:“Hop Around” 18.4 随书光盘 18.5 资源页 第19 方法的调用与返回 19.1 方法调用 19.1.1 Java方法的调用 19.1.2 本地方法的调用 19.2 方法调用的其他形式 19.3 指令invokespecial 19.3.1 指令invokespecial和[init]()方法 19.3.2 指令invokespecial和私有方法 19.3.3 指令invokespecial和super关键字 19.4 指令invokeinterface 19.5 指令的调用和速度 19.6 方法调用的实例 19.7 从方法中返回 19.8 随书光盘 19.9 资源页 第20 线程同步 20.1 监视器 20.2 对象锁 20.3 指令集中对同步的支持 20.3.1 同步语句 20.3.2 同步方法 20.4 Object类中的协调支持 20.5 随书光盘 20.6 资源页 附录A 按操作码助记符排列的指令集 附录B 按功能排列的操作码助记符 附录C 按操作码字值排列的操作码助

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值