1. 程式人生 > >java中各種演算法和資料結構的使用場景

java中各種演算法和資料結構的使用場景

一。通用資料結構:陣列,連結串列,樹,雜湊表

通用資料結構通過關鍵字的值來儲存並查詢資料,如報表,合同,記錄,業績等資料。通用資料結構可以用速度的快慢來分類,陣列和連結串列是最慢的,樹相對較快,雜湊表是最快的。請注意,並不是最快的就一定是最好的,因為最快的結構的程式在不同程度上比陣列和連結串列的複雜,而且雜湊表要求預先要知道儲存多少資料,資料對儲存空間的利用率也不是非常高。普通的二叉樹對順序的資料來說,會變成緩慢的O(N)級操作,平衡樹雖然避免了這個問題,但程式編寫卻比較困難。 通過資料結構的關係可以用這個圖來表示: 在這裡插入圖片描述 隨著計算機硬體效能的不斷提升,CPU的計算速度也越來越快,所以首選簡單資料結構,除非他們真的表現太慢,否則不要採用複雜的資料結構,只要執行結果在可接受範圍內,沒人會在乎你用了什麼資料結構的。 在操作物件的速度上,java比其他語言有優勢,對於大多數資料結構來說,java只儲存引用而不是實際的物件。在分析演算法時,不是從物件的真實儲存空間出發,因而移動物件的速度也不依賴於物件的大小,只是考慮物件引用的移動,而物件本身的大小就不重要了。在程式語言中,類庫會封裝一些資料結構和演算法,所以在選擇類庫時要確保這個類能真正適應特定的業務場景。 1.陣列

儲存和操作資料時,大多數情況下,陣列是首選的,陣列最有用的情況是資料量較小和資料量的大小事先可預測。陣列元素的刪除總是很慢,因為為了填充空出來的單元,平均半數以上的陣列元素要被移動。在有序陣列中的遍歷是很快的。向量是一種當資料太滿時可以自己擴充空間的陣列,向量可以應用於資料量不可預知的情況下,但是向量在擴充時,要將舊的資料拷貝到一個新的空間,這個過程會造成程式的週期性暫停。 2,連結串列 如果需要儲存的資料量不能預知或需要頻繁的插入刪除資料元素,考慮使用連結串列。當有新的元素加入時,連結串列就開闢新的空間,它甚至可以佔滿所有可用空間,在刪除過程中不會像陣列那樣填補空缺。在一個無序連結串列中,插入很快,查詢和刪除卻很慢,因此,與陣列一樣,連結串列最好也應用於資料量較小的場景。相比之下,連結串列比陣列複雜,但比樹和雜湊表簡單。 3.二叉搜尋樹
當確認陣列和連結串列過慢時,最先考慮的是二叉樹,樹可以提供快速的O(logN)級的插入,查詢,刪除,遍歷的時間複雜度是O(N)級的,對於遍歷一定範圍內的資料可以很快得到訪問資料的最大值和最小值。對於程式,不平衡的二叉樹要比平衡二叉樹簡單的多,但是有序資料能將它的效能降低到O(N)級,並不比連結串列好。如果能保證資料是隨機的,就不需要用平衡二叉樹。 4.平衡樹 紅黑樹和多叉樹都是平衡樹,無論資料是否有序,都能保證效能是O(logN)。對於程式設計來說,最難的是紅黑樹(應用最廣泛)。它們也因用了附加儲存而產生額外耗費,這對系統多少都有些影響。利用樹的類庫可以降低程式設計困難度,某些情況下,選擇雜湊表比平衡樹要好。 5.雜湊表
雜湊表在資料儲存結構中速度最快,雜湊表通常用於拼寫檢查器和作為計算機程式語言中的編譯器的符號表。雜湊表對資料插入的順序並不敏感,因此可以取代平衡樹,而且雜湊表的程式設計比平衡樹簡單多了。雜湊表需要有額外的儲存空間,因為雜湊表用陣列作為基本結構,所以預先必須精確的知道待儲存的資料量。用鏈地址法處理衝突的雜湊表是最好的實現方法。 雜湊表並不能提供任何形式的有序遍歷,或對最大最小值元素進行存取,實現這些功能使用二叉搜尋樹更好一些。

二。專用資料結構:棧,佇列,優先順序佇列

這些結構不是為了使用者可訪問的資料庫而建立的,通常用它們在程式中輔助實現一些演算法。這些結構都不支援查詢或遍歷。 棧,佇列,優先順序佇列都是抽象資料型別(ADT),它們由一些更加基礎的結構如陣列,連結串列,堆組成。這些ADT只提供給使用者簡單的介面,一般只允許使用者插入和訪問或刪除一個數據項(棧:最後被插入的資料;佇列:最先被插入的資料;優先順序佇列:具有最高優先順序的資料)。 1.棧 是一個後進先出的結構,往往通過陣列或連結串列實現,因為最後被插入的資料總是在陣列的最後,這個位置的資料很容易被刪除。棧的溢位有可能會出現,通過合理規劃陣列的大小可以避免,而且棧很少會擁有大量的資料。如果棧擁有大量的資料,並且數量不可精確預測(用棧實現遞迴時),用連結串列比陣列更好一些,因為從表頭的位置刪除或插入一個元素很方便,除非整個記憶體滿了。連結串列比陣列稍慢一些,因為對於插入一個新連結必須分配記憶體,從表中某個連結點上刪除元素後回收分配記憶體也是必需的。 2,佇列 是一個先進先出的結構,同樣可以通過陣列和連結串列實現,陣列需要附加的程式來處理佇列在陣列尾部迴繞的情況。連結串列必須是雙端的,這樣才能從一段插入從另一端刪除。如果知道資料量的多少就可以用陣列,否則用連結串列。 3.優先順序佇列 優先順序佇列用在只對訪問最高優先順序資料項訪問的時候,最高優先順序資料項就是含有最大或最小的關鍵字的項。 可以用有序陣列或堆來實現。向有序陣列中插入是很慢的,但刪除很快。使用堆來實現優先順序佇列,插入和刪除的時間複雜度都是O(logN)級。當插入速度不重要時,可以使用陣列或雙端連結串列,資料量可以被預測時,使用陣列,資料量未知時使用連結串列,如果速度很重要的話,選擇堆更好。

三。排序:插入排序,希爾排序,快速排序,歸併排序,堆排序

隨著計算機的處理能力越來越快,在實際中,選擇資料結構時,可以先嚐試選擇一種較慢但簡單的排序,如插入排序(這裡指資料量小於1000的資料),插入排序如果沒有太多的元素處於亂序的位置,操作的時間複雜度約為O(N)級,通常是在往一個已經排好序的檔案中插入一些新的資料元素的場景下。如果插入排序顯得太慢,可以嘗試希爾排序,根據經驗,資料量在5000以下時很有用。只有當希爾排序變得很慢時,才應該考慮複雜的演算法,如歸併排序,堆排序,快速排序。歸併排序需要輔助儲存空間,堆排序需要有一個堆的資料結構,在某些程度上這兩種都比快速排序慢,所以經常會優先選擇快速排序。但是快速排序在處理非隨機性資料時效能不太好。如果有可能是非隨機性資料,堆排序更好。

四。圖:鄰接矩陣,鄰接表

圖並不儲存通用資料,也不會在其他演算法中成為程式設計師的工具,它們直接模擬現實世界的情況,圖的形狀直接反映了問題的結構。沒有其他資料結構可以取代圖的使用。根據圖的疏密程度,稠密的圖選擇鄰接矩陣,稀疏的圖用鄰接表。鄰接矩陣表示的圖的深度優先搜尋和廣度優先搜尋時間複雜度為O(V*V)級,V是頂點的個數。鄰接表表示的圖的兩種操作時間複雜度是O(V+E)級,E是邊的條數。使用前,先估計圖中的V和E,然後計算哪種表示方法更合適。

五。外部儲存:順序儲存,索引檔案,B-樹,雜湊方法

如果資料量大到記憶體容不下時,只能被儲存到外部儲存空間,一般是磁碟檔案。存在磁碟檔案中具有固定大小單元的資料稱為塊,每個塊都儲存一定數量的記錄(磁碟檔案中的記錄擁有與主存中物件相同的資料型別)。與物件一樣,每條記錄都有一個關鍵字值,通過它可以訪問到這條記錄。假設讀寫操作總是在一個單一的塊中進行,這些讀寫操作比對主存中的資料進行任何操作都要耗時的多,為了提高操作速度必須將磁碟的存取次數減到最小。 1.順序儲存 通過特定的關鍵字進行搜尋的最簡單的方法是隨機儲存記錄然後順序讀取,新的記錄可以被簡單的插入到檔案的最後,已刪除的記錄可以標記為已刪除或將記錄移動,同陣列中一樣來填補空缺。 平均來看,查詢和刪除會涉及到讀取半數的塊,所以順序儲存並不快,時間複雜度為O(N)級,但是對於小量資料它仍然是可以選擇的。 2.索引檔案 使用索引檔案時,速度會明顯的提高。在這種方法中,關鍵字的索引和相應塊的號數被存放在記憶體中,通過一個特殊的關鍵字訪問一條記錄時,程式會先向索引詢問,索引提供這個關鍵字的塊號數,然後只需要讀取這一個塊,僅耗費了O(1)級的時間。可以使用不同種類的關鍵字來做多種索引,只要索引數量能在記憶體的儲存範圍之內。通常,索引檔案儲存在磁碟上,只有在需要時才複製進記憶體中。 索引檔案的缺點是必需先建立索引,這有可能會對磁碟上的檔案進行順序讀取,所以建立索引是很慢的。當記錄被加入到檔案中時,索引還需要更新。 3.B-樹 B-樹是多叉樹,通常用於外部儲存,樹中的節點對應於磁碟中的塊。跟其他樹一樣,根據演算法來遍歷樹,在每一層上讀取一個塊。B-樹可以在O(logN)級的時間內進行查詢,插入和刪除,這樣很快,並且對大檔案也很有效。但是程式設計很麻煩。 4.雜湊方法 外部雜湊同索引檔案有相同的存取時間O(1),但它可以對更大的檔案進行操作,如圖選擇外部儲存結構: 在這裡插入圖片描述 5.虛擬記憶體 有時可以不需要程式設計,通過作業系統的虛擬記憶體來解決磁碟存取問題,如果讀取一個大小超過主存的檔案,虛擬記憶體系統會讀取合適主存大小的部分並將其儲存在磁碟上。當訪問檔案的不同部分時,它們會自動從磁碟讀入並放置在記憶體中。可以對整個檔案使用內部儲存的演算法,使它們好像同時都在記憶體中一樣,如果檔案的哪個部分不在記憶體中,也讓作業系統去讀取它們。當然,這個操作比整個檔案在記憶體中的操作要慢的多,但是通過外部儲存演算法一塊一塊的處理檔案的話,速度也是一樣的慢。不要在乎檔案的大小適合放在記憶體中,在虛擬記憶體的幫助下驗證演算法的好壞尤其是對那些比可用記憶體大不了多少的檔案來說,這個解決方案更簡單。