第3章 表 棧 和 隊列

第3章 表 棧 和 隊列

每一種有意義的程序都將顯示的至少使用一種這樣的數據結構,而棧總是要被間接的使用到

3.1 抽象數據類型

抽象數據類型(ADT)是帶有一組操作的一些對象的集合,抽象數據類型是數學的抽象,在ADT的定義中沒有提到關於這組操作時如何實現的任何解釋。

3.2 ADT

我們將處理形如A0   A1  A2  .... AN-1   的一般的表,我們說這個表的大小爲N 我們將大小爲0的表稱爲空表

我們說Ai 後繼Ai-1 或繼 Ai-1之後並稱Ai-1前驅Ai不定義A0的前驅元和An-1的後繼元

 

3.2.1 表的簡單數組實現

對錶的所有操作可以用數組來實現

將舊數組賦值給新數組就可以實現數組擴容

插入和刪除潛藏着昂貴的開銷,主要是看插入和刪除在什麼地方(線性開銷0N-i)

 

3.2.2 簡單鏈表

爲了避免插入和刪除的線性開銷,我們需要保證表可以不連續存儲,所有可以採用鏈表

鏈表有一系列節點組成,這些節點不必在內存中相連,每一個節點均含有表元素和到包含該元素的後繼元的節點的鏈

查找一個元素需要花費Oi)的時間,

聯表示適合插入和刪除,不需要移動多餘的項

 

3.3 JAVA Collection API中的表

3.3.1 Collection接口

集合collection的概念在Collection接口中得到抽象,它存儲一組類型相同的對象,

Collection接口擴展了Iterablejava.lang)接口,實現Iterable的那些類可以擁有增強for循環

3.3.2 Iterator接口

實現Iteratorjava.util)接口的思路是,通過iterator方法,每一個集合均可創建並返回給客戶一個實現Iterator接口的對象,並將當前位置的概念在對象中存儲下來

Iteratorremove()的主要優點在於Collectionremove()方法必須首先找到要被刪除的項,如果知道要刪除的項的位置,那麼刪除開銷會小很多

 

當直接使用Iterator遍歷時,重要的是要記住一個基本法則, 如果對正在被迭代的集合進行結構上的改變,那麼迭代器就不在合法,這也是更喜歡使用迭代器remove()方法的原因

3.3.3 List接口ArrayListLinkedList

ArrayList的優點在於,對get() set()方法的調用花費常數時間,其缺點是新項的插入和刪除代價昂貴,除非變動的是最後一個元素

LinkedList的優點在於插入和刪除開銷都很小隊已知位置,對添加和刪除都是常數時間,缺點是 它不易做索引,因此對get的調用時昂貴的,除非調用非常靠近表的端點(如果對錶的調用靠近表的尾端,那麼搜索也可以從尾部開始)

 

如果我們通過向表的前段添加一些項來構造一個List

對於LinkedList它運行時間是O(N) 對於ArrayList它的運行時間是O(N2)

 

如果我們是對集合進行遍歷

對於LinkedList它運行時間是 O(N2) 對於ArrayList它的運行時間是O(N)

 

對於搜索而言,ArrayList LinkedList都是低效的,對Collectioncontains()remove()兩個方法均花費線性時間

trimTosize()方法將容量設置爲集合的長度,操作,ArrayList調用該方法可以避免浪費空間

擴容代碼:  int newCapacity = (oldCapacity * 3)/2 + 1;   

3.3.4 例子 remove()方法對LinkedList類的使用

eg. 假如一個表包含 6,5,1,4,2 需要將表中的偶數都刪除

1. 顯然對於ArrayList這幾乎是一個失敗的策略(任意地方刪除都很昂貴)

2. LinkedList暴漏了兩個問題

1get()調用效率不高

2remove()方法效率同樣很低

3. 因此我們應該使用一個迭代器一步一步遍歷該表,但是我們使用的是Collectionremove()方法來刪除一個偶數值,這不是高效的操作,(因爲remove()方法必須再次搜索該項,它花費線性時間)但更糟的是程序會產生異常

4 在迭代器找到一個偶數值時,我們可以使用該迭代器來刪除這個值(對於迭代器而言,只用花費常數時間)

public static void removeEvenver(List<Integer> 1st) {

    Interator<Integer> itr = 1st.iterator();

while (itr.hasNext()) {

if(ist.next() % 2 == 0) {

itr.remove() ;

}

}

}

3.3.5 關於ListIterator接口

ListIterator擴展了ListIterator的功能,previous()  hasPrevious()使得表可以從後面想前遍歷

 

3.4 ArrayList類的實現

爲了和類庫ArrayList類區別 我們編寫的類叫做MyArrayList

實現MyArrayList包括的細節

1. MyArrayList將保持基礎數組,數組的容量,及當前項

2. MyArrayList提供一種機制來改變數組的容量,允許GC回收老數組

3. MyArrayList 提供get() set()的實現

4. MyArrayList 提供基本的方法 size isEmpty clear 還提供remove 以及兩個不同版本的add 如果數組的大小容量都相同,那麼這兩個add()將增加容量

5. MyArrayList將提供一個實現Iterator接口的類,

3.4.2 迭代器java嵌套類和內部類

3.5 LinkedList類的實現

MyLinkedList 設計要求

1. MyLinkedList類本身,它包含到兩端的鏈,表的大小及一些方法

2. Node類,它可以是一個私有的嵌套類,一個節點包含數據以及前一個節點和下一個節點的鏈,還有一些構造方法

3. LinkedListIterator類,該類抽象了位置概念,是一個私有類,並實現接口Iterator

 

3.6 ADT

3.6.1 棧模型

棧是限制插入和刪除只能在一個位置上進行的表,該位置是表的末端,叫做棧的頂(top

當棧爲空的時候進行pop()top()一般別認爲棧ADT中的一個錯誤,

當棧滿時,運行push()是一個實現限制,並不是錯誤。

棧有時又叫做LIFO(後進先出)表

3.6.2 棧實現

由於棧是一個表,因此可以使用任何實現表的方法來實現棧,因爲棧的操作時常數時間操作,

我們給出兩種方法來實現

1. 棧的鏈表實現

使用單鏈表,通過在表的頂端插入來實現push(),通過刪除表頂端元素來實現pop()

top()操作只是獲得頂端元素並返回它的值

2. 棧的數組實現方法

模仿了ArrayListadd()操作

這些操作不僅以常數時間運行,最現代化的計算機把棧操作作爲它的指令的一部分,棧有可能成爲數組之後的最基本的數據結構了

3.6.3應用

1. 平衡符號

編譯器檢查程序語法錯誤,但常常由於缺少一個符號,引起編譯成列出很多錯誤提示,但是並沒有找到真正的錯誤

2 後綴表達式

4.99 1.06*5.99 + 6.99 1.06* 這種記法叫做後賊或者逆波蘭

3. 中綴到後綴的轉換

可以用棧將一個標準形式的表達式(或叫作中綴的表達式轉換成後綴)

4. 方法調用

當調用一個新的方法時,主調例程的所有局部變量需要由系統存儲起來,否則被調用的新方法將會重寫由主調例程的變量所使用的內存,不僅如此,主調例程的當前位置也要儲存,這些變量一般由編譯器指派給機器的寄存器

當存在方法調用的時候,需要儲存的所有重要信息,諸如寄存器的值(對應變量的名字)返回地址(可以從程序的計數器中得到,一般是在一個寄存器中)等都要以抽象的形式存在堆的頂部

遞歸的所有工作都可以由一個棧來完成,而這正是實現遞歸的每一種程序設計語言中實際發生的事情。所存儲的信息或稱爲活動記錄,或叫作棧幀。

3.7隊列ADT

隊列也是表,使用隊列時,插入在一段進行而刪除在另一端進行

3.7.1 隊列模型

隊列的基本操作時入隊enqueue,它是在表的末端(隊尾)插入一個元素,和出隊,它是刪除dequeue並返回在表的開頭(隊頭)的元素

3.7.2 隊列的數組實現

對於隊列而言任何的表的實現都是合法的,隊列通常不是很大

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章