zoukankan      html  css  js  c++  java
  • (七)栈的三种实现

    目标

    • 使用链表、数组或向量实现ADT栈
    • 对不同的实现方式及其性能进行对比

    6.1 链式实现

      ADT栈中每个操作push、pop、peek都会涉及栈顶,使用链式,将头结点指向的第一个元素作为栈顶,链的最后一个元素作为栈底。

    的框架

      有一个数据域topNode,为节点链的头引用,构造时默认为null,实现类包含Node类,每个节点都为Node类的实例对象。

      链式实现ADT栈的类框架

    public class LinkedStack<T> implements StackInterface<T> {
      private Node topNode;
      public LinkedStack() {
        topNode = null;
      } // end default constructor
      @Override
      public void push(T newEntry) {
        // TODO Auto-generated method stub
      } // end push
      @Override
      public T pop() {
        // TODO Auto-generated method stub
        return null;
      } // end pop
      @Override
      public T peek() {
        // TODO Auto-generated method stub
        return null;
      } // end peek
      @Override
      public boolean isEmpty() {
        // TODO Auto-generated method stub
        return false;
      } // end isEmpty
      @Override
      public void clear() {
        // TODO Auto-generated method stub
      } // end clear
      private class Node{
        private T data;       // Entry in stack
        private Node next;    // Link to next node
        public Node(T data, Node next) {
          super();
          this.data = data;
          this.next = next;
        } // end constructor
        public T getData() {
          return data;
        } // end getData
        public void setData(T data) {
          this.data = data;
        } // end setData
        public Node getNextNode() {
          return next;
        } // end getNextNode
        public void setNextNode(Node next) {
          this.next = next;
        } // end setNextNode
      } // end Node
    } // end LinkedStack

    栈顶添加  O(1)

      即在链表中添加一个项到链表头

    /**
     * Adds a new entry to the top of this stack.
     * @param newEntry: An object to be added to the stack.
     */
    @Override    // O(1)
    public void push(T newEntry) {
      topNode = new Node(newEntry, topNode);
    } // end push

    获取栈顶 O(1)

      返回头结点指向的节点的数据域,栈为空抛出异常

    /**
     * Retrieves this stack's top entry.
     * @return: The object at the top of the stack.
     * @throws: EmptyStackException if the stack is empty.
     */
    @Override
    public T peek() {
      if (isEmpty()) {
        throw new EmptyStackException();
      }
      else {
        return topNode.getData();
      } // end if
    } // end peek
    /**
     * Detects whether this stack is empty.
     * @return: True if the stack is empty.
     */
    @Override
    public boolean isEmpty() {
      return topNode == null;
    } // end isEmpty

      将异常抽出为私有方法

    public T peek() {
      checkStactEmpty();
      return topNode.getData();
    } // end peek
    private void checkStactEmpty() {
      if (isEmpty()) {
        throw new EmptyStackException();
      } // end if
    } // end checkStackEmpty

    删除栈顶  O(1) 

      即删除头结点指向的第一个节点,处理栈为空的异常

    /**
     * Romoves and returns this stack's top entry.
     * @return: The object at the top of the stack.
     * @throws: EmptyStackException if the stack is empty before the operation.
     */
    @Override
    public T pop() {
      T top = peek();  // Might throw EmptyStackException
      assert topNode != null;
      topNode = topNode.getNextNode();
      return top;
    } // end pop

    其他方法

    public boolean isEmpty() {
      return topNode == null;
    } // end isEmpty
    public void clear() {
      topNode = null;
    } // end clear

    总:

    import java.util.EmptyStackException;
    
    /**
     * A class of stacks whose entries are stored in a chain of nodes.
     * @author Administrator
     *
     * @param <T>
     */
    public class LinkedStack<T> implements StackInterface<T> {
        private Node topNode;
        //private int numberOfEntries;
        
        public LinkedStack() {
            topNode = null;
        } // end default constructor
    
        /**
         * Adds a new entry to the top of this stack.
         * @param newEntry: An object to be added to the stack.
         */
        @Override    // O(1)
        public void push(T newEntry) {
            topNode = new Node(newEntry, topNode);
            //numberOfEntries++;
        } // end push
    
        /**
         * Romoves and returns this stack's top entry.
         * @return: The object at the top of the stack.
         * @throws: EmptyStackException if the stack is empty before the operation.
         */
        @Override
        public T pop() {
            T top = peek();  // Might throw EmptyStackException
            assert topNode != null;
            topNode = topNode.getNextNode();
            //numberOfEntries--;
            return top;
        } // end pop
    
        /**
         * Retrieves this stack's top entry.
         * @return: The object at the top of the stack.
         * @throws: EmptyStackException if the stack is empty.
         */
        @Override
        public T peek() {
            checkStactEmpty();
            return topNode.getData();    
        } // end peek
    
        private void checkStactEmpty() {
            if (isEmpty()) {
                throw new EmptyStackException();
            } // end if
        } // end checkStackEmpty
        
        /**
         * Detects whether this stack is empty.
         * @return: True if the stack is empty.
         */
        @Override
        public boolean isEmpty() {
            //return numberOfEntries == 0;
            return topNode == null;
        } // end isEmpty
    
        /*
        public int getCurrentSize() {
            return numberOfEntries;
        }
        */
        
        /**
         * Removes all entries from this stack.
         */
        @Override
        public void clear() {
            topNode = null;
        } // end clear
        
        private class Node{
            private T data;       // Entry in stack
            private Node next;    // Link to next node
            public Node(T data, Node next) {
                super();
                this.data = data;
                this.next = next;
            } // end constructor
            public T getData() {
                return data;
            } // end getData
            public void setData(T data) {
                this.data = data;
            } // end setData
            public Node getNextNode() {
                return next;
            } // end getNextNode
            public void setNextNode(Node next) {
                this.next = next;
            } // end setNextNode
            
        } // end Node
    } // end LinkedStack
    View Code

    6.2 基于数组的实现 

      数组第一个位置为栈底,最后一个为栈顶

    的框架

    public class ArrayStack<T> implements StackInterface<T> {
      private T[] stack;      // Array of stack entries
      private int topIndex;   // Index of top entry
      private boolean initialized = false;
      private static final int DEFAULT_CAPACITY = 50;
      private static final int MAX_CAPACITY = 10000;
      public ArrayStack() {
        this(DEFAULT_CAPACITY);
      } // end default constructor
      public ArrayStack(int initialCapacity) {
        checkCapacity(initialCapacity);
        @SuppressWarnings("unchecked")
        T[] tempStack = (T[])new Object[initialCapacity];
        stack = tempStack;
        topIndex = -1;
        initialized = true;
      } // end ArrayStack
      private void checkCapacity(int capacity) {
        if (capacity > MAX_CAPACITY) {
          throw new IllegalStateException("Attempt to create a stack whose "
                        + "capacity exceeds allowed maximum.");
        } // end if
      } // end checkCapacity
      @Override
      public void push(T newEntry) {
        // TODO Auto-generated method stub
      }
      @Override
      public T pop() {
        // TODO Auto-generated method stub
        return null;
      }
      @Override
      public T peek() {
        // TODO Auto-generated method stub
        return null;
      }
      @Override
      public boolean isEmpty() {
        // TODO Auto-generated method stub
      return false;
      }
      @Override
      public void clear() {
        // TODO Auto-generated method stub
      }
    } // end ArrayStack

    栈顶添加

      不需要改变数组大小时,为O(1),改变数组大小的操作是O(n)的,所以数组满时,pushO(n)。下一次又为O(1),公平起见,所有push操作应均摊偶尔的数组调整改变的开销。即将数组倍增的开销分摊到栈的所有入栈操作上。除非必须多次地调整数组大小,否则每次push几乎都是O(1)的。

    public void push(T newEntry) {
      checkInitialization();
      ensureCapacity();
      stack[topIndex + 1] = newEntry;
      topIndex++;
    } // end push
    private void ensureCapacity() {
      if (topIndex == stack.length - 1) {  // If array is full, double its size
        int newLength = stack.length * 2;
        checkCapacity(newLength);
        stack = Arrays.copyOf(stack, newLength);
      } // end if
    } // end ensureCapacity
    // Throws an exception if this ovject is not initialized.
    private void checkInitialization() {
      if(!initialized) {
        throw new SecurityException("ArrayStack object is not initialized properly.");
      }
    } // end checkInitialized
    private void checkCapacity(int capacity) {
      if (capacity > MAX_CAPACITY) {
        throw new IllegalStateException("Attempt to create a stack whose "
                      + "capacity exceeds allowed maximum.");
      } // end if
    } // end checkCapacity

    获取栈顶  O(1)

      注意栈空抛出异常

    public T peek() {
      checkInitialization();
      checkStackEmpty();
      return stack[topIndex];
    } // end peek
    public boolean isEmpty() {
      return topIndex < 0;
    } // isEmpty
    private void checkStackEmpty() {
      if (isEmpty()) {
        throw new EmptyStackException();
      } // end if
    } // end checkStackEmpty

    删除栈顶  O(1)

      注意栈空抛异常

    public T pop() {
      T result = peek();
      stack[topIndex] = null;
      topIndex--;
      return result;
    } // end pop

    isEmpty和clear方法

    public boolean isEmpty() {
      return topIndex < 0;
    } // isEmpty
    public void clear() {
      while (!isEmpty()) {
        pop();
      } // end while
    } // end clear

    数组实现:

    涉及到数组数据的方法均需要检查初始化是否成功

    peek,pop注意检查是否为空,为空则抛异常

    import java.util.Arrays;
    
    public class ArrayStack<T> implements StackInterface<T> {
        private T[] stack;      // Array of stack entries
        private int topIndex;   // Index of top entry
        private boolean initialized = false;
        private static final int DEFAULT_CAPACITY = 50;
        private static final int MAX_CAPACITY = 10000;
        
        public ArrayStack() {
            this(DEFAULT_CAPACITY);
        } // end default constructor
        
        public ArrayStack(int initialCapacity) {
            checkCapacity(initialCapacity);
            @SuppressWarnings("unchecked")
            T[] tempStack = (T[])new Object[initialCapacity];
            stack = tempStack;
            topIndex = -1;
            initialized = true;
        } // end ArrayStack
    
        private void checkCapacity(int capacity) {
            if (capacity > MAX_CAPACITY) {
                throw new IllegalStateException("Attempt to create a stack whose "
                        + "capacity exceeds allowed maximum.");
            } // end if
        } // end checkCapacity
    
        /**
         * Adds a new entry to the top of this stack.
         * @param newEntry: An object to be added to the stack.
         */
        @Override
        public void push(T newEntry) {
            checkInitialization();
            ensureCapacity();
            stack[topIndex + 1] = newEntry;
            topIndex++;
        } // end push
    
        
        private void ensureCapacity() {
            if (topIndex == stack.length - 1) {  // If array is full, double its size
                int newLength = stack.length * 2;
                checkCapacity(newLength);
                stack = Arrays.copyOf(stack, newLength);
            } // end if
        } // end ensureCapacity
    
        /**
         * Romoves and returns this stack's top entry.
         * @return: The object at the top of the stack.
         * @throws: EmptyStackException if the stack is empty before the operation.
         */
        @Override
        public T pop() {
            T result = peek();
            stack[topIndex] = null;
            topIndex--;
            return result;
        } // end pop
    
        /**
         * Retrieves this stack's top entry.
         * @return: The object at the top of the stack.
         * @throws: EmptyStackException if the stack is empty.
         */
        @Override
        public T peek() {
            checkInitialization();
            checkStackEmpty();
            return stack[topIndex];
        } // end peek
    
        @Override
        public boolean isEmpty() {
            return topIndex < 0;
        } // isEmpty
        
        private void checkStackEmpty() {
            if (isEmpty()) {
                throw new EmptyStackException();
            } // end if
        } // end checkStackEmpty
    
        // Throws an exception if this ovject is not initialized.
        private void checkInitialization() {
            if(!initialized) {
                throw new SecurityException("ArrayStack object is not initialized properly.");
            }
        } // end checkInitialized
        
        /**
         * Removes all entries from this stack.
         */
        @Override
        public void clear() {
            while (!isEmpty()) {
                pop();
            } // end while
        } // end clear
    } // end ArrayStack
    View Code

    6.3 基于向量的实现

      让栈按需增大的一种办法是将元素保存在可变大小的数组中,另一种办法是使用向量(vector)替代数组。向量是一个对象,其行为类似于高级数组。与数组中的项一样向量中的项也有下标,且从0开始。与向量不同的是,向量有设置或访问项的方法。可以创建一个指定大小的向量,而且它的大小按需增长,实现细节对客户隐藏。

    6.3.1 Java类库:类Vector

      Java类库中包含类Vector,其实例(称为向量)的行为类似于一个可变大小的数组,下面是实现ADT栈时会用到的Vector的构造方法和方法:

    public Vector()

    创建一个空向量或类似于数组的容器,初始大小为10.当向量需要扩展容量时,容量倍增。

    public Vector(int initialCapacity)

    创建一个带指定初始容量的空向量。当向量需要扩展容量时,容量倍增。

    public boolean add(T newEntry)

    将新元素添加到向量的末尾。数组扩增也是通过数组复制得到的。

    public T remove(int index)

    删除向量中指定下标的项并返回,后续操作和ArrayList相同,将后面的数据向前复制。

    public void clear()

    删除向量中的所有项。将所有项设为null。

    public T lastElement()

    返回向量中的最后一项。

    public boolean isEmpty()

    如果向量为空则返回真。

    public int size()

    返回向量中当前的元素个数。

      注Java类库中的类Vector在Java.util包中。向量类似于可变大小的数组,其元素有从0开始的下标。可以使用类中的方法来处理向量。

    6.3.2 使用向量实现ADT栈

      使用向量保存栈的元素,类似于使用数组保存栈的元素,但更容易。让向量的第一个元素指向栈底元素。所以,向量看起来与之前的数组一样。不需要维护栈顶元素的下标,可以从向量的大小推出这个下标,向量大小很容易得到。

      注:如果使用向量实现栈,则向量的首元应该指向栈底元素。而向量最后的占用位置指向栈顶元素。

    类的框架

      实现栈的类首先要声明一个向量作为数据域,并在构造方法中分配向量。

    import java.util.Vector;
    /**
     * A class of stacks whose entries are stored in a vector.
     * @author Administrator
     *
     */
    public class VectorStack<T> implements StackInterface<T> {
      private Vector<T> stack;    // Last element is the top entry in stack
      private boolean initialized = false;
      private static final int DEFAULT_CAPACITY = 50;
      private static final int MAX_CAPACITY = 10000;
      public VectorStack() {
        this(DEFAULT_CAPACITY);
      } // end default constructor
      public VectorStack(int initialCapacity) {
        checkCapacity(initialCapacity);
        stack = new Vector<>(initialCapacity);   // Size double as need
        initialized = true;
      } // end constructor
      private void checkCapacity(int capacity) {
        if (capacity > MAX_CAPACITY) {
          throw new IllegalStateException("Attempt to create a stack whose "
                          + "capacity exceeds allowed maximum.");
        } // end if
      } // end checkCapacity
    } // end VectorStack

    栈顶添加

      使用类Vector的方法add,可以将元素添加到向量尾,即栈顶。

    public void push(T newEntry) {
      checkInitialization();
      stack.add(newEntry);
    } // end push

    获取栈顶

      使用类Vector的方法lastElement,可以获取栈顶元素

    public T peek() {
      checkInitialization();
      checkStackEmpty();
      return stack.lastElement();
    } // end peek

     删除栈顶

     使用Vector的方法remove,可以删除栈顶元素。方法的参数是向量最后一个元素的下标,因为那个元素是栈顶。下标是向量当前大小stack.size()1.

    public T pop() {
      checkInitialization();
      checkStackEmpty();
      return stack.remove(stack.size() - 1);
    } // end pop

    的其他方法

    public boolean isEmpty() {
      return stack.isEmpty();
    } // end isEmpty
    public void clear() {
      stack.clear();
    } // end clear

      注:VectorStack比写基于数组实现的代码容易,因为调用了Vector的方法,而运行时间要多于ArrayStack方法的运行时间。但是,多出的这些时间常常微不足道。

    小结

    链式实现中栈的操作都是O(1)的;

    调整数组大小可避免栈满时不能容纳新元素。但是数组中往往含有未用的位置;

    基于数组的实现中,栈操作是O(1)的。数组满push为O(n),分摊所有入栈则为O(1);

    因为Vector的实现是基于动态可变大小的数组的,所以基于向量实现的栈的性能类似于基于数组实现的性能。

  • 相关阅读:
    redis之windows安装
    ubuntu安装 ssh
    less乱码ESC显示问题
    linux 命令/目录 名称 英文单词 缩写 助记
    设计模式-策略模式
    设计模式-责任链模式
    ApplicationContexAware的作用
    ApplicationEvent事件机制
    mybatis注解版延迟加载、立即加载、一级缓存、二级缓存
    mybatis xml版延迟加载、立即加载、一级缓存、二级缓存
  • 原文地址:https://www.cnblogs.com/datamining-bio/p/9642830.html
Copyright © 2011-2022 走看看