數據結構總結,及在Java代碼使用數據結構

數據結構總結

本博客爲基本知識點的淺嘗輒止,僅供參考,詳細的需要讀者繼續查閱資料和實踐。建議讀者在學習這些基礎的數據結構時,能夠參照編程語言裏面的源碼,會效果更佳。


一、概念

  • 在計算機科學中,數據結構(英語:data structure)是計算機中存儲、組織數據的方式。

  • 數據結構意味着接口或封裝:一個數據結構可被視爲兩個函數之間的接口,或者是由數據類型聯合組成的存儲內容的訪問方法封裝。

  • 不同種類的數據結構適合不同種類的應用,部分數據結構甚至是爲了解決特定問題而設計出來的。例如B樹即爲加快樹狀結構訪問速度而設計的數據結構,常被應用在數據庫和文件系統上。

  • 系統架構的關鍵因素是數據結構而非算法的見解,導致了多種形式化的設計方法與編程語言的出現。絕大多數的語言都帶有某種程度上的模塊化思想,透過將數據結構的具體實現封裝隱藏於用戶界面之後的方法,來讓不同的應用程序能夠安全地重用這些數據結構。C++、Java、Python等面向對象的編程語言可使用類 (計算機科學)來達到這個目的。

  • 因爲數據結構概念的普及,現代編程語言及其API中都包含了多種默認的數據結構,例如 C++ 標準模板庫中的容器、Java集合框架以及微軟的.NET Framework。

二、常見的數據結構

  • 線性表
    • 順序表 - 數組(Array)
    • 鏈表(Linked List)
  • 棧(Stack)
  • 隊列(Queue)
  • 樹(Tree)
  • 圖(Graph)
  • 堆(Heap)
  • 散列表(Hash table)

1. 數組(Array)- 順序表

  1. 概念:
    在計算機科學中,數組數據結構(array data structure),簡稱數組(Array),是由相同類型的元素(element)的集合所組成的數據結構,分配一塊連續的內存來存儲。利用元素的索引(index)可以計算出該元素對應的存儲地址。

    最簡單的數據結構類型是一維數組。例如,索引爲0到9的40位整數數組,可作爲在存儲器地址2000,2004,2008,…2040中,存儲10個變量,因此索引爲 i 的元素即在存儲器中的 2000 + 4×i 地址。數組第一個元素的存儲器地址稱爲第一地址或基礎地址。

	int[] array = new int[10];

  二維數組,對應於數學上的矩陣概念,可表示爲二維矩形格。

	int[][] b = {{3, 6, 2}, {0, 1, -4}, {2, -1, 0}};
	// 或
	int[][] b = new int[][]{{3, 6, 2}, {0, 1, -4}, {2, -1, 0}};

2. 鏈表(Linked List)

  1. 概念
    鏈表(Linked list)是一種常見的基礎數據結構,是一種線性表,但是並不會按順序表的順序存儲數據,而是在每一個節點裏存到下一個節點的指針(Pointer)。由於不必須按順序存儲,鏈表在插入的時候可以達到 O(1) 的複雜度,比順序錶快得多,但是查找一個節點或者訪問特定編號的節點則需要O(n)的時間,而順序表相應的時間複雜度分別是O(n)和O(1)。

    使用鏈表結構可以克服數組鏈表需要預先知道數據大小的缺點,鏈表結構可以充分利用計算機內存空間,實現靈活的內存動態管理。但是鏈表失去了數組隨機讀取的優點,同時鏈表由於增加了結點的指針域,空間開銷比較大。

在這裏插入圖片描述
鏈表中最簡單的一種是單向鏈表,它包含兩個域,一個數據域和一個指針域。這個指針域指向列表中的下一個節點,而最後一個節點則指向一個空值。

  1. 代碼使用:
	// LinkedList是雙向鏈表
	List<Integer> linkList = new LinkedList<>();

根據jdk中的源碼,左邊是 LinkedList 提供的函數API,右邊箭頭處可以看到 LinkedList 是雙向鏈表。
在這裏插入圖片描述


3. 棧(Stack)

  1. 概念:
    堆棧(stack)又稱爲棧或堆疊,是計算機科學中的一種抽象數據類型,只允許在有序的線性數據集合的一端(稱爲棧頂端,top)進行加入數據(push)和移除數據(pop)的運算。因而按照後進先出(LIFO, Last In First Out)的原理運作。

    常與另一種有序的線性數據集合隊列相提並論。

    堆棧常用一維數組或鏈表來實現。
    在這裏插入圖片描述

  2. 操作
    堆棧使用兩種基本操作:推入(壓棧,push)和彈出(出棧,pop):
    壓棧:將數據放入堆棧頂端,堆棧頂端移到新放入的數據。
    出棧:將堆棧頂端數據移除,堆棧頂端移到移除後的下一筆數據。

  3. 特點
    堆棧的基本特點:
    先入後出,後入先出。
    除頭尾節點之外,每個元素有一個前驅,一個後繼。

  4. 堆棧的應用:
    回溯
    遞歸
    深度優先搜索

  5. 代碼使用:

	Stack stack = new Stack();
	1. 創建棧:			public Stack() { }
	2. 出棧:			public E push(E item) { }
	3. 入棧:			public synchronized E pop() { }
	4. 查看棧頂元素:		public synchronized E peek() { }
	5. 查找元素在棧中位置:public synchronized int search(Object o) { }

在這裏插入圖片描述

4. 隊列(Queue)

  1. 概念
    隊列,是先進先出(FIFO, First-In-First-Out)的線性表。在具體應用中通常用鏈表或者數組來實現。隊列只允許在後端(稱爲rear)進行插入操作,在前端(稱爲front)進行刪除操作。

  2. 使用場景:
    因爲隊列先進先出的特點,在多線程阻塞隊列管理中非常適用。

  3. 代碼使用:
    Java 集合中的 Queue 接口繼承自 Collection\color{red}Collection 接口 ,Deque, AbstractQueue,CheckedQueue,AsLIFOQueue,BlockingQueue,ConcurrentLinkedQueue, 等類都實現了它。
    在這裏插入圖片描述基礎的 Queue接口,聲明瞭如下這些方法:

    • add
    • offer
    • remove
    • poll
    • element
    • peek
      在這裏插入圖片描述
	// LinkedList是雙向鏈表,它實現了Dequeue接口
	Queue<Integer> queue = new LinkedList<>();

5. 樹(Tree)

  1. 概念:
    在計算機科學中,樹(tree)是一種抽象數據類型(ADT)或是實現這種抽象數據類型的數據結構,用來模擬具有樹狀結構性質的數據集合。它是由n(n>0)個有限節點組成一個具有層次關係的集合。把它叫做“樹”是因爲它看起來像一棵倒掛的樹,也就是說它是根朝上,而葉朝下的。它具有以下的特點:

     1)每個節點都只有有限個子節點或無子節點;
     
     2)沒有父節點的節點稱爲根節點;
     
     3)每一個非根節點有且只有一個父節點;
     
     4)除了根節點外,每個子節點可以分爲多個不相交的子樹;
     
     5)樹裏面沒有環路(cycle)
    

6. 圖(Graph)

7. 堆(Heap)


8. 散列表(Hash table)

  1. 概念:

    • 散列表(Hash table,也叫哈希表),是根據鍵(Key)而直接訪問在內存儲存位置的數據結構。也就是說,它通過計算一個關於鍵值的函數,將所需查詢的數據映射到表中一個位置來訪問記錄,這加快了查找速度。這個映射函數稱做散列函數,存放記錄的數組稱做散列表。

    • 若關鍵字爲 k,則其值存放在 f(k) 的存儲位置上。由此,不需比較便可直接取得所查記錄。稱這個對應關係 f 爲散列函數,按這個思想建立的表爲散列表。

    • 對不同的關鍵字可能得到同一散列地址,即 k1 != k2,而 f(k1)=f(k2),這種現象稱爲\color{red}衝突(Collision)。具有相同函數值的關鍵字對該散列函數來說稱做\color{red}同義詞。綜上所述,根據散列函數 f(k) 和處理衝突的方法將一組關鍵字映射到一個有限的連續的地址集(區間)上,並以關鍵字在地址集中的“像”作爲記錄在表中的存儲位置,這種表便稱爲散列表,這一映射過程稱爲散列造表或散列,所得的存儲位置稱散列地址。

    • 若對於關鍵字集合中的任一個關鍵字,經散列函數映象到地址集合中任何一個地址的概率是相等的,則稱此類散列函數爲均勻散列函數(Uniform Hash function),這就使關鍵字經過散列函數得到一個“隨機的地址”,從而減少衝突。

  2. 構造散列函數:
    散列函數能使對一個數據序列的訪問過程更加迅速有效,通過散列函數,數據元素將被更快定位。方法如下:

    • 直接定址法:取關鍵字或關鍵字的某個線性函數值爲散列地址。即 hash(k) = k 或 hash(k) = a * k + b,其中a, b爲常數(這種散列函數叫做自身函數)
    • 數字分析法:假設關鍵字是以r爲基的數,並且哈希表中可能出現的關鍵字都是事先知道的,則可取關鍵字的若干數位組成哈希地址。
    • 平方取中法:取關鍵字平方後的中間幾位爲哈希地址。通常在選定哈希函數時不一定能知道關鍵字的全部情況,取其中的哪幾位也不一定合適,而一個數平方後的中間幾位數和數的每一位都相關,由此使隨機分佈的關鍵字得到的哈希地址也是隨機的。取的位數由表長決定。
    • 摺疊法:將關鍵字分割成位數相同的幾部分(最後一部分的位數可以不同),然後取這幾部分的疊加和(捨去進位)作爲哈希地址。
      隨機數法
    • 除留餘數法:取關鍵字被某個不大於散列表表長m的數p除後所得的餘數爲散列地址。即 hash(k)=k mod p, p <= m。不僅可以對關鍵字直接取模,也可在摺疊法、平方取中法等運算之後取模。對p的選擇很重要,一般取素數或m,若p選擇不好,容易產生衝突。
  3. 處理衝突:
    爲了知道衝突產生的相同散列函數地址所對應的關鍵字,如當f(k1) = f(k2)時,如何確認關鍵字爲 k1 還是 k2 ,必須選用另外的散列函數,或者對沖突結果進行處理。而不發生衝突的可能性是非常之小的,所以通常對沖突進行處理。常用方法有以下幾種:

    • 開放定址法(open addressing):hashi = (hash(key) + di) mod m, i = 1,2…k(k <= m - 1) 其中hash(key)爲散列函數,m爲散列表長,di 爲增量序列,i爲已發生衝突的次數。增量序列可有下列取法:

      • di = 1,2,3…(m-1)稱爲 線性探測(Linear Probing);即di = i,或者爲其他線性函數。相當於逐個探測存放地址的表,直到查找到一個空單元,把散列地址存放在該空單元。
      • di = ±12,±22,…,±k2稱爲 平方探測(Quadratic Probing)。相對線性探測,相當於發生衝突時探測間隔 di = i2 個單元的位置是否爲空,如果爲空,將地址存放進去。
      • di = 僞隨機數序列,稱爲 僞隨機探測。
    • 單獨鏈表法:將散列到同一個存儲位置的所有元素保存在一個鏈表中。實現時,一種策略是散列表同一位置的所有衝突結果都是用棧存放的,新元素被插入到表的前端還是後端完全取決於怎樣方便。

    • 雙散列。

    • 再散列:hashi = hashi(key), i=1,2…k。hashi 是一些散列函數。即在上次散列計算髮生衝突時,利用該次衝突的散列函數地址產生新的散列函數地址,直到衝突不再發生。這種方法不易產生“聚集”(Cluster),但增加了計算時間。

    • 建立一個公共溢出區

      舉例:
      顯示線性探測填裝一個散列表的過程:
      關鍵字爲{89,18,49,58,69}插入到一個散列表中的情況。此時線性探測的方法是取{\displaystyle d_{i}=i}d_{i}=i。並假定取關鍵字除以10的餘數爲散列函數法則。
      散列地址 空表 插入89 插入18 插入49 插入58 插入69
      0 49 49 49
      1 58 58
      2 69
      3
      4
      5
      6
      7
      8 18 18 18 18
      9 89 89 89 89 89
      第一次衝突發生在填裝49的時候。地址爲9的單元已經填裝了89這個關鍵字,所以取{\displaystyle i=1}i=1,往下查找一個單位,發現爲空,所以將49填裝在地址爲0的空單元。第二次衝突則發生在58上,取{\displaystyle i=3}{\displaystyle i=3},往下查找3個單位,將58填裝在地址爲1的空單元。69同理。
      表的大小選取至關重要,此處選取10作爲大小,發生衝突的機率就比選擇質數11作爲大小的可能性大。越是質數,mod取餘就越可能均勻分佈在表的各處。
      聚集(Cluster,也翻譯做“堆積”)的意思是,在函數地址的表中,散列函數的結果不均勻地佔據表的單元,形成區塊,造成線性探測產生一次聚集(primary clustering)和平方探測的二次聚集(secondary clustering),散列到區塊中的任何關鍵字需要查找多次試選單元才能插入表中,解決衝突,造成時間浪費。對於開放定址法,聚集會造成性能的災難性損失,是必須避免的。

  4. 代碼使用:

  		Map<String, Integer> hashtable = new Hashtable<>();
        Map<String, Integer> hashMap = new HashMap<>();

三、參考資料

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