原文:http://www.cnblogs.com/people/category/479839.htmlhtml
數據結構是對計算機內存中(有時在磁盤中)的數據的一種安排。數據結構是存放數據物理結構在邏輯上的形式體現,常見的數據結構有數組、鏈表、棧、二叉樹、哈希表等等。算法對這些結構中的數據進行各類處理,例如,查找一條特殊的數據項或對數據進行排序。數據結構和處理技術(即算法)能夠解決以下問題算法
現實世界中有不少信息,有些信息是強相關的,好比一我的的身高、體重、年齡等等,這些信息不是隨便放的,就像你不會把廚房裏的鍋放在臥室裏面,鏟子放到衛生間裏面,咱們須要一個統一地方存放這些信息,在物理上就是放在存儲空間裏面,好比硬盤或內存,在邏輯形式上就是上面提到的各類數據結構,好比數組、鏈表等等。數據庫
一些數據具備很強的實用性,就是與相應的事件對應起來,好比隊列能夠模擬顧客在銀行中排隊等待。編程
數據結構與算法就是討論這些數據結構的實現以及在數據結構上進行一些操做。
下面列出了一些數據結構的描述
數據結構 優勢 缺點
數組 插入塊,若是知道下標,能夠很是快地存取 查找慢,刪除慢,大小固定
有序數組 比無序的數組查找快 刪除和插入慢,大小固定
棧 提供後進先出方式的存取 存取其餘項很慢
隊列 提供先進先出方式的存取 存取其餘項很慢
鏈表 插入快,刪除快 查找慢
二叉樹 查找、插入、刪除都快(若是樹保持平衡) 刪除算法複雜
紅-黑樹 查找、插入、刪除都快,樹老是平衡的 算法複雜
2-3-4樹 查找、插入、刪除都快,樹老是平衡的,樹對磁盤存儲有用 算法複雜
哈希表 若是關鍵字已知則存取極快,插入塊 刪除慢,若是不知道關鍵字則存取很慢,對存儲空間使用不充分
堆 插入、刪除快,對最大數據項的存取很快 對其餘數據項存取慢
圖 對現實世界建模 有些算法慢且複雜數組
數據結構除了數組以外均可以被認爲是抽象數據結構(ADT),主要是數據的存儲物理結構與邏輯結構並不是拓撲結構上一致。數據結構
許多將要討論到的算法直接適用於某些特殊的數據結構,對大多數數據結構來講,都須要實現如下功能
1.插入一條新的數據項
2.查找某一特定的數據項
3.刪除某一特定的數據項
4.給數據結構裏的數據排序
5.其餘數據結構和算法
面向對象編程語言的產生是因爲發現過程性語言在處理大小的複雜問題時有些力不從心。具體是哪些問題呢?
有兩類問題:一是程序與現實世界缺少對應關係,二是程序內部的結果出現了問題。
對現實世界建模的無能爲力
使用過程語言對現實世界問題進行抽象即概念化十分困難:方法執行任務,而數據存儲信息,可是現實世界中的事物是對兩者同時進行操做。
粗糙的組織結構
解決程序的內部組織結構是一個更微妙並且事關重大的問題。面向過程的程序被劃分爲一個個方法,這種基於方法組織形式的一個巨大問題是它僅僅考慮了方法,而沒有重視數據。當不得不面對數據時,它沒有太多的選擇。簡而言之,數據能夠是一個特定的方法的局部量,也能夠是全部方法均可以存錢的全局量,就是沒法規定一個變量只容許某些方法存取而不容許另外一些方法存取。編程語言
Java.util包中含有諸如向量(一個可擴充的數組)、棧、庫和哈希表等類型的數據結構。這些數據結構已經被實現並提供了相關操做方法。可是咱們仍然須要學習別的數據結構,提供的數據結構是不夠的。post
通用數據結構:數組、鏈表、樹、哈希表
專用數據結構:棧、隊列、優先級隊列
排序:插入排序、希爾排序、快速排序、歸併排序、堆排序
圖:鄰接矩陣、鄰接表
外部存儲:順序存儲、索引文件、B-樹、哈希方法性能
若想存儲真實世界中的相似人事記錄、存貨目錄、合同表或銷售業績等數據,則只須要通常用途的數據結構。在本書中屬於這種類型的結構有數組、鏈表、樹和哈希表。他們被稱之爲通用的數據結構是由於它們經過關鍵字的值來存儲並查找數據,這一點在通用數據庫程序中常見到(棧等特殊結構正好相反,它們只容許存取必定的數據項)。
通用數據結構能夠徹底按照速度的快慢來分類:數組和鏈表是最慢的,樹相對較快,哈希表是最快的。
可是不要有這樣的結論:使用最快的結構永遠是最好的方案。這些最快的結構也有缺陷。首先,它們的程序在不一樣程度上比數組和鏈表的複雜;其次,哈希表要求預先知道要存儲多少數據,數據對存儲空間的利用率也不是很是高。普通的二叉樹對順序的數據來講,會變成緩慢的O(N)級操做,而平衡樹雖然避免了上述的問題,可是它的程序編制起來卻比較困難。
處理器速度因素
快速的結構都有缺陷,而計算機的另外一個發展因素卻能使低速的結構更加具備吸引力。新計算機的CPU和存取速度每年都有提高。Moore定律聲明瞭CPU的速度每18個月翻一倍。這形成了早期計算機和現代應用的計算機在性能方面的驚人差別,並且目前沒有任何理由能忍我這個增加速度會減慢。
幾年前一臺電腦能夠在一個可接受的時間內處理100個對象的數組,而如今的計算機則快多了,所以能夠在一樣的時間裏處理含有10000個對象的數組。
請從簡單數據結構入手考慮:除非它們明顯是太慢了,不然就用數組或鏈表編寫程序,看看結構究竟怎樣。若是能在一個可接受的時間內運行完畢,那麼就採用它,沒必要再找別的。沒有人會留意用的是數組或別的什麼結構,爲何必定要拼命地寫出一個平衡樹的算法?甚至必須面對成千上萬、百萬的數據項進行操做時,不妨先看一看數組或鏈表處理表現的狀況,這也仍是值得的。只有在實驗中發現這些簡單結構的性能太慢時,纔回過頭來採用哪些更加複雜的數據結構。
Java引用的優勢
在操做對象的速度上,Java與其餘語言相比有極大的優點,那是因爲對於大多數數據結構來講,Java數據結構只存儲引用而不是實際的對象,所以相對於那些在數據結構中實際爲對象開闢了空間的語言來講,大多數Java算法的執行速度更快。在分析算法時,不是從對象的真實存儲空間出發,於是移動對象的速度也不依賴於對象的大小,而只是考慮對象引用的移動,所以對象自己的大小就不重要了。
當存儲和操做數據時,在大多數狀況下數組是首先應該考慮的結構,數組在下列狀況下頗有用:
數據量較小
數據量的大小事先可預測
若是存儲空間足夠大的話,能夠放鬆第二條,建立一個足夠大的數組來應付全部能夠預見的數據輸入。
若是插入速度很重要的話,使用無序數組,若是查找速度很重要的話,使用有序數組,並用二分查找。數組元素的刪除老是很慢,這是因爲將來填充空出來的單元,平均半數以上的數組元素要被移動,在有序數組中的遍歷是很快的,而在無序數組不支持這種功能。向量類是一種當數據太滿時能夠本身擴展空間的數組,向量能夠應用於數據量不可預知的狀況下,然而,在向量擴充時,要將舊的數據拷入一個新的空間中,這一過程會形成程序明顯的週期性暫停。
若是須要存儲的數據量不能預知或者須要頻繁地插入刪除數據元素時,考慮使用鏈表。當有新的元素加入時,鏈表就開闢新的所須要的空間,因此它甚至能夠佔滿所有可用的內存;在刪除過程當中,不必像數組那樣填補"空白"
在一個無序的鏈表中,插入是至關快的,查找和刪除卻很慢,所以,與數組同樣,鏈表最好也應用於數據量相對較小的狀況。
對於編程而言,鏈表比數組複雜,但它比樹或哈希表簡單。
當確認數組和鏈表過慢時,二叉搜索樹是最早應該考慮的結果,樹能夠提供快速的O(logN)級的插入、查找和刪除。遍歷的時間複雜度是O(N)級的,這是任何數據結構遍歷的最大值(根據定義,必須訪問全部的數據)。對於遍歷必定範圍內的數據能夠很快得出訪問數據的最大值或最小值。
對於程序來講,不平衡的二叉搜索樹要比平衡的二叉樹簡單得多,但不幸的是,有序數據能將它的性能降至O(N)級,不比一個鏈表好多歲,然而若是能夠保證數據是隨機進入的,就不須要用平衡二叉樹。
在衆多平衡樹中,咱們討論了紅黑樹和2-3-4樹,它們都是平衡樹,而且不管輸入數據是否有序,它們都能保證性能爲O(logN)。然而對於實現來講,平衡樹是很複雜的,特別是紅黑樹。若是利用樹的商用類,能夠下降複雜性。
哈希表在數據存儲結構中速度最快,這使它成爲計算機而不是人與數據交互時的必需。哈希表一般用於拼寫檢查器和做爲計算機語言編譯器的符號表,這些應用中,程序必須在幾分之一秒的時間裏檢查上千的詞或符號。
哈希表對插入的順序並不敏感,所以能夠取代平衡樹。可是哈希表的編程比平衡樹簡單多了。
哈希表須要有額外的存儲空間,尤爲是對於開放地址法,由於哈希表用數組做爲基本結構,因此,必須預先精確地知道待存儲的數據量。
用鏈地址法處理衝突的哈希表是最健壯的實現方法,若能預先精確地知道數據量,在這種狀況下,用開放地址法編程最簡單,由於不須要用到鏈表類。
哈希表並不能提供任何形式的有序遍歷,或對最大值、最小值進行存取,若是這些功能很重要,使用二叉樹更好些。
通用數據存儲結構的比較
專用數據結構有棧、隊列和優先級隊列。這些結構不是爲了用戶可訪問的數據庫而創建的,一般用它們在程序中輔助實現一些算法。
棧、隊列和優先級隊列是抽象數據類型,它們由一些更加基礎的數據結構如數組、鏈表或堆來實現。這些ADT只提供給用戶間的的接口,通常僅容許插入和訪問或者刪除一個數據項。這些數據項是:
對於棧:最後被插入的數據項
對於隊列:最早被插入的數據項
對於優先級隊列:具備最高優先級的數據項
棧用在最後被插入數據項訪問的時候,它是一個後進先出的結構。
棧每每經過數組或鏈表實現,經過數組實現頗有效率,由於最後被插入的數據老是在數組的最後,這個位置的數據很容易被刪除。棧的溢出有可能出現,但當數組的大小被合理規劃後,溢出並不常見,由於棧不多會擁有大量的數據。
若是棧擁有許多數據,而且數量不可精確預測(當用棧實現遞歸時),用鏈表比數組更好一些,這是因爲從表頭的位置刪除或插入一個元素很方便,除非整個內存滿了,棧的溢出不可能出現。鏈表比數組稍慢一些,由於對於插入一個新鏈結必須分配內存,從表中某個鏈結點上刪除元素後回收分配內存亦是必須的。
隊列用在只對最早被插入數據項訪問的時候,它是一個先進先出的結構。
同棧相比,隊列一樣能夠經過數組和鏈表實現,這兩種方法都頗有效率。數組須要附加的程序來處理隊在數組尾部迴繞的狀況。鏈表必須是雙端的,這樣才能從一端插入到另外一端刪除。
用數組仍是鏈表來實現隊列的選擇是經過數據量是否能夠被很好地預測來決定的,若是知道會有多少數據量的話,就使用數組,不然的話就用鏈表。
優先級隊列用在只對訪問最高優先級數據項訪問的時候,最高優先級數據項就是含有最大(有時最小)的關鍵字的項。
優先級隊列能夠用有序數組或堆來實現,向有序數組中插入是很慢的,可是刪除很快,使用堆來實現優先級隊列,插入和刪除的時間複雜度都是O(logN)級
當插入速度不重要時,可使用數組或雙端鏈表,當數據量能夠被預測時,使用數組,當數據量未知時,可使用鏈表,若是速度很重要的話,選擇堆更好一些。
專用結構的比較
當選擇數據結構時,能夠先嚐試一種較慢但簡單的排序,例如插入排序。若是採用了這些方法,現代計算機的快速處理速度也有可能在恰當的時間內將較大的數據量排序。
插入排序對幾乎已排好的文件也頗有效,若是沒有太多的元素處於亂序的位置上,操做的時間複雜度大約在O(N)級,這一般發生在往一個已排好序的文件中插入一些新的數據元素的狀況。
若是插入排序顯得太慢,下一步能夠嘗試希爾排序,它很容易實現,而且使用起來不會由於條件不一樣而性能變化差距太大:Sedgewick估計它的數據量爲5000如下時頗有用。
只有當希爾排序變得很慢時,你才應該使用那些更復雜但更快速的排序方法:歸併排序、堆排序或快速排序。歸併排序須要輔助存儲空間,堆排序須要有一個堆的數據結構,前二者都比快速排序在某些程度上慢,因此當須要最短的排序時間時常常選擇快速排序。
然而,快速排序在處理非隨機性能數據時性能不大可靠,由於那時它的速度有可能退化至O(N^2)級。
對那些有多是非隨機性的數據來講,堆排序更加可靠,當快速排序沒有被正確地實現時,它會產生微小誤差,在代碼中細小的錯誤會使它對按某些順序排列的數據無能爲力,而診斷這種狀況又至關難。
下面是幾種排序算法的時間複雜度級別