摘要: 原創出處:www.bysocket.com 泥瓦匠BYSocket 但願轉載,保留摘要,謝謝! 「歲月極美,在於它必然的流逝」 「春花 秋月 夏日 冬雪」 — 三毛java
樹是由節點和邊構成,儲存元素的集合。節點分根節點、父節點和子節點的概念。 如圖:樹深=4; 5是根節點;一樣8與3的關係是父子節點關係。node
二叉樹binary tree,則加了「二叉」(binary),意思是在樹中做區分。每一個節點至多有兩個子(child),left child & right child。二叉樹在不少例子中使用,好比二叉樹表示算術表達式。 如圖:1/8是左節點;2/3是右節點; git
顧名思義,二叉樹上又加了個搜索的限制。其要求:每一個節點比其左子樹元素大,比其右子樹元素小。 如圖:每一個節點比它左子樹的任意節點大,並且比它右子樹的任意節點小github
直接上代碼,對應代碼分享在 Github 主頁 BinarySearchTree.java算法
package org.algorithm.tree; /* * Copyright [2015] [Jeff Lee] * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * 二叉搜索樹(BST)實現 * * Created by bysocket on 16/7/7. */ public class BinarySearchTree { /** * 根節點 */ public static TreeNode root; public BinarySearchTree() { this.root = null; } /** * 查找 * 樹深(N) O(lgN) * 1. 從root節點開始 * 2. 比當前節點值小,則找其左節點 * 3. 比當前節點值大,則找其右節點 * 4. 與當前節點值相等,查找到返回TRUE * 5. 查找完畢未找到, * @param key * @return */ public TreeNode search (int key) { TreeNode current = root; while (current != null && key != current.value) { if (key < current.value ) current = current.left; else current = current.right; } return current; } /** * 插入 * 1. 從root節點開始 * 2. 若是root爲空,root爲插入值 * 循環: * 3. 若是當前節點值大於插入值,找左節點 * 4. 若是當前節點值小於插入值,找右節點 * @param key * @return */ public TreeNode insert (int key) { // 新增節點 TreeNode newNode = new TreeNode(key); // 當前節點 TreeNode current = root; // 上個節點 TreeNode parent = null; // 若是根節點爲空 if (current == null) { root = newNode; return newNode; } while (true) { parent = current; if (key < current.value) { current = current.left; if (current == null) { parent.left = newNode; return newNode; } } else { current = current.right; if (current == null) { parent.right = newNode; return newNode; } } } } /** * 刪除節點 * 1.找到刪除節點 * 2.若是刪除節點左節點爲空 , 右節點也爲空; * 3.若是刪除節點只有一個子節點 右節點 或者 左節點 * 4.若是刪除節點左右子節點都不爲空 * @param key * @return */ public TreeNode delete (int key) { TreeNode parent = root; TreeNode current = root; boolean isLeftChild = false; // 找到刪除節點 及 是否在左子樹 while (current.value != key) { parent = current; if (current.value > key) { isLeftChild = true; current = current.left; } else { isLeftChild = false; current = current.right; } if (current == null) { return current; } } // 若是刪除節點左節點爲空 , 右節點也爲空 if (current.left == null && current.right == null) { if (current == root) { root = null; } // 在左子樹 if (isLeftChild == true) { parent.left = null; } else { parent.right = null; } } // 若是刪除節點只有一個子節點 右節點 或者 左節點 else if (current.right == null) { if (current == root) { root = current.left; } else if (isLeftChild) { parent.left = current.left; } else { parent.right = current.left; } } else if (current.left == null) { if (current == root) { root = current.right; } else if (isLeftChild) { parent.left = current.right; } else { parent.right = current.right; } } // 若是刪除節點左右子節點都不爲空 else if (current.left != null && current.right != null) { // 找到刪除節點的後繼者 TreeNode successor = getDeleteSuccessor(current); if (current == root) { root = successor; } else if (isLeftChild) { parent.left = successor; } else { parent.right = successor; } successor.left = current.left; } return current; } /** * 獲取刪除節點的後繼者 * 刪除節點的後繼者是在其右節點樹種最小的節點 * @param deleteNode * @return */ public TreeNode getDeleteSuccessor(TreeNode deleteNode) { // 後繼者 TreeNode successor = null; TreeNode successorParent = null; TreeNode current = deleteNode.right; while (current != null) { successorParent = successor; successor = current; current = current.left; } // 檢查後繼者(不可能有左節點樹)是否有右節點樹 // 若是它有右節點樹,則替換後繼者位置,加到後繼者父親節點的左節點. if (successor != deleteNode.right) { successorParent.left = successor.right; successor.right = deleteNode.right; } return successor; } public void toString(TreeNode root) { if (root != null) { toString(root.left); System.out.print("value = " + root.value + " -> "); toString(root.right); } } } /** * 節點 */ class TreeNode { /** * 節點值 */ int value; /** * 左節點 */ TreeNode left; /** * 右節點 */ TreeNode right; public TreeNode(int value) { this.value = value; left = null; right = null; } }
1. 節點數據結構 首先定義了節點的數據接口,節點分左節點和右節點及自己節點值。如圖express
代碼以下:apache
/** * 節點 */ class TreeNode { /** * 節點值 */ int value; /** * 左節點 */ TreeNode left; /** * 右節點 */ TreeNode right; public TreeNode(int value) { this.value = value; left = null; right = null; } }
2. 插入 插入,和刪除同樣會引發二叉搜索樹的動態變化。插入相對刪處理邏輯相對簡單些。如圖插入的邏輯:數據結構
a. 從root節點開始 b.若是root爲空,root爲插入值 c.循環: d.若是當前節點值大於插入值,找左節點 e.若是當前節點值小於插入值,找右節點 代碼對應:app
/** * 插入 * 1. 從root節點開始 * 2. 若是root爲空,root爲插入值 * 循環: * 3. 若是當前節點值大於插入值,找左節點 * 4. 若是當前節點值小於插入值,找右節點 * @param key * @return */ public TreeNode insert (int key) { // 新增節點 TreeNode newNode = new TreeNode(key); // 當前節點 TreeNode current = root; // 上個節點 TreeNode parent = null; // 若是根節點爲空 if (current == null) { root = newNode; return newNode; } while (true) { parent = current; if (key < current.value) { current = current.left; if (current == null) { parent.left = newNode; return newNode; } } else { current = current.right; if (current == null) { parent.right = newNode; return newNode; } } } }
3.查找 其算法複雜度 : O(lgN),樹深(N)。如圖查找邏輯:less
a.從root節點開始 b.比當前節點值小,則找其左節點 c.比當前節點值大,則找其右節點 d.與當前節點值相等,查找到返回TRUE e.查找完畢未找到 代碼對應:
/** * 查找 * 樹深(N) O(lgN) * 1. 從root節點開始 * 2. 比當前節點值小,則找其左節點 * 3. 比當前節點值大,則找其右節點 * 4. 與當前節點值相等,查找到返回TRUE * 5. 查找完畢未找到, * @param key * @return */ public TreeNode search (int key) { TreeNode current = root; while (current != null && key != current.value) { if (key < current.value ) current = current.left; else current = current.right; } return current; }
4. 刪除 首先找到刪除節點,其尋找方法:刪除節點的後繼者是在其右節點樹種最小的節點。如圖刪除對應邏輯:
a.找到刪除節點 b.若是刪除節點左節點爲空 , 右節點也爲空; c.若是刪除節點只有一個子節點 右節點 或者 左節點 d.若是刪除節點左右子節點都不爲空 代碼對應見上面完整代碼。 案例測試代碼以下,BinarySearchTreeTest.java
package org.algorithm.tree; /* * Copyright [2015] [Jeff Lee] * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * 二叉搜索樹(BST)測試案例 {@link BinarySearchTree} * * Created by bysocket on 16/7/10. */ public class BinarySearchTreeTest { public static void main(String[] args) { BinarySearchTree b = new BinarySearchTree(); b.insert(3);b.insert(8);b.insert(1);b.insert(4);b.insert(6); b.insert(2);b.insert(10);b.insert(9);b.insert(20);b.insert(25); // 打印二叉樹 b.toString(b.root); System.out.println(); // 是否存在節點值10 TreeNode node01 = b.search(10); System.out.println("是否存在節點值爲10 => " + node01.value); // 是否存在節點值11 TreeNode node02 = b.search(11); System.out.println("是否存在節點值爲11 => " + node02); // 刪除節點8 TreeNode node03 = b.delete(8); System.out.println("刪除節點8 => " + node03.value); b.toString(b.root); } }
運行結果以下:
value = 1 -> value = 2 -> value = 3 -> value = 4 -> value = 6 -> value = 8 -> value = 9 -> value = 10 -> value = 20 -> value = 25 -> 是否存在節點值爲10 => 10 是否存在節點值爲11 => null 刪除節點8 => 8 value = 1 -> value = 2 -> value = 3 -> value = 4 -> value = 6 -> value = 9 -> value = 10 -> value = 20 -> value = 25 ->
與偶爾吃一碗「老壇酸菜牛肉麪」同樣的味道,品味一個算法,好比BST,的時候,老是那種說不出的味道。 樹,二叉樹的概念 BST算法 相關代碼分享在 Github 主頁
如以上文章或連接對你有幫助的話,別忘了在文章結尾處評論哈~ 你也能夠點擊頁面右邊「分享」懸浮按鈕哦,讓更多的人閱讀這篇文章。