栈:顺序栈和链式堆栈

一、堆栈的基本概念:

堆栈(也简称作栈)是一种特殊的线性表,堆栈的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置进行插入和删除操作,而堆栈只允许在固定一端进行插入和删除操作。

先进后出:堆栈中允许进行插入和删除操作的一端称为栈顶,另一端称为栈底。堆栈的插入和删除操作通常称为进栈或入栈,堆栈的删除操作通常称为出栈或退栈。

备注:栈本身就是一个线性表,所以我们之前讨论过线性表的顺序存储和链式存储,对于栈来说,同样适用。

二、堆栈的抽象数据类型:

数据集合:

堆栈的数据集合可以表示为a0,a1,…,an-1,每个数据元素的数据类型可以是任意的类类型。

操作集合:

(1)入栈push(obj):把数据元素obj插入堆栈。

(2)出栈pop():出栈, 删除的数据元素由函数返回。

(3)取栈顶数据元素getTop():取堆栈当前栈顶的数据元素并由函数返回。

(4)非空否notEmpty():若堆栈非空则函数返回true,否则函数返回false。

三、顺序栈:

顺序存储结构的堆栈称作顺序堆栈。其存储结构示意图如下图所示:

1、顺序栈的实现:

(1)设计Stack接口

(2)实现SequenceStack类

注:栈是线性表的特例,线性表本身就是用数组来实现的。于是,顺序栈也是用数组实现的。

代码实现:

(1)Stack.java:(Stack接口)


public interface Stack {

 

    //入栈

    public void push(Object obj) throws Exception;

 

    //出栈

    public Object pop() throws Exception;

 

    //获取栈顶元素

    public Object getTop() throws Exception;

 

    //判断栈是否为空

     public boolean isEmpty();

 

}

(2)SequenceStack.java:


//顺序栈

public class SequenceStack implements Stack {

 

    Object[] stack; //对象数组(栈用数组来实现)

    final int defaultSize = 10; //默认最大长度

    int top; //栈顶位置(的一个下标):其实可以理解成栈的实际长度

    int maxSize; //最大长度

 

    //如果用无参构造的话,就设置默认长度

    public SequenceStack() {

        init(defaultSize);

    }

 

    //如果使用带参构造的话,就调用指定的最大长度

    public SequenceStack(int size) {

        init(size);

    }

 

    public void init(int size) {

        this.maxSize = size;

        top = 0;

        stack = new Object[size];

    }

 

    //获取栈顶元素

    @Override

    public Object getTop() throws Exception {

        // TODO Auto-generated method stub

        if (isEmpty()) {

            throw new Exception("堆栈为空!");

        }

 

        return stack[top - 1];

    }

 

 

    //判断栈是否为空

    @Override

    public boolean isEmpty() {

        // TODO Auto-generated method stub

        return top == 0;

    }

 

    //出栈操作

    @Override

    public Object pop() throws Exception {

        // TODO Auto-generated method stub

        if (isEmpty()) {

            throw new Exception("堆栈为空!");

        }

        top--;

 

        return stack[top];

    }

 

    //入栈操作

    @Override

    public void push(Object obj) throws Exception {

        // TODO Auto-generated method stub

        //首先判断栈是否已满

        if (top == maxSize) {

            throw new Exception("堆栈已满!");

        }

        stack[top] = obj;

        top++;

    }

 

 

}

2、测试类:

设计一个顺序栈,从键盘输入十个整数压进栈,然后再弹出栈,并打印出栈序列。

代码实现:

(3)Test.java:


import java.util.Scanner;

 

public class Test {

 

    public static void main(String[] args) throws Exception {

        SequenceStack stack = new SequenceStack(10);

 

        Scanner in = new Scanner(System.in);

        int temp;

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

            System.out.println("请输入第" + (i + 1) + "个整数:");

            temp = in.nextInt();

            stack.push(temp);

        }



//遍历输出 

        while (!stack.isEmpty()) {

            System.out.println(stack.pop());

        }

 

    }

 

}

运行效果:

四、Java中栈与堆的区别:

栈(stack):(线程私有)

是一个先进后出的数据结构,通常用于保存方法(函数)中的参数,局部变量。在java中,所有基本类型和引用类型的引用都在栈中存储。栈中数据的生存空间一般在当前scopes内(就是由{...}括起来的区域)。

堆(heap):(线程共享)

是一个可动态申请的内存空间(其记录空闲内存空间的链表由操作系统维护),C中的malloc语句所产生的内存空间就在堆中。在java中,所有使用new xxx()构造出来的对象都在堆中存储,当垃圾回收器检测到某对象未被引用,则自动销毁该对象。所以,理论上说java中对象的生存空间是没有限制的,只要有引用类型指向它,则它就可以在任意地方被使用。

五、hashCode与对象之间的关系:

如果两个对象的hashCode不相同,那么这两个对象肯定也不同。

如果两个对象的hashCode相同,那么这两个对象有可能相同,也有可能不同。

总结一句:不同的对象可能会有相同的hashCode;但是如果hashCode不同,那肯定不是同一个对象

代码举例:


public class StringTest {

 

    public static void main(String[] args) {

 

        //s1 和 s2 其实是同一个对象。对象的引用存放在栈中,对象存放在方法区的字符串常量池

        String s1 = "china";

        String s2 = "china";

 

        //凡是用new关键创建的对象,都是在堆内存中分配空间。

        String s3 = new String("china");

        //凡是new出来的对象,绝对是不同的两个对象。

        String s4 = new String("china");

 

        System.out.println(s1 == s2);  //true

        System.out.println(s1 == s3);

        System.out.println(s3 == s4);

        System.out.println(s3.equals(s4));

 

        System.out.println("\n-----------------\n");

 

      /*String很特殊,重写从父类继承过来的hashCode方法,使得两个

       *如果字符串里面的内容相等,那么hashCode也相等。

       **/

 

        //hashCode相同

        System.out.println(s3.hashCode());  //hashCode为94631255

        System.out.println(s4.hashCode());  //hashCode为94631255

 

        //identityHashCode方法用于获取原始的hashCode

        //如果原始的hashCode不同,表明确实是不同的对象

 

        //原始hashCode不同

        System.out.println(System.identityHashCode(s3)); //2104928456

        System.out.println(System.identityHashCode(s4)); //2034442961

 

        System.out.println("\n-----------------\n");

 

        //hashCode相同

        System.out.println(s1.hashCode());  //94631255

        System.out.println(s2.hashCode());  //94631255

 

        //原始hashCode相同:表明确实是同一个对象

        System.out.println(System.identityHashCode(s1));  //648217993

        System.out.println(System.identityHashCode(s2));  //648217993

 

    }

 

}

六、链式堆栈:

链式存储结构的堆栈称作链式堆栈

与单链表相同,链式堆栈也是由一个个结点组成的,每个结点由两个域组成,一个是存放数据元素的数据元素域data,另一个是存放指向下一个结点的对象引用(即指针)域next。

堆栈有两端,插入数据元素和删除数据元素的一端为栈顶,另一端为栈底。链式堆栈都设计成把靠近堆栈头head的一端定义为栈顶

依次向链式堆栈入栈数据元素a0, a1, a2, ..., an-1后,链式堆栈的示意图如下图所示: 

1、设计链式堆栈:

(1)Node.java:结点类


//结点类

public class Node {

 

    Object element; //数据域

    Node next;  //指针域

 

    //头结点的构造方法

    public Node(Node nextval) {

        this.next = nextval;

    }

 

    //非头结点的构造方法

    public Node(Object obj, Node nextval) {

        this.element = obj;

        this.next = nextval;

    }

	

    //获得当前结点的后继结点

    public Node getNext() {

        return this.next;

    }

 

    //获得当前的数据域的值

    public Object getElement() {

        return this.element;

    }

 

    //设置当前结点的指针域

    public void setNext(Node nextval) {

        this.next = nextval;

    }

 

    //设置当前结点的数据域

    public void setElement(Object obj) {

        this.element = obj;

    }

 

    public String toString() {

        return this.element.toString();

    }

}

(2)Stack.java:


//栈接口

public interface Stack {

 

    //入栈

    public void push(Object obj) throws Exception;

 

    //出栈

    public Object pop() throws Exception;

 

    //获得栈顶元素

    public Object getTop() throws Exception;

 

    //判断栈是否为空

    public boolean isEmpty();

}

(3)LinkStack.java:


public class LinkStack implements Stack {

 

    Node head;  //栈顶指针

    int size;  //结点的个数

 

    public LinkStack() {

        head = null;

        size = 0;

    }

 

    @Override

    public Object getTop() throws Exception {

        // TODO Auto-generated method stub

        return head.getElement();

    }

 

    @Override

    public boolean isEmpty() {

        // TODO Auto-generated method stub

        return head == null;

    }

 

    @Override

    public Object pop() throws Exception {

        // TODO Auto-generated method stub

        if (isEmpty()) {

            throw new Exception("栈为空!");

        }

        Object obj = head.getElement();

        head = head.getNext();

        size--;

        return obj;

 

    }

 

    @Override

    public void push(Object obj) throws Exception {

        // TODO Auto-generated method stub

        head = new Node(obj, head);

        size++;

    }

(4)Test.java:测试类


import java.util.Scanner;

 

public class Test {

 

    public static void main(String[] args) throws Exception {

        //SequenceStack stack = new SequenceStack(10);

        LinkStack stack = new LinkStack();

        Scanner in = new Scanner(System.in);

        int temp;

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

            System.out.println("请输入第" + (i + 1) + "个整数:");

            temp = in.nextInt();

            stack.push(temp);

        }

 

        //遍历输出

        while (!stack.isEmpty()) {

            System.out.println(stack.pop());

        }

 

    }

 

}

运行效果:

七、堆栈的应用:

堆栈是各种软件系统中应用最广泛的数据结构之一。括号匹配和表达式计算是编译软件中的基本问题,其软件设计中都需要使用堆栈。

  • 括号匹配问题

  • 表达式计算

1、括号匹配问题:

假设算术表达式中包含圆括号,方括号,和花括号三种类型。使用栈数据结构编写一个算法判断表达式中括号是否正确匹配,并设计一个主函数测试。

比如:

{a+[b+(c*a)/(d-e)]}    正确

([a+b)-(c*e)]+{a+b}    错误,中括号的次序不对

括号匹配有四种情况:

1.左右括号匹配次序不正确

2.右括号多于左括号

3.左括号多于右括号

4.匹配正确

代码实现:


public class Test {

 

    //方法:将字符串转化为字符串数组

    public static String[] expToStringArray(String exp) {

        int n = exp.length();

        String[] arr = new String[n];

        for (int i = 0; i < arr.length; i++) {

            arr[i] = exp.substring(i, i + 1);

        }

        return arr;

    }

 

 

    //方法:括号匹配问题的检测

    public static void signCheck(String exp) throws Exception {

        SequenceStack stack = new SequenceStack();

        String[] arr = Test.expToStringArray(exp);

        for (int i = 0; i < arr.length; i++) {

            if (arr[i].equals("(") || arr[i].equals("[") || arr[i].equals("{")) { //当碰到都是左边的括号的时候,统统压进栈

                stack.push(arr[i]);

            } else if (arr[i].equals(")") && !stack.isEmpty() && stack.getTop().equals("(")) { //当碰到了右小括号时,如果匹配正确,就将左小括号出栈

                stack.pop();

            } else if (arr[i].equals(")") && !stack.isEmpty() && !stack.getTop().equals("(")) {

                System.out.println("左右括号匹配次序不正确!");

                return;

            } else if (arr[i].equals("]") && !stack.isEmpty() && stack.getTop().equals("[")) {

                stack.pop();

            } else if (arr[i].equals("]") && !stack.isEmpty() && !stack.getTop().equals("[")) {

                System.out.println("左右括号匹配次序不正确!");

                return;

            } else if (arr[i].equals("}") && !stack.isEmpty() && stack.getTop().equals("{")) {

                stack.pop();

            } else if (arr[i].equals("}") && !stack.isEmpty() && !stack.getTop().equals("{")) {

                System.out.println("左右括号匹配次序不正确!");

                return;

            } else if (arr[i].equals(")") || arr[i].equals("]") || arr[i].equals("}") && stack.isEmpty()) {

                System.out.println("右括号多于左括号!");

                return;

            }

 

        }

        if (!stack.isEmpty()) {

            System.out.println("左括号多于右括号!");

        } else {

            System.out.println("括号匹配正确!");

        }

    }

 

 

    public static void main(String[] args) throws Exception {

 

        String str = "([(a+b)-(c*e)]+{a+b}";

        //括号匹配的检测

        Test.signCheck(str);

 

    }

 

}

运行效果:

2、表达式计算:

比如:

3+(6-4/2)*5=23

后缀表达式为:3642/-5*+# (#符号为结束符)

现在要做的是:

使用链式堆栈,设计一个算法计算表达式:A+(B-C/D)*E

代码实现:


public class Test {

 

    //方法:使用链式堆栈,设计一个算法计算表达式

    public static void expCaculate(LinkStack stack) throws Exception {

        char ch; //扫描每次输入的字符。

        int x1, x2, b = 0; //x1,x2:两个操作数 ,b字符的ASCII码

        System.out.println("输入后缀表达式并以#符号结束:");

        while ((ch = (char) (b = System.in.read())) != ‘#‘) {

            //如果是数字,说明是操作数则压入堆栈

            if (Character.isDigit(ch)) {

                stack.push(new Integer(Character.toString(ch)));

            }

            //如果不是数字,说明是运算符

            else {

                x2 = ((Integer) stack.pop()).intValue();

                x1 = ((Integer) stack.pop()).intValue();

                switch (ch) {

                    case ‘+‘:

                        x1 += x2;

                        break;

                    case ‘-‘:

                        x1 -= x2;

                        break;

                    case ‘*‘:

                        x1 *= x2;

                        break;

                    case ‘/‘:

                        if (x2 == 0) {

                            throw new Exception("分母不能为零!");

                        } else {

                            x1 /= x2;

                        }

                        break;

                }

                stack.push(new Integer(x1));

            }

        }

        System.out.println("后缀表达式计算结果是:" + stack.getTop());

    }

 

    public static void main(String[] args) throws Exception {

 

        LinkStack stack = new LinkStack();

        //(2+3)*(3-1)/2=5的后缀表达式为:23+31-*2/

        //方法:键盘输入后缀表达式,输出的得到计算结果

        Test.expCaculate(stack);

 

    }

 

}

运行效果:

时间: 2024-10-11 16:02:42

栈:顺序栈和链式堆栈的相关文章

数据结构Java实现05----栈:顺序栈和链式堆栈

数据结构Java实现05----栈:顺序栈和链式堆栈 一.堆栈的基本概念: 堆栈(也简称作栈)是一种特殊的线性表,堆栈的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置进行插入和删除操作,而堆栈只允许在固定一端进行插入和删除操作. 先进后出:堆栈中允许进行插入和删除操作的一端称为栈顶,另一端称为栈底.堆栈的插入和删除操作通常称为进栈或入栈,堆栈的删除操作通常称为出栈或退栈. 备注:栈本身就是一个线性表,所以我们之前讨论过线性表的顺序存储和链式存储,对于栈来说,同样适

栈和队列分别的顺序结构和链式结构

栈和队列 栈和队列本身作为特殊的线性表,要记住他俩本身就费劲.难受的是他俩还能分别考虑顺序结构和链式结构,很复杂,容易混淆. 其实比起FILO(先进后出)和FIFO(先进先出)等特点,更重要的是对指针的把握.进出顺序是在逻辑层面的,只要理解就行,难得是如何用指针来表示这种特点,于是我就此方面进行个总结. 顺序栈 虽然栈只需要对一段进行操作(栈顶),但我们除了栈顶指针(top)外,还需要设置栈底指针(base),主要是为了用于判断栈空栈满. s.top == s.base; //当栈顶指针等于栈底

顺序队列和链式队列的实现

队列是一种常用的数据结构,它跟栈一样,操作都受到限制,队列只允许从一端进数据,另一端出数据.队列跟栈不同,栈是一种"后进先出"的模式,而队列是一种"先进先出"的操作模式.就好比日常排队一样,先排队的先出,后排队的后出.例如,进入队列的顺序是1,2,3,4,5则出队列的顺序是1,2,3,4,5(只考虑一次性出列的情况). 队列也分顺序队列和链式队列,跟顺序栈和链表栈一样,顺序队列同样是基于数组实现,链式队列则是基于链表实现. 顺序队列: //顺序队列 #include

Java实现线性表-顺序表示和链式表示

顺序表示和链式表示的比较: 1.读写方式:顺序表可以顺序存取,也可以随机存取:链表只能从表头顺序存取元素: 2.逻辑结构与物理结构:顺序存储时,逻辑上相邻的元素其对应的物理存储位置也相邻:链式存储时,逻辑上相邻的元素,其物理存储位置则不一定相邻: 3.查找.插入和删除操作: 按值查找,当线性表在无序的情况下,两者的时间复杂度均为o(n):而当顺序表有序时,可采用折半查找,此时时间复杂度为o(log n): 按位查找,顺序表支持随机访问,时间复杂度为o(1):而链表的平均时间复杂度为o(n). 顺

栈的顺序结构和链式结构实现

1.栈的顺序存储<数组实现> 1.1.栈的接口 1 package com.neusoft.stack; 2 3 public interface IStack { 4 //1.栈置空 5 public void clear(); 6 //2.栈判空 7 public boolean isEmpty(); 8 //3.栈长度 9 public int length(); 10 //4.取栈顶元素 11 public Object peek(); 12 //5.移除栈顶元素-----出栈 13

C++栈的顺序存储和链式存储的实现

栈是最常见的数据结构,其特点是后进先出(Last In First Out)也是链表的特殊形式,所以和链表一样,有两种存储方式,第一是顺序存储的栈,方便快速读写数据,但是栈的长度必须先固定:第二种是链式存储的栈,可以不用定义栈的长度,可以大量插入数据,如果不是物理内存使用完的话,可以存储大量的数据. 首先,顺序存储的栈的实现,代码如下: #pragma once #define MAXSIZE 10 template<typename EleType> class OrderStack { p

数据结构_线性表的顺序表示和链式表示

/********************************************************************************************************************/ 声明: (1)*.h文件是代码声明, *.cpp文件是代码实现; (2)一般头文件的内容有: ①类型声明; ②函数声明; ③枚举; ④常量; ⑤宏 (3)以下说明是为了方便代码文件的管理而设定的一些规则, 以后代码都会按照此规则编写: 1)Pubuse.h 是几

顺序结构与链式结构

摘抄自,严蔚敏<算法与数据结构> >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

串的数据结构表——顺序串与链式串

串的结构类似与线性表,只不过串的数据元素是一个字符,即是由零个或多个字符组成的有限序列. 一.串的顺序存储 串的顺序存储结构也就是顺序存储,即串中的字符被依次的存在一组连续的存储单元中,可以类比线性表的顺序存储,可以写出其数据结构如下: typedef struct st { char *ch; //串存放的起始地址,串中第i个字符存储在ch[i-1]中 int length; //串的长度 int strsize; //分配的存储空间的大小,如果不足,在通过realloc()分配增加空间 }s