棧(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();
}