Java容器&数据结构——栈

  “栈”通常指“后进先出”的(LIFO)容器。本文将分别介绍基于LinkedList和不依赖LinkedList的两种实现方法。

  友情提示:这里,我假设读者已经了解泛型,内部类,LinkedList容器以及递归的概念,如果没有,可能会对您的理解造成一点不便。

一、基于LinkedList的实现

  在Java中,LinkedList具有能够实现栈的所有功能的方法,因此可以将LinkedList作为栈使用,实现类可以非常的优雅而简洁:

package com.test.collection;

import java.util.LinkedList;

public class Stack<T> {
    private LinkedList<T> storage = new LinkedList<T>();
    public void push(T v) {
        storage.addFirst(v);
    }
    public T peek() {
        return storage.getFirst();
    }
    public T pop() {
        return storage.removeFirst();
    }
    public boolean empty() {
        return storage.isEmpty();
    }
    public String toString() {
        return storage.toString();
    }
}
View Code

  通过使用泛型,引入了栈在类定义中最简单的可行示例。push()接受T类型对象并压入栈中,而peek()和pop()将返回T类型的对象。区别是前者仅返回栈顶元素,后者返回并移除栈顶元素。

  如果你只需要栈的行为,这里使用继承就不合适了,因为这样会产生具有LinkedList其他所有方法的类(据说,Java1.0设计者在设计Java.util.Stack时,就犯了这个错误)。因此尽管已经有了java.util.Stack,但是LinkedList可以产生更好的Stack,下面的测试类中同时测试自己实现的Stack和Java.util.Stack里的Stack

package com.test.collection;

public class StackCollision {

    public static void main(String[] args) {
        com.test.collection.Stack<String> stack = new com.test.collection.Stack<String>();
        for(String s : "My dog has fleats".split(" "))
            stack.push(s);
        while(!stack.empty())
            System.out.print(stack.pop() + " ");
        System.out.println();
        java.util.Stack<String> stack2 = new java.util.Stack<String>();
        for(String s : "My dog has fleats".split(" "))
            stack2.push(s);
        while(!stack2.empty())
            System.out.print(stack2.pop() + " ");
    }

}
View Code

测试结果:

这里创建Stack实例时,需要完整地指定包名,否则有可能与java.util包中的Stack产生冲突。这两个Stack具有相同的接口,但是java.util中没有任何公共的Srack接口。

 

二、不依赖于LinkedList的实现

  如上面的例子所示,LinkedList本身已经具备了创建堆栈所必须的方法。我们只需要将LinkedList已有的方法进行封装,就可以完成一个堆栈的实现了。但是如果我觉得这样不利于我深入理解堆栈的实现,不想调用LinkedList的方法,想自己从头实现,该怎么办呢?下面的例子或许可以给你提供一点思路:

 1 package com.test.generic;
 2 
 3 public class LinkedStack<T> {
 4     private class Node<U> {
 5         U item;
 6         Node<U> next;
 7         Node() {
 8             item = null;
 9             next = null;
10         }
11         Node(U item, Node<U> next) {
12             this.item = item;
13             this.next = next;
14         }
15         boolean end() {
16             return item == null && next == null;
17         }
18     }
19     private Node<T> top = new Node<T>();//End sentiel 末端哨兵
20     public void push(T item) {
21         top = new Node<T>(item, top);
22     }
23     public T pop() {
24         T result = top.item;
25         //item和next(节点参数)任意一个非null,则查找下一个节点,若item和next
26         //的值均为null,则不再查找下一个节点,直接返回null的result = top.item
27         if(!top.end())
28             top = top.next;
29         return result;
30     }
31     
32     public static void main(String[] args) {
33         LinkedStack<String> lss = new LinkedStack<String>();
34         for(String s : "Phasers on stun!".split(" "))
35             lss.push(s);
36         String s;
37         while((s = lss.pop()) != null)
38             System.out.println(s);
39     }
40 }
View Code

 

  上例使用了一个带泛型参数的内部类Node<U>作为堆栈节点,参数item为泛型,用于接收节点上各种类型的对象或者基本类型;类型为next作为节点参数,接收下一个节点的信息;top为末端哨兵(end sential)的一个例子,与前两个参数配合,实现判断堆栈何时为空。这个末端哨兵实在构造LinkedStack时创建的,然后,每调用一次push()方法,就会创建一个Node<T>对象。调用pop()方法时,总是返回top.item,然后舍弃当前top所指的Node<T>,并将top转移到下一个Node<T>,碰到末端哨兵,就不在移动top,因为如果到了末端,再继续调用pop方法,只能得到null,说明堆栈已经空了。

  输出结果中我们可以看到,被顺序装入的字符串"Phasers","on","stun!"被倒序的输出,符合堆栈“后进先出”(LIFO)的特性。

  最后,感谢Bruce Eckel提供的例子。

posted @ 2017-03-02 12:59  night_joe  阅读(1317)  评论(0编辑  收藏  举报