從零開始學資料結構和演算法(四)雜湊表的思想和二叉樹入門
- 陣列(順序表):定址容易
- 連結串列:插入與刪除容易
- 雜湊表:定址容易,插入刪除也容易的資料結構
HashTable
-
雜湊表(HashTable, 也叫散列表)
是根據關鍵碼值(Key value)而直接進行訪問的資料結構,它通過把關鍵碼值對映到表中一個位置來訪問記錄,以加快查詢的速度。
-
關鍵碼值(Key value)也可以當成是key的hash值
這個對映函式叫做雜湊函式
-
存放記錄的陣列叫做散列表
HashTable 例子

-
Key : {14, 19, 5, 7, 21, 1, 13, 0, 18} 散列表: 大小為13 的陣列 a[13]; 雜湊函式: f(x) = x mod 13;
-
hashtable 需要自定義的內容
雜湊函式與散列表大小 hash 衝突的解決方案 裝填因子:為什麼需要這個值?因為資料越接近陣列最大值,可能產生衝突的情況就越多
缺點
- 擴容需要大量的空間和效能
應用
- 電話號碼、字典、點歌系統、QQ、微信的好友等
設計(拉鍊法)
- JDK 1.8 以前

-
JDK 1.8 開始
當連結串列長度超過閾值,就轉成紅黑樹
樹
什麼是樹


樹的概念
節點與樹的度
- 結點擁有的子樹數稱為結點的度。 度為0的結點稱為葉子結點或終端結點,度不為0的結點稱為非終端結點或分支結點。 除根結點以外,分支結點也稱為內部結點。 樹的度是樹內各結點的度的最大值。

層次和深度


森林

樹的儲存結構
雙親表示法

孩子表示法

雙親孩子表示法
- 把每個結點的孩子結點排列起來,以單鏈表作為儲存結構, 則n個結點有n個孩子連結串列,如果是葉子結點則此單鏈表為空, 然後n個頭指標又組成一個線性表,採用順序儲存結構,存放在一個一維陣列中

孩子兄弟表示法
- 孩子兄弟表示法為每個節點設計三個域: 一個數據域,一個該節點的第一個孩子節點域,一個該節點的下一個節點的兄弟指標域

二叉樹

概念

斜樹

滿二叉樹

完全二叉樹

定義:
-
若設二叉樹的深度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第 h 層所有的結點都連續集中在最左邊,這就是完全二叉樹。
完全二叉樹是由滿二叉樹而引出來的。對於深度為K的,有n個結點的二叉樹,當且僅當其每一個結點都與深度為K的滿二叉樹中編號從1至n的結點一一對應時稱之為完全二叉樹。
- 所有的葉結點都出現在第k層或k-l層(層次最大的兩層)
- 對任一結點,如果其右子樹的最大層次為L,則其左子樹的最大層次為L或L+l。
一棵二叉樹至多隻有最下面的兩層上的結點的度數可以小於2,並且最下層上的結點都集中在該層最左邊的若干位置上,則此二叉樹成為完全二叉樹,並且最下層上的結點都集中在該層最左邊的若干位置上,而在最後一層上,右邊的若干結點缺失的二叉樹,則此二叉樹成為完全二叉樹。
二叉樹的儲存結構
順序儲存

鏈式儲存

二叉樹的遍歷
前序 ( DLR )
- 規則是若二叉樹為空,則空操作返回,否則先訪問跟結點,然後前序遍歷左子樹,再前序遍歷右子樹

中序 ( LDR )
- 規則是若樹為空,則空操作返回,否則從根結點開始(注意並不是先訪問根結點), 中序遍歷根結點的左子樹,然後是訪問根結點,最後中序遍歷右子樹

後續 ( LRD )
- 規則是若樹為空,則空操作返回,否則從左到右先葉子後結點的方式遍歷訪問左右子樹,最後是訪問根結點

二叉樹簡單程式碼實現
public class BinarayTree { Node<String> root; public BinarayTree(String data){ root=new Node<>(data,null,null); } public void createTree(){ Node<String> nodeB=new Node<String>("B",null,null); Node<String> nodeC=new Node<String>("C",null,null); Node<String> nodeD=new Node<String>("D",null,null); Node<String> nodeE=new Node<String>("E",null,null); Node<String> nodeF=new Node<String>("F",null,null); Node<String> nodeG=new Node<String>("G",null,null); Node<String> nodeH=new Node<String>("H",null,null); Node<String> nodeJ=new Node<String>("J",null,null); Node<String> nodeI=new Node<String>("I",null,null); root.leftChild=nodeB; root.rightChild=nodeC; nodeB.leftChild=nodeD; nodeC.leftChild=nodeE; nodeC.rightChild=nodeF; nodeD.leftChild=nodeG; nodeD.rightChild=nodeH; nodeE.rightChild=nodeJ; nodeH.leftChild=nodeI; } /** * 中序訪問樹的所有節點 */ public void midOrderTraverse(Node root){//邏輯 if(root==null){ return; } midOrderTraverse(root.leftChild);//邏輯 System.out.println("mid:"+root.data);//輸出 midOrderTraverse(root.rightChild);//邏輯 } /** * 前序訪問樹的所有節點Arrays.sort(); */ public void preOrderTraverse(Node root){ if(root==null){ return; } System.out.println("pre:"+root.data); preOrderTraverse(root.leftChild); preOrderTraverse(root.rightChild); } /** * 後序訪問樹的所有節點 */ public void postOrderTraverse(Node root){ if(root==null){ return; } postOrderTraverse(root.leftChild); postOrderTraverse(root.rightChild); System.out.println("post:"+root.data); } /** * 節點 */ public class Node<T>{ T data; Node<T> leftChild; Node<T> rightChild; public Node(T data, Node<T> leftChild, Node<T> rightChild) { this.data = data; this.leftChild = leftChild; this.rightChild = rightChild; } } } 複製程式碼