zoukankan      html  css  js  c++  java
  • Java的顺序栈和链式栈

    栈的定义

    栈是限制在表的一段进行插入和删除的运算的线性表,通常能够将插入、删除的一端为栈顶,例外一端称为栈底,当表中没有任何元素的时候称为空栈。

    通常删除(又称“退栈”)叫做弹出pop操作,每次删除的都是栈顶最新的元素;每次插入(又称“进栈”)称为压入push操作。

    当栈满的时候,进行push 操作,会上溢,当空栈的时候进行退栈操作的时称为下溢

    上溢是一种出错的情况,下溢可能是正常的情况处理。

    堆栈的运算是按照后进先出的原则,简称LIFO

    栈的基本运算定义:

    • initStack:构造一个空栈;
    • stackEmpty:判断是否为空;
    • stackFUll:判断是否栈满;
    • push:进栈,将该元素压入栈顶;
    • pop:退栈,弹出栈顶元素,注意非空判断;
    • stackTop:去除栈顶元素,不改变指针。

    做个简单的Stack接口:

    package com.wuwii.utils;
    
    /**
     * 堆栈
     * @author Zhang Kai
     * @version 1.0
     * @since <pre>2017/12/14 22:51</pre>
     */
    public interface Stack<E> {
        /**
         * 进栈
         *
         * @param element 进栈的元素
         */
        void push(E element);
    
        /**
         * 弹出栈顶元素 ,并改变指针
         *
         * @return 栈顶元素
         */
        E pop();
    
        /**
         * 返回栈顶元素 ,不改变指针
         *
         * @return 栈顶元素
         */
        E topElement();
    
        /**
         * 判断是否为空栈
         *
         * @return true为空栈
         */
        Boolean isEmpty();
    
        /**
         * 清空栈
         */
        void clear();
    
    }
    
    
    

    顺序栈

    就是符合LIFO运算规则的顺序线性表。

    package com.wuwii.utils;
    
    /**
     * 顺序栈
     * @author Zhang Kai
     * @version 1.0
     * @since <pre>2017/12/14 23:05</pre>
     */
    public class ArrayStack<E> implements Stack<E> {
        /**
         * 初始化栈的默认大小
         */
        private final int defaultSize = 10;
        /**
         * 栈的集合大小
         */
        private int size;
        /**
         * 栈顶的位置
         */
        private int top;
        /**
         * 元素存储在数组
         */
        private Object[] elements;
    
        /**
         * 初始化默认大小为10 的栈
         */
        public ArrayStack() {
            initStack(defaultSize);
        }
    
        /**
         * 初始化指定大小的栈
         * @param givenSize 指定栈大小
         */
        public ArrayStack(Integer givenSize) {
            initStack(givenSize);
        }
    
        /**
         * 初始化栈
         * @param givenSize 给定的栈大小
         */
        private void initStack(Integer givenSize) {
    		size = givenSize;
            top = 0;
            elements = new Object[size];
        }
    
        /**
         * 清空栈
         */
        @Override
        public void clear() {
            top = 0;
        }
    
        /**
         * 进栈
         * @param element 进栈的元素
         */
        @Override
        public void push(E element) {
            sizeCheckForPush();
            elements[top++] = element;
        }
    
        /**
         * 弹出栈顶元素 ,并改变指针
         * @return 栈顶元素
         */
        @Override
        public E pop() {
            sizeCheckForPop();
            return (E) elements[--top];
        }
        /**
         * 返回栈顶元素 ,不改变指针
         * @return 栈顶元素
         */
        @Override
        public E topElement() {
            sizeCheckForPush();
            return (E) elements[top - 1];
        }
    
        /**
         * 判断是否为空栈
         * @return true为空栈
         */
        @Override
        public Boolean isEmpty() {
            return size == 0;
        }
        
        /**
         * 在进栈的时候检查
         */
        private void sizeCheckForPush() {
            if (top >= size) {
                throw new RuntimeException("Stack overflow");
            }
        }
    
        /**
         * 退栈检查
         */
        private void sizeCheckForPop() {
            if (isEmpty()) {
                throw new RuntimeException("Stack is empty");
            }
        }
    }
    
    
    
    

    链式栈

    符合LIFO运算规则的链式线性表。

    package com.wuwii.utils;
    
    
    /**
     * @author Zhang Kai
     * @version 1.0
     * @since <pre>2017/12/15 12:58</pre>
     */
    public class LinkStack<E> implements Stack<E> {
        /**
         * 链式单元
         */
        private Node<E> top;
    
        /**
         * 初始化链式堆栈
         */
        public LinkStack() {
            initStack();
        }
    
        /**
         * 初始化
         */
        private void initStack() {
            top = null;
        }
    
        /**
         * 存储单元
         */
        private static class Node<E> {
            E element;
            Node<E> next;
            Node(E element, Node<E> next) {
                this.element = element;
                this.next = next;
            }
        }
        /**
         * 进栈
         *
         * @param element 进栈的元素
         */
        @Override
        public void push(E element) {
            top = new Node<E>(element, top);
        }
    
        /**
         * 弹出栈顶元素 ,并改变指针
         *
         * @return 栈顶元素
         */
        @Override
        public E pop() {
            checkEmpty();
            E element = top.element;
            top = top.next;
            return element;
        }
    
        /**
         * 返回栈顶元素 ,不改变指针
         *
         * @return 栈顶元素
         */
        @Override
        public E topElement() {
            checkEmpty();
            return top.element;
        }
    
        /**
         * 判断是否为空栈
         *
         * @return true为空栈
         */
        @Override
        public Boolean isEmpty() {
            return top == null;
        }
    
        /**
         * 清空栈
         */
        @Override
        public void clear() {
            if (isEmpty()) {
                return;
            }
            for (Node<E> x = top; x != null; ) {
                Node<E> next = x.next;
                x.element = null;
                x.next = null;
                x = next;
            }
    		size = 0;
        }
    
        /**
         * 检查链式堆栈是否为空,为空抛出异常
         */
        private void checkEmpty() {
            if (isEmpty()) {
                throw new RuntimeException("LinkStack is empty");
            }
        }
    }
    
    

    首先push 修改新产生的链表节点的next 域并指向栈顶,然后设置top 指向新的链表节点,pop则相反。

    顺序栈和链式栈的比较

    实现链式栈和顺序栈的操作都是需要常数时间,时间复杂度为O(1),主要从空间和时间复杂度考虑。

    顺序栈初始化的时候必须要给定指定大小,当堆栈不满的时候,会造成一部分的空间浪费,链式栈变长,相对节约空间,但是增加了指针域,额外加大了数据结构的开销。

    当需要多个堆栈共享的时候,顺序存储中可以充分的利用顺序栈的单向延伸,将一个数组可以存在两个堆栈里,每个堆栈从各自的栈顶出发延伸,这样减少了空间的浪费。但只有两个为堆栈的空间有相反的需求的时候才能使用。就是最好一个堆栈只能增加,一个只能减少。如果,两个一起增加,可能造成堆栈的溢出。

    如果在多个顺序堆栈共享空间,一个堆栈满了,其他可能没满,需要使用堆栈的LIFO 运算法则,将满的堆栈元素向左或者右进行平移操作,这样会造成大量的数据元素移动,使得时间的开销增大。

    相对来说,使用两个堆栈共享一个空间是比较适宜的存储方式,但是也增加了堆栈溢出的危险。

    由于链式存储结构的不连续性,什么时候需要,就什么时候去存储,不存在溢出的问题,但是增加了结构的开销,总体上来说浪费了空间,但是不需要堆栈共享,

  • 相关阅读:
    DEDECMS点击主栏目默认显示第一个子栏目列表的方法
    Dede 删除文档同时文章中的图片的方法
    DEDECMS点击主栏目默认显示第一个子栏目列表的方法
    Inside SharePoint 2010 (5): Pages and Navigation
    开源中文分词FudanNLP
    Caching And Processing 2TB Mozilla Crash Reports In Memory With Hazelcast
    盛大 牛人 blog
    a phd
    lily project
    使用redis实现trie结构
  • 原文地址:https://www.cnblogs.com/qnight/p/8983136.html
Copyright © 2011-2022 走看看