Java實作二元搜尋樹演算法的程式碼詳解(圖)
二元查找樹可以遞歸地定義如下,二叉查找樹或是空二叉樹,或是滿足下列性質的二元樹:
(1)若它的左子樹不為空,則其左子樹上任意結點的關鍵字的值都小於根結點關鍵字的值。
(2)若它的右子樹不為空,則其右子樹上任意結點的關鍵字的值都大於根節點關鍵字的值。
(3)它的左、右子樹本身又是一個二元查找樹。
從效能上來說如果二元尋找樹的所有非葉子結點的左右子樹的結點數目都保持差不多(平衡),那麼二元尋找樹的搜尋效能逼近二分查找;但它比連續記憶體空間的二分查找的優點是,改變二元查找樹結構(插入與刪除結點)不需要移動大段的記憶體數據,甚至通常是常數開銷。二元查找樹可以表示按順序序列排列的資料集合,因此二元查找樹也被稱為二元排序樹,並且同一個資料集合可以表示為不同的二元查找樹。二元查找樹的結點的資料結構定義為:
struct celltype{ records data; celltype * lchild, * rchild; } typedef celltype * BST;
在Java中,節點的資料結構定義如下:
package wx.algorithm.search.bst; /** * Created by apple on 16/7/29. */ /** * @function 二叉搜索树中的节点 */ public class Node { //存放节点数据 int data; //指向左子节点 Node left; //指向右子节点 Node right; /** * @function 默认构造函数 * @param data 节点数据 */ public Node(int data) { this.data = data; left = null; right = null; } }
查找
而二叉查找樹的查找過程為從根結點開始,如果查詢的關鍵字與結點的關鍵字相等,那麼就命中;否則,如果查詢關鍵字比結點關鍵字小,就進入左兒子;如果比結點關鍵字大,就進入右兒子;如果左兒子或右兒子的指針為空,則報告找不到相應的關鍵字。
BST Search(keytype k, BST F){ //在F所指的二叉查找树中查找关键字为k的记录。若成功,则返回响应结点的指针,否则返回空 if(F == NULL) //查找失败 return NULL; else if(k == F -> data.key){ //查找成功 return F; } else if (k < F -> data.key){ //查找左子树 return Search(k,F -> lchild); } else if (k > F -> data.key){ //查找右子树 return Search(k,F -> rchild); } }
插入
把一個新的記錄R插入到二元尋找樹,應該保證在插入之後不破壞二元尋找樹的結構性質。因此,為了執行插入操作首先應該查找R所在的位置。查找時,仍然採用上述的遞歸演算法。若查找失敗,則把包含R的結點插在具有空子樹位置,若查找成功,則不執行插入,操作結束。
void Insert(records R, BST &F){ //在F所指的二叉查找树中插入一个新纪录R if(F == NULL){ F = new celltype; F -> data = R; F -> lchild = NULL; F -> rchild = NULL; } else if (R.key < F -> data.key){ Insert(R,F -> lchild); }else if(R.key > F -> data.key){ Insert(R,F -> rchild); } //如果 R.key == F -> data.key 则返回 }
刪除
刪除葉節點
刪除只有一個子節點的內部節點
刪除有兩個子節點的內部節點
如果我們進行簡單的替換,那麼可能碰到如下情況:
#因此我們要在子樹中選擇一個合適的替換節點,替換節點一般來說會是右子樹中的最小的節點:
Java 實作
BinarySearchTree的Java版本程式碼參考BinarySearchTree:
package wx.algorithm.search.bst; /** * Created by apple on 16/7/29. */ /** * @function 二叉搜索树的示范代码 */ public class BinarySearchTree { //指向二叉搜索树的根节点 private Node root; //默认构造函数 public BinarySearchTree() { this.root = null; } /** * @param id 待查找的值 * @return * @function 默认搜索函数 */ public boolean find(int id) { //从根节点开始查询 Node current = root; //当节点不为空 while (current != null) { //是否已经查询到 if (current.data == id) { return true; } else if (current.data > id) { //查询左子树 current = current.left; } else { //查询右子树 current = current.right; } } return false; } /** * @param id * @function 插入某个节点 */ public void insert(int id) { //创建一个新的节点 Node newNode = new Node(id); //判断根节点是否为空 if (root == null) { root = newNode; return; } //设置current指针指向当前根节点 Node current = root; //设置父节点为空 Node parent = null; //遍历直到找到第一个插入点 while (true) { //先将父节点设置为当前节点 parent = current; //如果小于当前节点的值 if (id < current.data) { //移向左节点 current = current.left; //如果当前节点不为空,则继续向下一层搜索 if (current == null) { parent.left = newNode; return; } } else { //否则移向右节点 current = current.right; //如果当前节点不为空,则继续向下一层搜索 if (current == null) { parent.right = newNode; return; } } } } /** * @param id * @return * @function 删除树中的某个元素 */ public boolean delete(int id) { Node parent = root; Node current = root; //记录被找到的节点是父节点的左子节点还是右子节点 boolean isLeftChild = false; //循环直到找到目标节点的位置,否则报错 while (current.data != id) { parent = current; if (current.data > id) { isLeftChild = true; current = current.left; } else { isLeftChild = false; current = current.right; } if (current == null) { return false; } } //如果待删除的节点没有任何子节点 //直接将该节点的原本指向该节点的指针设置为null 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) { //寻找右子树中的最小值 Node successor = getSuccessor(current); if (current == root) { root = successor; } else if (isLeftChild) { parent.left = successor; } else { parent.right = successor; } successor.left = current.left; } return true; } /** * @param deleleNode * @return * @function 在树种查找最合适的节点 */ private Node getSuccessor(Node deleleNode) { Node successsor = null; Node successsorParent = null; Node current = deleleNode.right; while (current != null) { successsorParent = successsor; successsor = current; current = current.left; } if (successsor != deleleNode.right) { successsorParent.left = successsor.right; successsor.right = deleleNode.right; } return successsor; } /** * @function 以中根顺序遍历树 */ public void display() { display(root); } private void display(Node node) { //判断当前节点是否为空 if (node != null) { //首先展示左子树 display(node.left); //然后展示当前根节点的值 System.out.print(" " + node.data); //最后展示右子树的值 display(node.right); } } }
測試函數:
package wx.algorithm.search.bst; import org.junit.Before; import org.junit.Test; /** * Created by apple on 16/7/30. */ public class BinarySearchTreeTest { BinarySearchTree binarySearchTree; @Before public void setUp() { binarySearchTree = new BinarySearchTree(); binarySearchTree.insert(3); binarySearchTree.insert(8); binarySearchTree.insert(1); binarySearchTree.insert(4); binarySearchTree.insert(6); binarySearchTree.insert(2); binarySearchTree.insert(10); binarySearchTree.insert(9); binarySearchTree.insert(20); binarySearchTree.insert(25); binarySearchTree.insert(15); binarySearchTree.insert(16); System.out.println("原始的树 : "); binarySearchTree.display(); System.out.println(""); } @Test public void testFind() { System.out.println("判断4是否存在树中 : " + binarySearchTree.find(4)); } @Test public void testInsert() { } @Test public void testDelete() { System.out.println("删除值为2的节点 : " + binarySearchTree.delete(2)); binarySearchTree.display(); System.out.println("\n 删除有一个子节点值为4的节点 : " + binarySearchTree.delete(4)); binarySearchTree.display(); System.out.println("\n 删除有两个子节点的值为10的节点 : " + binarySearchTree.delete(10)); binarySearchTree.display(); } }
以上是Java實作二元搜尋樹演算法的程式碼詳解(圖)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

PHP適合web開發,特別是在快速開發和處理動態內容方面表現出色,但不擅長數據科學和企業級應用。與Python相比,PHP在web開發中更具優勢,但在數據科學領域不如Python;與Java相比,PHP在企業級應用中表現較差,但在web開發中更靈活;與JavaScript相比,PHP在後端開發中更簡潔,但在前端開發中不如JavaScript。

膠囊是一種三維幾何圖形,由一個圓柱體和兩端各一個半球體組成。膠囊的體積可以通過將圓柱體的體積和兩端半球體的體積相加來計算。本教程將討論如何使用不同的方法在Java中計算給定膠囊的體積。 膠囊體積公式 膠囊體積的公式如下: 膠囊體積 = 圓柱體體積 兩個半球體體積 其中, r: 半球體的半徑。 h: 圓柱體的高度(不包括半球體)。 例子 1 輸入 半徑 = 5 單位 高度 = 10 單位 輸出 體積 = 1570.8 立方單位 解釋 使用公式計算體積: 體積 = π × r2 × h (4

PHP和Python各有優勢,適合不同場景。 1.PHP適用於web開發,提供內置web服務器和豐富函數庫。 2.Python適合數據科學和機器學習,語法簡潔且有強大標準庫。選擇時應根據項目需求決定。

Java是熱門程式語言,適合初學者和經驗豐富的開發者學習。本教學從基礎概念出發,逐步深入解說進階主題。安裝Java開發工具包後,可透過建立簡單的「Hello,World!」程式來實踐程式設計。理解程式碼後,使用命令提示字元編譯並執行程序,控制台上將輸出「Hello,World!」。學習Java開啟了程式設計之旅,隨著掌握程度加深,可創建更複雜的應用程式。

PHP成為許多網站首選技術棧的原因包括其易用性、強大社區支持和廣泛應用。 1)易於學習和使用,適合初學者。 2)擁有龐大的開發者社區,資源豐富。 3)廣泛應用於WordPress、Drupal等平台。 4)與Web服務器緊密集成,簡化開發部署。
