(七)栈的三种实现

目标

  • 使用链表、数组或向量实现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的实现是基于动态可变大小的数组的,所以基于向量实现的栈的性能类似于基于数组实现的性能。

原文地址:https://www.cnblogs.com/datamining-bio/p/9642830.html