数据结构——栈

简介: 数据结构——栈

介绍

栈是一种用于存储数据的简单数据结构,有点类似链表或者顺序表(统称线性表),栈与线性表的最大区别是

数据的存取的操作,我们可以这样认为栈(Stack)是一种特殊的线性表,其插入和删除操作只允许在线性表的一端

进行,一般而言,把允许操作的一端称为栈顶(Top),不可操作的一端称为栈底(Bottom),同时把插入元素的操作

称为入栈(Push),删除元素的操作称为出栈(Pop)。若栈中没有任何元素,则称为空栈,栈的结构如下图:

由图我们可看成栈只能从栈顶存取元素,同时先进入的元素反而是后出,而栈顶永远指向栈内最顶部的元素。

到此可以给出栈的正式定义:栈(Stack)是一种有序特殊的线性表,只能在表的一端(称为栈顶,top,总是指向栈顶

元素)执行插入和删除操作,最后插入的元素将第一个被删除,因此栈也称为后进先出(Last In First Out,LIFO)或先

进后出(First In Last Out FILO)的线性表。栈的基本操作创建栈,判空,入栈,出栈,获取栈顶元素等,注意栈不

支持对指定位置进行删除,插入。

栈的设计与实现

顺序栈

接口Stack声明如下:

/**
* 栈接口抽象数据类型
*/
publicinterfaceStack<T> {

  /**
   * 栈是否为空
   * @return
   */
  booleanisEmpty();

  /**
   * data元素入栈
   * @param data
   */
  voidpush(Tdata);

  /**
   * 返回栈顶元素,未出栈
   * @return
   */
  Tpeek();

  /**
   * 出栈,返回栈顶元素,同时从栈中移除该元素
   * @return
   */
  Tpop();
}

顺序栈,顾名思义就是采用顺序表实现的的栈,顺序栈的内部以顺序表为基础,实现对元素的存取操作,当然我们

还可以采用内部数组实现顺序栈,在这里我们使用内部数据组来实现栈,至于以顺序表作为基础的栈实现,将以源

码提供。这里先声明一个顺序栈其代码如下,实现Stack和Serializable接口:

**
*顺序栈的实现
*/
publicclassSeqStack<T>implementsStack<T>, Serializable{

   // 栈顶指针, -1 为空栈
   privateinttop=-1;

   //存放元素的数组
   privateT[] array;

   //容量,默认为10
   privateintcapacity=10;

   // 当前存放的个数
   privateintsize;

   publicSeqStack(intcapacity) {
       array= (T[]) newObject[capacity];
   }

   publicSeqStack() {
       array= (T[]) newObject[capacity];
   }

   publicintgetSize() {
       returnthis.size;
   }

   @Override
   publicbooleanisEmpty() {

       returntop==-1;
   }

   @Override
   publicvoidpush(Tdata) {

       if (array.length==size){
           //扩容
           ensureCapacity(size*2+1);
       }
       //添加元素、记录存放个数
       array[++top] =data;
       size++;
   }

   @Override
   publicTpeek() {

       //判断是否为空栈
       if (isEmpty()){
           thrownewEmptyStackException();
       }

       returnarray[top];
   }

   @Override
   publicTpop() {

       //判断是否为空栈
       if (isEmpty()){
           thrownewEmptyStackException();
       }

       size--;
       returnarray[top--];
   }

   /**
    * 扩容方法
    * @param capacity
    */
   privatevoidensureCapacity(intcapacity) {

       // 如果需要拓展的容量比现在数组的容量还小,则无需扩容
       if (capacity<size){
           return;
       }

       //先将数据存起来
       T[] old=array;
       array= (T[]) newObject[capacity];
       for (inti=0; i<old.length ;i++){
           array[i] =old[i];
       }

   }

   //测试
   publicstaticvoidmain(String[] args) {

       SeqStack<Object>stack=newSeqStack<>();
       stack.push("A");
       stack.push("B");
       stack.push("C");

       // size在减少,必须记录
       intsize=stack.getSize();
       for (inti=0; i<size ;i++){
           System.out.println(stack.pop());
       }
       System.out.println(stack.getSize());
   }

}

链式栈

我们接着来看看链式栈,所谓的链式栈(Linked Stack),就是采用链式存储结构的栈,由于我们操作的是栈顶一端,因此这里采用单链表(不带头结点)作为基础,直接实现栈的添加,获取,删除等主要操作即可。其操作过程如下图:

无论是插入还是删除直接操作的是链表头部也就是栈顶元素,因此我们只需要使用不带头结点的单链表即可。

/**
* 链式栈实现类
* @param <T>
*/
publicclassLinkedStack<T>  implementsStack<T>, Serializable{

   privateintsize;

   privateNode<T>top;

   publicintgetSize() {
       returnsize;
   }

   @Override
   publicbooleanisEmpty() {
       returntop==null||top.data==null;
   }

   @Override
   publicvoidpush(Tdata) {

       if (data==null){
           thrownewNullPointerException();
       }

       //调用pop()后top可能为null
       if (top==null){
           top=newNode<>(data, null);

       }elseif(top.data==null){
           //有头节点,但是内容为空
           top.data=data;

       }else{
           //加入子节点
           Node<T>p=newNode<T>(data, top);
           //更新栈顶
           top=p;
       }
       size++;

   }

   @Override
   publicTpeek() {

       if (isEmpty()){
           thrownewEmptyStackException();
       }

       returntop.data;
   }

   @Override
   publicTpop() {

       if (isEmpty()){
           thrownewEmptyStackException();
       }

       Tdata=top.data;
       //获取上一个节点
       top=top.top;
       size--;
       returndata;
   }

   publicstaticvoidmain(String[] args) {

       LinkedStack<Object>stack=newLinkedStack<>();
       stack.push("A");
       stack.push("B");
       stack.push("C");

       // size在减少,必须记录
       intsize=stack.getSize();
       for (inti=0; i<size ;i++){
           System.out.println(stack.peek());
           stack.pop();
       }
       System.out.println(stack.getSize());

   }

}

节点类

/**
* 节点类
* @param <T>
*/
publicclassNode<T> {

   publicTdata;

   publicNode<T>top;

   publicNode(Tdata, Node<T>top) {
       this.data=data;
       this.top=top;
   }
}

栈的应用

栈是一种很重要的数据结构,在计算机中有着很广泛的应用,如下一些操作都应用到了栈。

  • 符号匹配
  • 中缀表达式转换为后缀表达式
  • 计算后缀表达式
  • 实现函数的嵌套调用
  • HTML和XML文件中的标签匹配
  • 网页浏览器中已访问页面的历史记录

接下来我们分别对符合匹配进行简单的分析,以加深我们对栈的理解。

符号匹配

在编写程序的过程中,我们经常会遇到诸如圆括号“()”与花括号“{}”,这些符号都必须是左右匹配的,这就是我们所说的符合匹配类型,当然符合不仅需要个数相等,而且需要先左后右的依次出现,否则就不符合匹配规则,如“)(”,明显是错误的匹配,而“()”才是正确的匹配。有时候符合如括号还会嵌套出现,如“9-(5+(5+1))”,而嵌套的匹配原则是一个右括号与其前面最近的一个括号匹配,事实上编译器帮我检查语法错误是也是执行一样的匹配原理,而这一系列操作都需要借助栈来完成,接下来我们使用栈来实现括号”()”是否匹配的检测。

判断原则如下(str=”((5-3)*8-2)”):

  1. 设置str是一个表达式字符串,从左到右依次对字符串str中的每个字符char进行语法检测,如果char是,左括号则入栈,如果char是右括号则出栈(有一对匹配就可以去匹配一个左括号,因此可以出栈),若此时出栈的字符char为左括号,则说明这一对括号匹配正常,如果此时栈为空或者出栈字符不为左括号,则表示缺少与char匹配的左括号,即目前不完整。
  2. 重复执行a操作,直到str检测结束,如果此时栈为空,则全部括号匹配,如果栈中还有左括号,是说明缺少右括号。

整个检测算法的执行流程如下图:

接着我们用栈作为存储容器通过代码来实现这个过程

/**
* 表达式校验
*/
publicclassCheckExpression  {

   publicstaticStringisValid(Stringexpstr){

       LinkedStack<String>stack=newLinkedStack<String>();
       inti=0;
       
       while (i<expstr.length()){

           charc=expstr.charAt(i);
           i++;

           switch (c){
               case'(':
                   //入栈
                   stack.push(String.valueOf(c));
                   break;
               case')':
                   //出栈
                   stack.pop();
                   break;
           }
       }

       //最后检测是否为空,为空则检测通过
       if(stack.isEmpty()){
           return"check pass!";
       }
       else{
           return"check exception!";
       }

   }

   publicstaticvoidmain(String[] args) {

       Stringstr="(((5-3)*8-2)";
       System.out.println(isValid(str));

   }

}

目录
打赏
0
0
0
0
21
分享
相关文章
|
8月前
|
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
745 9
|
8月前
|
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
182 58
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
21 0
栈区的非法访问导致的死循环(x64)
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。
|
6月前
|
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
297 77
|
5月前
|
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
75 11
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
6月前
|
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
216 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
6月前
|
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
108 9
|
6月前
|
C++
【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
157 7
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问