二叉排序樹
二叉排序樹(Binary Sort Tree),又稱二叉查詢樹(Binary Search Tree),亦稱二叉搜尋樹。是資料結構中的一類。在一般情況下,查詢效率比連結串列結構要高。
-
當左子樹不為空時,左子樹上的所有節點值都小於左子樹的根節點值
-
當右子樹不為空時,右子樹上的所有節點值都小於右子樹的根節點值
-
如果二叉樹中有相同值節點時,可以放在它的左子節點或右子節點(如果不是開發需要,儘量不要有相同值的節點)
插入操作
步驟:
(1) 判斷根節點是否為空,如果為空則將插入節點設定為根節點
(2.1) 判斷插入節點值是否小於當前節點值,如果小於則往左節點走
(2.2) 當往左節點走時判斷左節點是否為空,如為空則將左節點設定為插入節點,不為空則跳到步驟(2)
(3.1) 判斷插入節點值是否大於當前節點值,如果大於則往右節點走
(3.2) 當往右節點走時判斷右節點是否為空,如為空則將右節點設定為插入節點,不為空則跳到步驟(2)
下圖是將 [6, 2, 7, 1, 4, 8] 陣列按順序插入二叉排序樹的過程圖示:
程式碼實現
BSTNode root;//二叉排序樹的根節點 public void add(BSTNode node){ //如果根節點為空則,則將傳入節點設定為根節點 if (root == null){ root = node; }else { add(node, root); } } /** * 在二叉排序樹中新增節點 * @param node 新增的節點 * @param pointer 輔助指標節點,初始指向根節點 */ public void add(BSTNode node, BSTNode pointer){ if (node == null){ return; } if (pointer.value > node.value){//指標節點值大於新增節點值時 //如果指標節點的左節點剛好為空,則將新增節點插入到該左節點 if (pointer.left == null){ pointer.left = node; }else { //如果不是則繼續往左節點走 add(node, pointer.left); } }else {//指標節點值小於新增節點值時 //如果指標節點的右節點剛好為空,則將新增節點插入到該右節點 if (pointer.right == null){ pointer.right = node; }else { //如果不是則繼續往右節點走 add(node, pointer.right); } } }
查詢操作
步驟:
(1) 判斷當前節點是否是查詢節點,如果是則直接返回當前節點
(2) 判斷當前節點值是否大於查詢節點值,如果大於則往左節點查詢,跳到步驟(1)
(3) 判斷當前節點值是否小於查詢節點值,如果小於則往右節點查詢,跳到步驟(1)
下圖是從二叉排序樹中查詢值為4的節點的圖示:
程式碼實現
//根據value值查詢節點 public BSTNode searchNode(int value){ if (root == null){ return null; } return searchNode(value, root); } /** * 根據value值查詢節點 * @param value 查詢的節點 * @param node 查詢的樹 * @return */ public BSTNode searchNode(int value, BSTNode node){ //如果當前節點的值等於value時,則返回該節點 if (node.value == value) { return node; } else if (node.value > value){//當前節點的值大於value時 //如果該節點的左節點為空,則表示二叉排序樹內沒有該值的節點,返回空 if (node.left == null) return null; //左節點不為空,繼續往左子樹查詢 return searchNode(value, node.left); }else {//當前節點的值小於value時 //如果該節點的右節點為空,則表示二叉排序樹內沒有該值的節點,返回空 if (node.right == null) return null; //右節點不為空,繼續往右子樹查詢 return searchNode(value, node.right); } }
刪除操作
刪除節點可能有的3中狀態:
-
刪除節點是葉子節點
-
刪除節點只有左子樹為空或右子樹為空
-
刪除節點左子樹和右子樹都為空
步驟:
(1) 判斷刪除節點值是否小於當前節點值,如小於則往左節點走
(2) 判斷刪除節點值是否大於當前節點值,如大於則往右節點走
(3) 當刪除節點值等於當前節點值時,即當前節點時要刪除的節點,判斷當前節點是什麼狀態
(3.1) 當前節點是葉子節點時,則直接刪除當前節點
(3.2) 當前節點左子樹為空時,則將右節點頂上,代替當前節點位置
(3.3) 當前節點右子樹為空時,則將左節點頂上,代替當前節點位置
(3.4) 當前節點左子樹和右子樹都不為空時,則將左子樹的最大值節點取出後刪除,再用最大值節點頂替當前節點位置
/** * 根據value值刪除節點 * 刪除節點可能有的3種狀態: * 1.該節點是葉子節點 * 2.該節點只有左子樹或只有右子樹 * 3.該節點左子樹和右子樹都有 * @param value 要刪除節點的value值 */ public BSTNode delete(int value, BSTNode node){ if (value < node.value){//當查詢節點值小於當前節點值 //向左子樹遞迴遍歷,並將刪除後的新的左子樹連線到左節點位置代替原先左子樹 node.left = delete(value, node.left); //返回刪除後新的樹 return node; }else if(value > node.value){//當查詢節點值大於當前節點值 //向右子樹遞迴遍歷,並將刪除後的新的右子樹連線到右節點位置代替原先右子樹 node.right = delete(value, node.right); //返回刪除後新的樹 return node; }else {//當查詢節點值等於當前節點值時,即當前節點就是要刪除的節點 //刪除節點時葉子節點的狀態 if (node.left == null && node.right == null){ //直接將該節點設為空 return null; } //刪除節點左子樹為空,右子樹不為空的狀態 else if (node.left == null && node.right != null){ //儲存刪除節點的右子樹 BSTNode rightSubTree = node.right; //將刪除節點的右子樹設為空,使得該節點能夠儘早被垃圾回收 node.right = null; //返回刪除節點的右子樹,連線到刪除節點的父節點 return rightSubTree; } //刪除節點右子樹為空,左子樹不為空的狀態 else if (node.right == null && node.left != null){ BSTNode leftSubTree = node.left; node.left = null; return leftSubTree; } //刪除節點的左子樹和右子樹都不為空的狀態 //這裡我們使用的是左子樹的最大值節點代替的方法 else { //獲取左子樹的最大值節點並從左子樹中刪除它 BSTNode max = max(node.left); //將該最大值節點的左子樹和右子樹設定為該節點的左子樹和右子樹 max.left = delMax(node.left); max.right = node.right; //將刪除節點的左子樹和右子樹設為空,使得該節點能夠儘早被垃圾回收 node.left = null; node.right = null; //執行完刪除操作後,返回以最大值節點為根節點的新的樹,連線的刪除節點的父節點 return max; } } } /** * 查詢傳入節點樹下value值最大的節點並刪除該節點 * @param node * @return */ public BSTNode delMax(BSTNode node){ if (node.right != null){ node.right = delMax(node.right); return node; }else { BSTNode leftSubTree = node.left; node.left = null; return leftSubTree; } } /** * 查詢傳入節點樹下value值最大的節點並放回該節點 * 在二叉排序樹中最大值的節點就是最右葉子節點 * @param node * @return */ public BSTNode max(BSTNode node){ BSTNode max = node; while (max.right != null){ max = max.right; } return max; }
public class BinarySortTreeDemo { public static void main(String[] args) { int array[] = {13,7,8,3,29,6,1}; BinarySortTree binarySortTree = new BinarySortTree(); for (int i=0; i<array.length; i++){ binarySortTree.add(new BSTNode(array[i])); } binarySortTree.midOrder(); System.out.println("刪除後二叉順序樹的節點:"); binarySortTree.delete(13); binarySortTree.delete(7); binarySortTree.delete(1); binarySortTree.delete(29); binarySortTree.delete(6); binarySortTree.midOrder(); } } //二叉排序樹 class BinarySortTree{ BSTNode root; public void setRoot(BSTNode root){ this.root = root; } //新增節點 public void add(BSTNode node){ //如果根節點為空則,則將傳入節點設定為根節點 if (root == null){ root = node; }else { add(node, root); } } /** * 在二叉排序樹中新增節點 * @param node 新增的節點 * @param pointer 指標節點,初始指向根節點 */ public void add(BSTNode node, BSTNode pointer){ if (node == null){ return; } if (pointer.value > node.value){//指標節點值大於新增節點值時 //如果指標節點的左節點剛好為空,則將新增節點插入到該左節點 if (pointer.left == null){ pointer.left = node; }else { //如果不是則繼續往左節點走 add(node, pointer.left); } }else {//指標節點值小於新增節點值時 //如果指標節點的右節點剛好為空,則將新增節點插入到該右節點 if (pointer.right == null){ pointer.right = node; }else { //如果不是則繼續往右節點走 add(node, pointer.right); } } } //根據value值查詢節點 public BSTNode searchNode(int value){ if (root == null){ return null; } return searchNode(value, root); } /** * 根據value值查詢節點 * @param value 查詢的節點 * @param node 查詢的樹 * @return */ public BSTNode searchNode(int value, BSTNode node){ //如果當前節點的值等於value時,則返回該節點 if (node.value == value) { return node; } else if (node.value > value){//當前節點的值大於value時 //如果該節點的左節點為空,則表示二叉排序樹內沒有該值的節點,返回空 if (node.left == null) return null; //左節點不為空,繼續往左子樹查詢 return searchNode(value, node.left); }else {//當前節點的值小於value時 //如果該節點的右節點為空,則表示二叉排序樹內沒有該值的節點,返回空 if (node.right == null) return null; //右節點不為空,繼續往右子樹查詢 return searchNode(value, node.right); } } public void delete(int value){ //判斷刪除節點在二叉排序樹中是否存在 BSTNode node = searchNode(value); if (node == null){ throw new RuntimeException("二叉排序樹內無對應節點"); } //將刪除後新的二叉排序樹更換掉原先二叉排序樹 root = delete(value, root); } /** * 根據value值刪除節點 * 刪除節點可能有的3種狀態: * 1.該節點是葉子節點 * 2.該節點只有左子樹或只有右子樹 * 3.該節點左子樹和右子樹都有 * @param value 要刪除節點的value值 */ public BSTNode delete(int value, BSTNode node){ if (value < node.value){//當查詢節點值小於當前節點值 //向左子樹遞迴遍歷,並將刪除後的新的左子樹連線到左節點位置代替原先左子樹 node.left = delete(value, node.left); //返回刪除後新的樹 return node; }else if(value > node.value){//當查詢節點值大於當前節點值 //向右子樹遞迴遍歷,並將刪除後的新的右子樹連線到右節點位置代替原先右子樹 node.right = delete(value, node.right); //返回刪除後新的樹 return node; }else {//當查詢節點值等於當前節點值時,即當前節點就是要刪除的節點 //刪除節點時葉子節點的狀態 if (node.left == null && node.right == null){ //直接將該節點設為空 return null; } //刪除節點左子樹為空,右子樹不為空的狀態 else if (node.left == null && node.right != null){ //儲存刪除節點的右子樹 BSTNode rightSubTree = node.right; //將刪除節點的右子樹設為空,使得該節點能夠儘早被垃圾回收 node.right = null; //返回刪除節點的右子樹,連線到刪除節點的父節點 return rightSubTree; } //刪除節點右子樹為空,左子樹不為空的狀態 else if (node.right == null && node.left != null){ BSTNode leftSubTree = node.left; node.left = null; return leftSubTree; } //刪除節點的左子樹和右子樹都不為空的狀態 //這裡我們使用的是左子樹的最大值節點代替的方法 else { //獲取左子樹的最大值節點並從左子樹中刪除它 BSTNode max = max(node.left); //將該最大值節點的左子樹和右子樹設定為該節點的左子樹和右子樹 max.left = delMax(node.left); max.right = node.right; //將刪除節點的左子樹和右子樹設為空,使得該節點能夠儘早被垃圾回收 node.left = null; node.right = null; //執行完刪除操作後,返回以最大值節點為根節點的新的樹,連線的刪除節點的父節點 return max; } } } /** * 查詢傳入節點樹下value值最大的節點並刪除該節點 * @param node * @return */ public BSTNode delMax(BSTNode node){ if (node.right != null){ node.right = delMax(node.right); return node; }else { BSTNode leftSubTree = node.left; node.left = null; return leftSubTree; } } /** * 查詢傳入節點樹下value值最大的節點並放回該節點 * 在二叉排序樹中最大值的節點就是最右葉子節點 * @param node * @return */ public BSTNode max(BSTNode node){ BSTNode max = node; while (max.right != null){ max = max.right; } return max; } public void midOrder(){ if (root != null){ midOrder(root); }else { System.out.println("二叉順序樹為空,無法遍歷"); } } //中序遍歷 public void midOrder(BSTNode node){ if (node.left != null){ midOrder(node.left); } System.out.println(this); if (node.right != null){ midOrder(node.right); } } } //二叉排序樹節點 class BSTNode{ int value; BSTNode left; BSTNode right; public BSTNode(int value){ this.value = value; } @Override public String toString() { return "BSTNode{" + "value=" + value + '}'; } }