數據結構之棧和隊列及其Java實現

        棧和隊列是數據結構中很是常見和基礎的線性表,在某些場合棧和隊列使用不少,所以本篇主要介紹棧和隊列,並用Java實現基本的棧和隊列,同時用棧和隊列相互實現。html

        棧:棧是一種基於「後進先出」策略的線性表。在插入時(入棧),最早插入的元素在棧尾,最後插入的元素在棧頂;在刪除時(出棧),最後插入的元素先出棧,最早插入的元素最後出棧。因而可知,對棧的插入和刪除操做都是在棧頂位置進行的。java

        在Java中,提供了一個類Stack<E>來實現棧的這些特性,並提供了一些經常使用的方法來對棧進行操做。數組

        Stack<E>源碼:微信

package java.util;
public
class Stack<E> extends Vector<E> {
    /**
     * Creates an empty Stack.
     */
    public Stack() {
    }
    //入棧
    public E push(E item) {
        addElement(item);
        return item;
    }
    //出棧
    public synchronized E pop() {
        E       obj;
        int     len = size();
        obj = peek();
        removeElementAt(len - 1);
        return obj;
    }
    //查看棧頂元素,但不會出棧
    public synchronized E peek() {
        int     len = size();

        if (len == 0)
            throw new EmptyStackException();
        return elementAt(len - 1);
    }
    //判斷棧是否爲空
    public boolean empty() {
        return size() == 0;
    }
    //在棧中查找某個元素,返回其位置
    public synchronized int search(Object o) {
        int i = lastIndexOf(o);
        if (i >= 0) {
            return size() - i;
        }
        return -1;
    }
}
View Code

        經過源碼能夠看出,類Stack實際上繼承了Vector類,而Vector是一個基於數組的集合類,它裏面的元素都存儲在「protected Object[] elementData;」這個數組裏面,而變量「protected int elementCount;」用於統計數組元素個數。經過上面源碼來看Stack類裏面的push方法,其實是經過Vector類裏面的addElement(item);方法向數組裏添加元素(以下面代碼),添加以前先判斷數組是否是滿了,若是滿了先進行擴容操做,防止發生溢出(數組下標越界)。而後將新元素添加到數組elementData,元素個數+1。數據結構

    public synchronized void addElement(E obj) {  //向數組裏面添加元素
        modCount++;
        ensureCapacityHelper(elementCount + 1);  //當數組填滿時,對數組「擴容」,防止溢出
        elementData[elementCount++] = obj;  //數組添加新元素,元素個數加1
    }

        再來看Stack類裏面的pop方法,先經過peek方法獲取數組最後的元素(len - 1位置),而後經過Vector類裏面的removeElementAt(int index);方法將數組末尾的元素置空(null)並使元素個數-1。將數組末尾元素置空的目的是防止發生對象遊離。Java的垃圾收集策略是回收全部沒法被訪問的對象的內存,而咱們在執行pop操做時,被彈出的元素的引用仍然存在於數組中,但這個元素實際上已是一個「孤兒」了,它永遠都不會被訪問了。但它依然佔着內存,Java的垃圾收集器不知道這一點,這種狀況又稱爲內存泄漏。所以將將數組末尾的元素置空(null)能夠防止這一狀況發生。app

        實際上咱們也能夠經過一個普通的數組來實現棧的操做,下面代碼實現了這一點。obj數組存放棧中的元素,index記錄元素個數。入棧時數組obj從前日後依次添加元素,每添加一個元素,就判斷數組是否滿了,若是滿了,則擴容爲原來的2倍,防止發生溢出(數組下標越界)。擴容時,新建一個容量爲原來兩倍的新數組,將原來數組中的元素逐個複製到新數組中,而後將新數組賦給原來的數組obj,完成數組擴容。出棧時,數組obj從後往前依次取出元素,獲取元素值之後,將數組中出棧元素位置置空(null),避免對象遊離,緣由如上所述。每出棧一次,判斷數組空間的使用率是否小於一半,若是小於一半,則縮小爲原來的一半(縮容),減小空間浪費。縮容操做與擴容操做原理相似,再也不贅述。代碼以下所示:ide

/*
 * 數組實現棧
 */
public class ArrayToStack {

    private Object[] obj;
    private int index;  //元素個數,注意不是數組長度
    
    public ArrayToStack(){
        obj = new Object[4];  //數組初始容量4
        index = 0;
    }
    //入棧,數組obj從前日後依次添加元素
    public void push(Object obj){
        this.obj[index++] = obj;
        if(index == this.obj.length){  //若是數組滿了,則擴容爲原來的2倍,防止溢出
            this.obj = resize(this.obj.length);  //將擴容後的數組賦給obj
        }
    }
    //出棧,數組obj從後往前依次取出元素
    public Object pop(){
        if(index == 0) return null;
        Object data = this.obj[index-1];  //先取出出棧元素
        this.obj[index-1] = null;  //將數組中出棧元素位置置空(null),避免對象遊離
        index--;
        if(index == this.obj.length/2-1){  //若是數組空間使用率小於一半,則縮小爲原來的一半,減小空間浪費
            this.obj = reduce(this.obj.length);  //將縮小後的數組賦給obj
        }
        return data;
    }
    
    public boolean isEmpty(){
        return index == 0;
    }
    //元素個數
    public int size(){
        return index;
    }
    
    public void display(){
        for(int i=0;i<index;i++){
            System.out.print(obj[i]+" ");
        }
        System.out.println();
    }
    //數組擴容
    private final Object[] resize(int size){
        Object[] newobj = new Object[size<<1];  //擴容爲原來的2倍
        for(int i=0;i<index;i++){
            newobj[i] = this.obj[i];
        }
        return newobj;
    }
    //數組縮小(縮容)
    private final Object[] reduce(int size){
        Object[] newobj = new Object[size>>1];  //縮小爲原來的一半
        for(int i=0;i<index;i++){
            newobj[i] = this.obj[i];
        }
        return newobj;
    }
    
    public static void main(String[] args) {
        ArrayToStack stack = new ArrayToStack();
        //入棧
        stack.push(0);
        stack.push(1);
        stack.push(2);
        System.out.println(stack.obj.length);  //4  數組還未擴容
        stack.push(3);                         //入棧順序:0、一、二、3
        System.out.println(stack.obj.length);  //8  數組進行了擴容
        System.out.println(stack.isEmpty());   //false
        System.out.println(stack.size());      //4
        stack.display();                       //0 1 2 3
        //出棧
        System.out.println(stack.pop());       //3
        System.out.println(stack.obj.length);  //4  數組進行了縮容
        System.out.println(stack.pop());       //2
        System.out.println(stack.pop());       //1
        System.out.println(stack.pop());       //0  出棧順序:三、二、一、0
        System.out.println(stack.pop());       //null
        System.out.println(stack.isEmpty());   //true
    }
}
View Code

        除了數組,鏈表也是實現棧的很好的方式,詳情可參考前面一篇「數據結構之鏈表及其Java實現」中,用單向鏈表實現棧。
學習

        隊列:隊列是一種基於「先進先出」策略的線性表。插入操做時(入列),每次都在隊尾插入一個新元素;刪除操做時(出列),每次都在隊頭插入一個新元素。因而可知,隊列的插入操做是在隊尾位置進行的,刪除操做是在隊頭位置進行的。this

        Java提供了一個隊列接口Queue<E>,但這個接口不太經常使用,每每經過其餘方式實現隊列的操做。鏈表是實現隊列的很好的方式,詳情可參考前面一篇「數據結構之鏈表及其Java實現」中,用雙端鏈表實現隊列。除此以外,還能夠經過兩個棧實現隊列。spa

        棧和隊列相互實現

        兩個棧實現隊列:兩個棧實現隊列的原理,可參考前面一篇「劍指offer題目系列二」中「六、用兩個棧實現隊列」。下面提供兩種方式:一種經過Java提供的Stack類來實現隊列,另外一種經過上面數組實現的棧來實現隊列。

        經過Java提供的Stack類來實現隊列,代碼以下:

/*
 * 兩個棧實現隊列
 */
import java.util.Stack;


public class TwoStackToQueue {
    
    private Stack<Object> stack1 = new Stack<Object>();  //存放入列元素
    private Stack<Object> stack2 = new Stack<Object>();  //存放出列元素
    private int size;  //元素數量
    
    public TwoStackToQueue(){
        size = 0;
    }
    
    //入列
    public void appendTail(Object obj){
        stack1.push(obj);  //將新入列的元素存放在stack1
        size++;
    }
    //出列
    public Object deleteHead(){
        if(this.isEmpty()) return null;
        if(stack2.empty()){  //若是stack2不爲空,則直接出列
            while(!stack1.empty()){  //若是stack2爲空,先將stack1中的元素出棧,同時進入stack2
                stack2.push(stack1.pop());
            }
        }
        size--;
        return stack2.pop();  //stack2出棧,完成出列操做
    }
    
    //判斷隊列是否爲空
    public boolean isEmpty(){
        return stack1.empty() && stack2.empty();
    }
    
    public int size(){
        return size;
    }


    public static void main(String[] args) {
        TwoStackToQueue queue = new TwoStackToQueue();
        System.out.println(queue.isEmpty());     //true
        System.out.println(queue.deleteHead());  //null
        //入列
        queue.appendTail(0);
        queue.appendTail(1);
        queue.appendTail(2);
        queue.appendTail(3);
        System.out.println(queue.isEmpty());     //false
        System.out.println(queue.size());        //4
        //出列
        System.out.println(queue.deleteHead());  //0
        System.out.println(queue.deleteHead());  //1
        System.out.println(queue.deleteHead());  //2
        queue.appendTail(1);
        System.out.println(queue.deleteHead());  //3
        System.out.println(queue.deleteHead());  //1
        System.out.println(queue.isEmpty());     //true
        System.out.println(queue.size());        //0
    }
}
View Code

        經過上面數組實現的棧來實現隊列,代碼以下:

/*
 * 數組實現隊列:利用ArrayToStack類中數組實現的棧來間接實現隊列。
 * 直接用數組實現隊列也能,但很繁瑣,用鏈表實現隊列更容易
 */
public class ArrayToQueue {

    private ArrayToStack stack1 = new ArrayToStack();  //存放入列元素
    private ArrayToStack stack2 = new ArrayToStack();  //存放出列元素
    private int size;  //元素數量
    
    public ArrayToQueue(){
        size = 0;
    }
    
    //入列
    public void appendTail(Object obj){
        stack1.push(obj);  //將新入列的元素存放在stack1
        size++;
    }
    //出列
    public Object deleteHead(){
        if(this.isEmpty()) return null;
        if(stack2.isEmpty()){  //若是stack2不爲空,則直接出列
            while(!stack1.isEmpty()){  //若是stack2爲空,先將stack1中的元素出棧,同時進入stack2
                stack2.push(stack1.pop());
            }
        }
        size--;
        return stack2.pop();  //stack2出棧,完成出列操做
    }
    
    //判斷隊列是否爲空
    public boolean isEmpty(){
        return stack1.isEmpty() && stack2.isEmpty();
    }
    
    public int size(){
        return size;
    }


    public static void main(String[] args) {
        TwoStackToQueue queue = new TwoStackToQueue();
        System.out.println(queue.isEmpty());     //true
        System.out.println(queue.deleteHead());  //null
        //入列
        queue.appendTail(0);
        queue.appendTail(1);
        queue.appendTail(2);
        queue.appendTail(3);
        System.out.println(queue.isEmpty());     //false
        System.out.println(queue.size());        //4
        //出列
        System.out.println(queue.deleteHead());  //0
        System.out.println(queue.deleteHead());  //1
        System.out.println(queue.deleteHead());  //2
        queue.appendTail(1);
        System.out.println(queue.deleteHead());  //3
        System.out.println(queue.deleteHead());  //1
        System.out.println(queue.isEmpty());     //true
        System.out.println(queue.size());        //0
    }
}
View Code

        兩個隊列實現棧:除了兩個棧能夠實現隊列之外,反過來,兩個隊列也能夠實現一個棧。定義兩個隊列que一、que2,size變量記錄元素數量。入棧時,將新入棧的元素放入que1;出棧時,que一、que2交替進行:若是que1不爲空,將que1除隊尾最後一個元素外的其他元素出列,放入que2中,而後將que1隊尾最後一個元素返回(出列),完成出棧操做,此時que1爲空;若是que2不爲空,將que2除隊尾最後一個元素外的其他元素出列,放入que1中,而後將que2隊尾最後一個元素返回(出列),完成出棧操做,此時que2爲空。

        下面代碼利用上面「經過Java提供的Stack類來實現隊列」的兩個隊列,來實現棧。

/*
 * 兩個隊列實現棧
 */
public class TwoQueueToStack {

    TwoStackToQueue que1 = new TwoStackToQueue();
    TwoStackToQueue que2 = new TwoStackToQueue();
    private int size;  //元素數量
    
    
    public TwoQueueToStack(){
        size = 0;
    }
    //入棧
    public void pushStack(Object obj){
        que1.appendTail(obj);  //將新入棧的元素放入que1
        size++;
    }
    
    //出棧,出棧時que一、que2交替進行
    public Object popStack(){
        if(this.isEmptyStack()) return null;
        if(!que1.isEmpty()){
            while(que1.size() != 1){  //將que1除隊尾最後一個元素外的其他元素出列,放入que2中
                que2.appendTail(que1.deleteHead());
            }
            size--;
            return que1.deleteHead();  //que1隊尾最後一個元素出列,完成出棧操做,此時que1爲空
        }else{
            while(que2.size() != 1){  //將que2除隊尾最後一個元素外的其他元素出列,放入que1中
                que1.appendTail(que2.deleteHead());
            }
            size--;
            return que2.deleteHead();  //que2隊尾最後一個元素出列,完成出棧操做,此時que2爲空
        }
    }
    
    public boolean isEmptyStack(){
        return que1.size()==0 && que2.size()==0;
    }
    
    public int size(){
        return size;
    }
    
    public static void main(String[] args) {
        TwoQueueToStack stack = new TwoQueueToStack();
        System.out.println(stack.isEmptyStack());  //true
        System.out.println(stack.popStack());      //null
        //入棧
        stack.pushStack(0);
        stack.pushStack(1);
        stack.pushStack(2);
        stack.pushStack(3);
        System.out.println(stack.isEmptyStack());  //false
        System.out.println(stack.size());          //4
        //出棧
        System.out.println(stack.popStack());      //3
        System.out.println(stack.popStack());      //2
        System.out.println(stack.popStack());      //1
        stack.pushStack(2);
        System.out.println(stack.popStack());      //2
        System.out.println(stack.popStack());      //0
        System.out.println(stack.isEmptyStack());  //true
        System.out.println(stack.size());          //0
    }
}
View Code

 

        轉載請註明出處 http://www.cnblogs.com/Y-oung/p/8893829.html

        工做、學習、交流或有任何疑問,請聯繫郵箱:yy1340128046@163.com  微信:yy1340128046

相關文章
相關標籤/搜索