1. 程式人生 > >資料結構與演算法 - 棧和佇列

資料結構與演算法 - 棧和佇列

棧(stack)

先進後出,刪除與加入均在棧頂操作

 

棧也稱為堆疊,是一種線性表。

堆疊的特性: 最先放入堆疊中的內容最後被拿出來,最後放入堆疊中的內容最先被拿出來, 被稱為先進後出、後進先出。

堆疊中兩個最重要的操作是PUSH和POP,兩個是相反的操作。

PUSH:在堆疊的頂部加入一 個元素。

POP:在堆疊頂部移去一個元素, 並將堆疊的大小減一。

 

  在成員變數方面,Vector提供了elementData , elementCount, capacityIncrement三個成員變數。其中

         elementData :"Object[]型別的陣列",它儲存了Vector中的元素,可以隨著元素的增加而動態的增長,如果在初始化Vector時沒有指定容器大小,則使用預設大小為10.

 private static final int DEFAULT_SIZE = 10;初始化的值

 protected int elementCount; 棧元素數量(非空元素的長度)

 

/** 
* 使用指定的初始容量和容量增量構造一個空的向量。
*/
 
public Vector(int initialCapacity, int capacityIncrement) { //初始化
      super();
      if (initialCapacity < 0)
          throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
      this.elementData = new Object[initialCapacity];
      this.capacityIncrement = capacityIncrement;
  }

 

 protected int capacityIncrement;擴容增長因子向量的大小大於其容量時,容量自動增加的量。

如果在建立Vector時,指定了capacityIncrement的大小;則,每次當Vector中動態陣列容量增加時>,增加的大小都是capacityIncrement。

如果容量的增量小於等於零,則每次需要增大容量時,向量的容量將增大一倍。

 

容量是最多能夠容納多少元素,而大小是目前容納了多少元素

 

得到最後元素的下標

public synchronized E peek(){
  try{
    return(E)elementData[elementCount-1];  //當前陣列[當前陣列長度-1]   >>得到最後元素的下標
  }catch(IndexoutOfBoundsException e){     
    throw new EmptyStackException(); //得到下標,肯定會丟擲異常
}}

 

出棧

@Suppres swarnings("unchecked")
public synchronized E pop(){
  if(slementCount==0){ //棧元素數量為0,表示空棧
    throw new EmptyStackException(); //空棧異常
}
  final int index = --elementCount; //將來要出棧的非空元素下標,棧數量-1就是下標
                                 棧數量:1 2 3 4
                                  下標:0 1 2 3
  final E obj=(E)elementData[index]; //拿到棧頂元素,讓它等於obj
   elementData[index]=nul1;把棧頂變成null,下次再出棧,非空元素長度減一得到下標,又是有資料的了
  modCount++; //發生改變,進行加一操作
  return obj;
}

 

入棧

public synchronized void addElement(E object){
  if(elementCount==elementData.1ength){//判斷是否棧滿
    growByOne(); //棧滿,擴容一次,長度不定
  } 
  elementData[ elementCount++]=object;
  modCount++;
}

 

private void growByOne(){
  int adding=0; //要新增的數量
  if(capacityIncrelent <=0){     if((adding=elementData.length)==0){ //如果是空棧,要新增元素的話,讓adding=1,增加一個元素 存疑?        adding=1;   } else{      adding = capacityIncrement; //capacityIncrement用它來判斷需要擴容多少
  }
E[] newData=newElementArray(elementData. length +adding);//新建立一個數組,把它的長度擴容成 增加的長度+擴容的
  System. arraycopy(elementData,0, newData,0, elementCount);//拷貝資料
  elementData=newData;
}

 

為什麼每個方法裡都要有全域性變數和區域性變數

安全問題:因為elementData可以會進行入棧和出棧操作,如果直接使用elementData,不能進行邊遍歷邊刪除,所以要使用區域性變數Object[] dumpArray

棧裡面可以有重複的元素

棧的遍歷可以從棧頂也可以從棧底,這個需要根據自己需求,為自己服務

 

棧的經典應用

 

 

 字尾表示式

931  -  3  *  +  10  2  /  +

923  *  +  10  2  /  +

96  +  10  2  /  +

15  10  2  /  +

15  5  +

20

 

中綴表示式 轉 字尾表示式: 數字輸出,運算子進棧,括號匹配出棧,棧頂優先順序低出棧(精髓就是優先順序越高越靠前)

9  + (3 - 1) ×  3  +  10  ÷  2       >>      931  -  3  *  +  10  2  /  +

            

           

 

 

 

 佇列

相對於棧而言,佇列的特性是:先進先出

  • 先排隊的小朋友肯定能先打到飯!

 

佇列的順序儲存

 

 缺點:出隊複雜度高0(n)

   容易假溢位

   容易造成資源浪費

佇列的鏈式儲存及結構模式

佇列的鏈式儲存結構,其實就是線性表的單鏈表,只不過它只能尾進頭出而已

 

 出隊:只需要讓頭指標指向a2,a1就出隊了

 入隊:只需要讓尾指標指向新結點,讓an的next結點指向新進來的結點

 

佇列也分成兩種:

  • 靜態佇列(陣列實現)

  • 動態佇列(連結串列實現)

值得注意的是:往往實現靜態佇列,我們都是做成迴圈佇列

 

做成迴圈佇列的好處是不浪費記憶體資源!

 

類到底採用什麼樣的資料結構

 transient Link<E>voidLink;頭指標

 

public Linkedlist(){初始化
voidLink=new Link<E>(null, null, null); 建立
voidLink. previous = voidLink;
voidLink. next = voidLink; //前後指標都指向自己(自己抱著自己),後面進來的資料到底採用什麼樣的資料結構要看add方法

 

 

 佇列隨機位置插入

public void add (int location, E object){
二分法查詢
link就是準備在這個位置插入一個新結點進來,當前結點

Link<E> previous = link.previous;
Link<E> newLink = new Link<E>(object,previous(previous),link(next));//新結點
previous.next = newLink;
link.previous = newLink;
}

 


 

入隊(只有隊頭的情況下)

private boolean addlastImpl(E object){
  Link<E>oldLast=voidLink. previous; //在沒有任何元素的情況下,void.link.previous等於它自己,也就是oldLast為head
  Link<E> newlink =new Link<E>(object, oldLast(previous指向head), voidlink(next也指向head));
  voidLink. previous =newlink;
  oldLast.next=newlink;
  size++;
  modCount++;
  return true;
}

 

本身就是一個迴圈,頭尾可以選擇,然後按照自己的選擇寫資料結構

 


 

 

出隊

voidlink = head

first = 0

next = 1

privateE removeFirstImpl(){ 
  Link<E>first = voidLink. next; //頭指標指向了第一個元素=first
  if(first = voidlink){ //如果first不等於voidlink,說明是有元素的     Link<E>next = first.next; //讓1=next,然後first.next指向它
   voidLink.next = next;
    next. previous = voidLink;
    size--;
    modCount++;
    return first.data;
  }
  throw new NoSuchElementException();
}

&n