棧和隊列是數據結構中很是常見和基礎的線性表,在某些場合棧和隊列使用不少,所以本篇主要介紹棧和隊列,並用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; } }
經過源碼能夠看出,類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 } }
除了數組,鏈表也是實現棧的很好的方式,詳情可參考前面一篇「數據結構之鏈表及其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 } }
經過上面數組實現的棧來實現隊列,代碼以下:
/* * 數組實現隊列:利用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 } }
兩個隊列實現棧:除了兩個棧能夠實現隊列之外,反過來,兩個隊列也能夠實現一個棧。定義兩個隊列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 } }
轉載請註明出處 http://www.cnblogs.com/Y-oung/p/8893829.html
工做、學習、交流或有任何疑問,請聯繫郵箱:yy1340128046@163.com 微信:yy1340128046