前言
此篇爲數據結構相關內容連載第一篇,爲博主總結所得,如有錯誤,請指出!我及時更正!
1. 數據結構
數組(無序,有序),棧,隊列(循環隊列,雙端隊列),鏈表(單鏈表,雙端鏈表),哈希表(也叫散列表),堆,樹,圖
如下圖所示:
1.1 數據結構說明及特點
-
數組:大小固定,有序數組就是按一定順序排列的數組
-
棧:後進先出,Stack底層是採用數組來實現的
-
隊列:隊列同樣是一種特殊的線性表,其插入和刪除的操作分別在表的兩端進行,隊列的特點就是先進先出(First In First Out)
隊列使用場景:
模擬現實世界中的隊列,如售票櫃檯的隊列以及其他先到先服務的場景。
計算客戶在呼叫中心等待的時間。
異步數據的傳輸(文件輸入輸出、管道、嵌套字)。
操作系統中的優先級任務執行。
短信羣體發送 應用的發佈訂閱模式 -
鏈表:空間可以不連續
-
哈希表:快速的插入操作和查找操作。其key基於數組來實現。key-value
-
堆:是一個可動態申請的內存空間,是一個特殊的二叉樹
-
樹:樹形結構是一類重要的非線性結構。樹形結構是結點之間有分支,並具有層次關係的結構。
-
圖:是一種複雜的非線性數據結構。
數據結構 | 優點 | 缺點 |
---|---|---|
數組 | 插入快,如果知道下標,可以快速的存取 | 查找慢,刪除慢,大小固定 |
有序數組 | 比無序的數組查找快 | 刪除慢, 大小固定, 插入時要按排序方式把後面的數據進行移動 |
棧 | 後進先出 | 存取其他項很慢 |
隊列 | 先進先出 | 存取其他項很慢 |
鏈表 | 插入塊,刪除快 | 查找慢 |
二叉樹 | 查找,插入,刪除都很快(如果樹保持平衡) | 刪除算法複雜 |
紅黑樹 | 查找,插入,刪除都很快 | 算法複雜 |
哈希表 | 如果關鍵字已知則存取快。插入塊 | 刪除慢。如果不知關鍵字則存取很慢,對存儲空間使用不充分 |
堆 | 插入、刪除快,對最大數據項的存取快 | 對其它數據項的存取很慢 |
圖 | 對現實世界建模 | 有些算法慢且複雜 |
2. 詳細介紹
每一種數據結構都有着獨特的數據存儲方式,下面爲大家詳細介紹它們的結構和優缺點
2.1 數組
數組是可以再內存中連續存儲多個元素的結構,在內存中的分配也是連續的,數組中的元素通過數組下標進行訪問,數組下標從0開始。例如下面這段代碼就是將數組的第一個元素賦值爲 1。
int[] data = new int[100];data[0] = 1;
優點:
1、按照索引查詢元素速度快
2、按照索引遍歷數組方便
缺點:
1、數組的大小固定後就無法擴容了
2、數組只能存儲一種類型的數據
3、添加,刪除的操作慢,因爲要移動其他的元素。
適用場景:
頻繁查詢,對存儲空間要求不大,很少增加和刪除的情況。
2.2 棧
棧是一種特殊的線性表,僅能在線性表的一端操作,棧頂允許操作,棧底不允許操作。 棧的特點是:先進後出,或者說是後進先出,從棧頂放入元素的操作叫入棧,取出元素叫出棧。
棧的結構就像一個集裝箱,越先放進去的東西越晚才能拿出來,所以,棧常應用於實現遞歸功能方面的場景,例如斐波那契數列。
2.3 隊列
隊列與棧一樣,也是一種線性表,不同的是,隊列可以在一端添加元素,在另一端取出元素,也就是:先進先出。從一端放入元素的操作稱爲入隊,取出元素爲出隊,示例圖如下:
使用場景:因爲隊列先進先出的特點,在多線程阻塞隊列管理中非常適用。
2.4 鏈表
鏈表是物理存儲單元上非連續的、非順序的存儲結構,數據元素的邏輯順序是通過鏈表的指針地址實現,每個元素包含兩個結點,一個是存儲元素的數據域 (內存空間),另一個是指向下一個結點地址的指針域。根據指針的指向,鏈表能形成不同的結構,例如單鏈表,雙向鏈表,循環鏈表等。
鏈表的優點:
鏈表是很常用的一種數據結構,不需要初始化容量,可以任意加減元素;
添加或者刪除元素時只需要改變前後兩個元素結點的指針域指向地址即可,所以添加,刪除很快;
缺點:
因爲含有大量的指針域,佔用空間較大;
查找元素需要遍歷鏈表來查找,非常耗時。
適用場景:
數據量較小,需要頻繁增加,刪除操作的場景
2.5 樹
樹是一種數據結構,它是由n(n>=1)個有限節點組成一個具有層次關係的集合。把它叫做 “樹” 是因爲它看起來像一棵倒掛的樹,也就是說它是根朝上,而葉朝下的。它具有以下的特點:
- 每個節點有零個或多個子節點;
- 沒有父節點的節點稱爲根節點;
- 每一個非根節點有且只有一個父節點;
- 除了根節點外,每個子節點可以分爲多個不相交的子樹;
在日常的應用中,我們討論和用的更多的是樹的其中一種結構,就是二叉樹。
二叉樹是樹的特殊一種,具有如下特點:
1、每個結點最多有兩顆子樹,結點的度最大爲2。
2、左子樹和右子樹是有順序的,次序不能顛倒。
3、即使某結點只有一個子樹,也要區分左右子樹。
二叉樹是一種比較有用的折中方案,它添加,刪除元素都很快,並且在查找方面也有很多的算法優化,所以,二叉樹既有鏈表的好處,也有數組的好處,是兩者的優化方案,在處理大批量的動態數據方面非常有用。
擴展:
二叉樹有很多擴展的數據結構,包括平衡二叉樹、紅黑樹、B+樹(數據庫底層中詳細講述了)等,這些數據結構二叉樹的基礎上衍生了很多的功能,在實際應用中廣泛用到,例如mysql的數據庫索引結構用的就是B+樹,還有HashMap的底層源碼中用到了紅黑樹(JDK1.8起,鏈表的長度超過了8)。這些二叉樹的功能強大,但算法上比較複雜,想學習的話還是後續需要花時間去深入的。
2.6 散列表(哈希表)
散列表,也叫哈希表,是根據關鍵碼和值 (key和value) 直接進行訪問的數據結構,通過key和value來映射到集合中的一個位置,這樣就可以很快找到集合中的對應元素。
記錄的存儲位置=f(key)
這裏的對應關係 f 成爲散列函數,又稱爲哈希 (hash函數),而散列表就是把Key通過一個固定的算法函數既所謂的哈希函數轉換成一個整型數字,然後就將該數字對數組長度進行取餘,取餘結果就當作數組的下標,將value存儲在以該數字爲下標的數組空間裏,這種存儲空間可以充分利用數組的查找優勢來查找元素,所以查找的速度很快。
哈希表在應用中也是比較常見的,就如Java中有些集合類就是借鑑了哈希原理構造的,例如HashMap,HashTable等,利用hash表的優勢,對於集合的查找元素時非常方便的,然而,因爲哈希表是基於數組衍生的數據結構,在添加刪除元素方面是比較慢的,所以很多時候需要用到一種數組鏈表來做,也就是拉鍊法。拉鍊法是數組結合鏈表的一種結構,較早前的hashMap底層的存儲就是採用這種結構,直到jdk1.8之後才換成了數組加紅黑樹的結構,其示例圖如下:
從圖中可以看出,左邊很明顯是個數組,數組的每個成員包括一個指針,指向一個鏈表的頭,當然這個鏈表可能爲空,也可能元素很多。我們根據元素的一些特徵把元素分配到不同的鏈表中去,也是根據這些特徵,找到正確的鏈表,再從鏈表中找出這個元素。
哈希表的應用場景很多,當然也有很多問題要考慮,比如哈希衝突的問題,如果處理的不好會浪費大量的時間,導致應用崩潰。
2.7 堆
堆是一種比較特殊的數據結構,可以被看做一棵樹的數組對象,具有以下的性質:
- 堆中某個節點的值總是不大於或不小於其父節點的值;
- 堆總是一棵完全二叉樹。
將根節點最大的堆叫做最大堆或大根堆,根節點最小的堆叫做最小堆或小根堆。常見的堆有二叉堆、斐波那契堆等。
堆的定義如下:n個元素的序列{k1,k2,ki,…,kn}當且僅當滿足下關係時,稱之爲堆。
(ki <= k2i,ki <= k2i+1)或者(ki >= k2i,ki >= k2i+1), (i = 1,2,3,4…n/2),滿足前者的表達式的成爲小頂堆,滿足後者表達式的爲大頂堆,這兩者的結構圖可以用完全二叉樹排列出來,示例圖如下:
因爲堆有序的特點,一般用來做數組中的排序,稱爲堆排序。
2.8 圖
圖是由結點的有窮集合V和邊的集合E組成。其中,爲了與樹形結構加以區別,在圖結構中常常將結點稱爲頂點,邊是頂點的有序偶對,若兩個頂點之間存在一條邊,就表示這兩個頂點具有相鄰關係。
按照頂點指向的方向可分爲無向圖和有向圖:
圖是一種比較複雜的數據結構,在存儲數據上有着比較複雜和高效的算法,分別有鄰接矩陣 、鄰接表、十字鏈表、鄰接多重表、邊集數組等存儲結構,這裏暫不做展開。