簡單實現二叉搜尋樹 (查詢樹)
阿新 • • 發佈:2018-11-26
直接看程式碼
/** * @author <a href=mailto:[email protected]>maple</a> * @since 2018-11-25 11:40 PM */ // 二分搜尋樹 // 由於Key需要能夠進行比較,所以需要extends Comparable<Key> public class BST<Key extends Comparable<Key>, Value> { // 樹中的節點為私有的類, 外界不需要了解二分搜尋樹節點的具體實現 private class Node { private Key key; private Value value; private Node left, right; public Node(Key key, Value value) { this.key = key; this.value = value; left = right = null; } public Node(Node node) { this.key = node.key; this.value = node.value; this.left = node.left; this.right = node.right; } } private Node root; // 根節點 private int count; // 樹種的節點個數 // 建構函式, 預設構造一棵空二分搜尋樹 public BST() { root = null; count = 0; } // 返回二分搜尋樹的節點個數 public int size() { return count; } // 返回二分搜尋樹是否為空 public boolean isEmpty() { return count == 0; } // 向二分搜尋樹中插入一個新的(key, value)資料對 public void insert(Key key, Value value) { root = insert(root, key, value); } // 檢視二分搜尋樹中是否存在鍵key public boolean contain(Key key) { return contain(root, key); } // 在二分搜尋樹中搜索鍵key所對應的值。如果這個值不存在, 則返回null public Value search(Key key) { return search(root, key); } // 二分搜尋樹的前序遍歷 public void preOrder() { preOrder(root); } // 二分搜尋樹的中序遍歷 public void inOrder() { inOrder(root); } // 二分搜尋樹的後序遍歷 public void postOrder() { postOrder(root); } // 二分搜尋樹的層序遍歷 public void levelOrder() { // 我們使用LinkedList來作為我們的佇列 LinkedList<Node> q = new LinkedList<>(); q.add(root); while (!q.isEmpty()) { Node node = q.remove(); System.out.println(node.key); if (node.left != null) q.add(node.left); if (node.right != null) q.add(node.right); } } // 尋找二分搜尋樹的最小的鍵值 public Key minimum() { assert count != 0; Node minNode = minimum(root); return minNode.key; } // 尋找二分搜尋樹的最大的鍵值 public Key maximum() { assert count != 0; Node maxNode = maximum(root); return maxNode.key; } // 從二分搜尋樹中刪除最小值所在節點 public void removeMin() { if (root != null) root = removeMin(root); } // 從二分搜尋樹中刪除最大值所在節點 public void removeMax() { if (root != null) root = removeMax(root); } /** * O(logn) * * @param key */ // 從二分搜尋樹中刪除鍵值為key的節點 public void remove(Key key) { root = remove(root, key); } //******************** //* 二分搜尋樹的輔助函式 //******************** // 向以node為根的二分搜尋樹中, 插入節點(key, value), 使用遞迴演算法 // 返回插入新節點後的二分搜尋樹的根 private Node insert(Node node, Key key, Value value) { if (node == null) { count++; return new Node(key, value); } if (key.compareTo(node.key) == 0) node.value = value; else if (key.compareTo(node.key) < 0) node.left = insert(node.left, key, value); else // key > node->key node.right = insert(node.right, key, value); return node; } // 檢視以node為根的二分搜尋樹中是否包含鍵值為key的節點, 使用遞迴演算法 private boolean contain(Node node, Key key) { if (node == null) return false; if (key.compareTo(node.key) == 0) return true; else if (key.compareTo(node.key) < 0) return contain(node.left, key); else // key > node->key return contain(node.right, key); } // 在以node為根的二分搜尋樹中查詢key所對應的value, 遞迴演算法 // 若value不存在, 則返回NULL private Value search(Node node, Key key) { if (node == null) return null; if (key.compareTo(node.key) == 0) return node.value; else if (key.compareTo(node.key) < 0) return search(node.left, key); else // key > node->key return search(node.right, key); } // 對以node為根的二叉搜尋樹進行前序遍歷, 遞迴演算法 private void preOrder(Node node) { if (node != null) { System.out.println(node.key); preOrder(node.left); preOrder(node.right); } } // 對以node為根的二叉搜尋樹進行中序遍歷, 遞迴演算法 private void inOrder(Node node) { if (node != null) { inOrder(node.left); System.out.println(node.key); inOrder(node.right); } } // 對以node為根的二叉搜尋樹進行後序遍歷, 遞迴演算法 private void postOrder(Node node) { if (node != null) { postOrder(node.left); postOrder(node.right); System.out.println(node.key); } } // 返回以node為根的二分搜尋樹的最小鍵值所在的節點 private Node minimum(Node node) { if (node.left == null) return node; return minimum(node.left); } // 返回以node為根的二分搜尋樹的最大鍵值所在的節點 private Node maximum(Node node) { if (node.right == null) return node; return maximum(node.right); } // 刪除掉以node為根的二分搜尋樹中的最小節點 // 返回刪除節點後新的二分搜尋樹的根 private Node removeMin(Node node) { if (node.left == null) { Node rightNode = node.right; node.right = null; count--; return rightNode; } node.left = removeMin(node.left); return node; } // 刪除掉以node為根的二分搜尋樹中的最大節點 // 返回刪除節點後新的二分搜尋樹的根 private Node removeMax(Node node) { if (node.right == null) { Node leftNode = node.left; node.left = null; count--; return leftNode; } node.right = removeMax(node.right); return node; } // 刪除掉以node為根的二分搜尋樹中鍵值為key的節點, 遞迴演算法 // 返回刪除節點後新的二分搜尋樹的根 Node remove(Node node, Key key) { if (node == null) return null; if (key.compareTo(node.key) < 0) { node.left = remove(node.left, key); return node; } else if (key.compareTo(node.key) > 0) { node.right = remove(node.right, key); return node; } else { // key == node->key // 待刪除節點左子樹為空的情況 if (node.left == null) { Node rightNode = node.right; node.right = null; count--; return rightNode; } // 待刪除節點右子樹為空的情況 if (node.right == null) { Node leftNode = node.left; node.left = null; count--; return leftNode; } // 待刪除節點左右子樹均不為空的情況 // 找到比待刪除節點大的最小節點, 即待刪除節點右子樹的最小節點 // 用這個節點頂替待刪除節點的位置 Node successor = new Node(minimum(node.right)); count++; successor.right = removeMin(node.right); successor.left = node.left; node.left = node.right = null; count--; return successor; } } // 測試二分搜尋樹 public static void main(String[] args) { int N = 1000000; // 建立一個數組,包含[0...N)的所有元素 Integer[] arr = new Integer[N]; for (int i = 0; i < N; i++) arr[i] = new Integer(i); // 打亂陣列順序 for (int i = 0; i < N; i++) { int pos = (int) (Math.random() * (i + 1)); Integer t = arr[pos]; arr[pos] = arr[i]; arr[i] = t; } // 由於我們實現的二分搜尋樹不是平衡二叉樹, // 所以如果按照順序插入一組資料,我們的二分搜尋樹會退化成為一個連結串列 // 平衡二叉樹的實現,我們在這個課程中沒有涉及, // 有興趣的同學可以檢視資料自學諸如紅黑樹的實現 // 以後有機會,我會在別的課程裡向大家介紹平衡二叉樹的實現的:) // 我們測試用的的二分搜尋樹的鍵型別為Integer,值型別為String // 鍵值的對應關係為每個整型對應代表這個整型的字串 BST<Integer, String> bst = new BST<Integer, String>(); for (int i = 0; i < N; i++) bst.insert(new Integer(arr[i]), Integer.toString(arr[i])); // 對[0...2*N)的所有整型測試在二分搜尋樹中查詢 // 若i在[0...N)之間,則能查詢到整型所對應的字串 // 若i在[N...2*N)之間,則結果為null for (int i = 0; i < 2 * N; i++) { String res = bst.search(new Integer(i)); if (i < N) assert res.equals(Integer.toString(i)); else assert res == null; } } }