1. 程式人生 > >《演算法筆記》5. 字首樹、桶排序、排序演算法總結

《演算法筆記》5. 字首樹、桶排序、排序演算法總結

[TOC] # 1 字首樹結構(trie)、桶排序、排序總結 ## 1.1 字首樹結構 > 單個字串中,字元從前到後的加到一顆多叉樹上 > 字元放在路上,節點上有專屬的資料項(常見的是pass和end值) > 所有樣本都這樣新增。如果沒有路就新建,如果有路就複用 > 沿途節點的pass值增加1.每個字串結束時來到的節點end值增加1 > 一個字串陣列中,所有字串的字元數為N,整個陣列加入字首樹種的代價是O(N) 功能一:構建好字首樹之後,我們查詢某個字串在不在字首樹中,某字串在這顆字首樹中出現了幾次都是特別方便的。例如找"ab"在字首樹中存在幾次,可以先看有無走向a字元的路徑(如果沒有,直接不存在),再看走向b字元的路徑,此時檢查該節點的end標記的值,如果為0,則字首樹中不存在"ab"字串,如果e>0則,e等於幾則"ab"在字首樹種出現了幾次 功能二:如果單單是功能一,那麼雜湊表也可以實現。現查詢所有加入到字首樹的字串,有多少個以"a"字元作為字首,來到"a"的路徑,檢視p值大小,就是以"a"作為字首的字串數量 ```Java package class05; import java.util.HashMap; public class Code02_TrieTree { public static class Node1 { // pass表示字元從該節點的路徑通過 public int pass; // end表示該字元到此節點結束 public int end; public Node1[] nexts; public Node1() { pass = 0; end = 0; // 每個節點下預設26條路,分別是a~z // 0 a // 1 b // 2 c // .. .. // 25 z // nexts[i] == null i方向的路不存在 // nexts[i] != null i方向的路存在 nexts = new Node1[26]; } } public static class Trie1 { // 預設只留出頭節點 private Node1 root; public Trie1() { root = new Node1(); } // 往該字首樹中新增字串 public void insert(String word) { if (word == null) { return; } char[] str = word.toCharArray(); // 初始引用指向頭節點 Node1 node = root; // 頭結點的pass首先++ node.pass++; // 路徑的下標 int path = 0; for (int i = 0; i < str.length; i++) { // 從左往右遍歷字元 // 當前字元減去'a'的ascii碼得到需要新增的下個節點下標 path = str[i] - 'a'; // 由字元,對應成走向哪條路 // 當前方向上沒有建立節點,即一開始不存在這條路,新開闢 if (node.nexts[path] == null) { node.nexts[path] = new Node1(); } // 引用指向當前來到的節點 node = node.nexts[path]; // 當前節點的pass++ node.pass++; } // 當新加的字串所有字元處理結束,最後引用指向的當前節點就是該字串的結尾節點,end++ node.end++; } // 刪除該字首樹的某個字串 public void delete(String word) { // 首先要查一下該字串是否加入過 if (search(word) != 0) { // 沿途pass-- char[] chs = word.toCharArray(); Node1 node = root; node.pass--; int path = 0; for (int i = 0; i < chs.length; i++) { path = chs[i] - 'a'; // 在尋找的過程中,pass為0,提前可以得知在本次刪除之後,該節點以下的路徑不再需要,可以直接刪除。 // 那麼該節點之下下個方向的節點引用置為空(JVM垃圾回收,相當於該節點下的路徑被刪了) if (--node.nexts[path].pass == 0) { node.nexts[path] = null; return; } node = node.nexts[path]; } // 最後end-- node.end--; } } // 在該字首樹中查詢 // word這個單詞之前加入過幾次 public int search(String word) { if (word == null) { return 0; } char[] chs = word.toCharArray(); Node1 node = root; int index = 0; for (int i = 0; i < chs.length; i++) { index = chs[i] - 'a'; // 尋找該字串的路徑中如果提前找不到path,就是未加入過,0次 if (node.nexts[index] == null) { return 0; } node = node.nexts[index]; } // 如果順利把word字串在字首樹中走完路徑,那麼此時的node對應的end值就是當前word在該字首樹中添加了幾次 return node.end; } // 所有加入的字串中,有幾個是以pre這個字串作為字首的 public int prefixNumber(String pre) { if (pre == null) { return 0; } char[] chs = pre.toCharArray(); Node1 node = root; int index = 0; for (int i = 0; i < chs.length; i++) { index = chs[i] - 'a'; // 走不到最後,就沒有 if (node.nexts[index] == null) { return 0; } node = node.nexts[index]; } // 順利走到最後,返回的pass就是有多少個字串以當前pre為字首的 return node.pass; } } /** * 實現方式二,針對各種字串,路徑不僅僅是a~z對應的26個,