標籤:
好幾天沒把筆記整理上來了,一個是這幾天在忙著一些投標以及項目論文的事情,哈哈,還有麼 就是畫圖也是比較耗費我時間的,如果有好的畫圖建議或者工具,麻煩推薦一下,謝謝。廢話不多說,直接進入今天的兩個主題:二叉搜尋樹,平衡二叉樹。
1.二叉搜尋樹(BST)
二叉搜尋樹的定義很簡單:是二叉樹,且滿足兩個條件:如果有左孩子,則左孩子的關鍵字一定不大於父節點的關鍵字,如果有右孩子,則右孩子的關鍵字不小於父節點的關鍵字。同時,左右子樹都是二叉搜尋樹。
BST_1
中序遍曆得到的結果是:1、2、3、4、5、6、7、8、9、10
BST_2
中序遍曆得到的結果是:1、2、3、4、5、6、7、8、9、10
現在有一個情境,加入我要在這顆樹中尋找一個關鍵字11,如果沒有的話,則加入到這顆樹中,查詢步驟是:
1.針對BST_1,根節點是6,因為11大於6,則尋找根節點的右孩子(因為二叉搜尋樹的右孩子的關鍵字不小於父節點);
2.右孩子的值是9,小於11,繼續查他的右孩子;
3.右孩子的值是10,小於11,繼續查他的右孩子;
4.因為沒有右孩子了,所以,需要將其添加為右孩子。
針對BST_2,我們只需要一直找右孩子就可以了。我們再看一個二叉搜尋樹:
BST3
中序遍曆得到的結果是:1、2、3、4、5、6、7、9、10
現在我要尋找一個節點,其中的關鍵字是8的一個節點,如果不在這顆樹中,則把它加到樹中作為一個節點:
1.根節點是6,小於8,找右孩子;
2.右孩子的值是9大於8,找他的左孩子;
3.左孩子的值7小於8,找他的右孩子;
4.因為沒有右孩子了,所以,將其添加為7的右孩子。
從上面幾個過程中我們發現一個規律:動態尋找過程中,我們不需要重構這個結構,只是在葉子節點上進行操作,非常方便。
那如果要刪除一個節點呢?
刪除節點的時候,有幾種情況需要考慮:
1.該節點是葉子節點,因此,不用重構;
2.該節點只有左子樹或者右子樹,則把左子樹或者右子樹的根,接到刪除節點的父節點上就可以了;
3.如果該節點兩者都有呢?
我們一個一個的來看:
1.該節點是葉子節點。
我們要刪除BST3中的節點7,則先按照查詢步驟找到這個節點,發現他沒有左子樹,也沒有右子樹,則直接刪除,並把該節點的父節點的左孩子或者右孩子(取決於該節點是左孩子還是右孩子)清空即可。
2.該節點只有左子樹。
我們要刪除BST3中的節點4,則先按照查詢步驟找到這個節點,發現他只有左子樹,因此,只要刪除該節點,並把該節點的左孩子放在改節點的位置即可。
3.該節點只有右子樹:
我們要刪除BST2中的節點5,則先按照查詢步驟找到這個節點,發現他只有由子樹,因此,只要刪除該節點,並把該節點的右孩子放在該節點的位置即可。
4.該節點有左子樹,也有右子樹。
我們現在要刪除BST1中的根節點,則中序遍曆這個節點的左子樹,得到的此節點前的那個節點,就是要刪除節點的前驅。
比如上述根節點的左子樹的中序遍曆是:1、2、3、5,則,我是要刪除節點6的前驅,將該前驅替代要刪除的節點的位置,並設定左指向和右指向即可。
,我們現在再來看一BST。
現在我要刪除節點9,按照上面的步驟,9有左右孩子,因此,先做9的左子樹的中序遍曆,找到9的前驅是8.5,然後來替換。最終得到結果是:
,說了那麼多,還是上上代碼吧,這次用java代碼:
package com.tang.bst;import java.util.ArrayList;import java.util.Iterator;import java.util.List;public class BST<Key extends Comparable<Key>> {BSTNode<Key> root;public BST() {}/** * 插入一個節點 * * @param data * 節點的資料 * @return */public boolean insert(Key data) {if (null == data)return false;if (null == this.root) {this.root = new BSTNode<Key>(data);this.root.parent = null;this.root.leftChild = null;this.root.rightChild = null;return true;} else {return insertBST(this.root, data);}}private boolean insertBST(BSTNode<Key> node, Key data) {// 左分支,右分支的判斷int lOrR = node.data.compareTo(data);if (lOrR == 0) {return false;} else if (lOrR < 0) {// 右子樹if (null == node.rightChild) {BSTNode<Key> rChild = new BSTNode<Key>(data);rChild.parent = node;node.rightChild = rChild;return true;} else {return insertBST(node.rightChild, data);}} else {// 左子樹if (null == node.leftChild) {BSTNode<Key> lChild = new BSTNode<Key>(data);lChild.parent = node;node.leftChild = lChild;return true;} else {return insertBST(node.leftChild, data);}}}/** * 在這棵樹中,指定一個節點,然後開始非遞迴方式中序遍曆以這個節點為根節點的二叉樹 * * @param node * 指定為根節點的節點 * @return */public List<BSTNode<Key>> noRecurseInOrderTraverse(Key data) {if (null == this.root)return null;BSTNode<Key> beginRoot = null;if (null == data) {beginRoot = this.root;// 如果沒有指定節點為根節點,那麼就從這棵樹的根節點開始} else {BSTNode<Key> sNode = searchBST(this.root, data);beginRoot = sNode;}List<BSTNode<Key>> stack = new ArrayList<BSTNode<Key>>();List<BSTNode<Key>> retStack = new ArrayList<BSTNode<Key>>();if (null != beginRoot) {stack.add(beginRoot);}while (!stack.isEmpty()) {while (beginRoot != null && null != beginRoot.leftChild) {beginRoot = beginRoot.leftChild;stack.add(beginRoot);}if (!stack.isEmpty()) {BSTNode<Key> tmpNode = stack.get(stack.size() - 1);stack.remove(stack.size() - 1);retStack.add(tmpNode);if (tmpNode != null && null != tmpNode.rightChild) {beginRoot = tmpNode.rightChild;stack.add(beginRoot);}}}return retStack;}/** * 尋找指定的節點,如果沒有,則將他加入到這顆樹上 * * @param data * @return 0:表示在原樹上就有,1:表示添加進去的新節點,-1:表示查詢中出錯 */public boolean search(Key data) {if (null == data) {return false;}if (null == this.root) {return false;}return searchBST(this.root, data) == null ? false : true;}private BSTNode<Key> searchBST(BSTNode<Key> searhNode, Key data) {if (null == data) {return null;}if (null == searhNode) {return null;}int lOrR = searhNode.data.compareTo(data);if (lOrR > 0) {// 要往左子樹去找return searchBST(searhNode.leftChild, data);} else if (lOrR == 0) {// 已經找到了,直接返回去return searhNode;} else {// 要往右子樹去找return searchBST(searhNode.rightChild, data);}}/** * 尋找指定的節點,如果沒有的話,插入節點 * * @param data * @return */public boolean searchOrInsert(Key data) {return search(data) == false ? insert(data) : true;}/** * 刪除指定的節點 * * @param data * @return */public boolean delete(Key data) {if (null == this.root || null == data) {return false;}BSTNode<Key> node = searchBST(this.root, data);if (null == node) {return false;}BSTNode<Key> parent = node.parent;BSTNode<Key> leftChild = node.leftChild;if (null == node.rightChild) {// 因為沒有右孩子,因此,只要重新接上她的左孩子就可以了if (null == parent) {// 說明他是根節點if (null != leftChild) {node.leftChild.parent = null;} else {this.root = null;}} else {node.parent.leftChild = leftChild;}} else if (null == node.leftChild) {// 因為沒有左孩子,只要重新接上她的右孩子就可以了if (parent == null) {// 說明他就是根節點if (null != node.rightChild) {node.rightChild.parent = null;} else {this.root = null;}} else {node.parent.rightChild = node.rightChild;}} else {// 既有左子樹,又有右子樹// 中序遍曆此節點的左子樹,找到此節點的前驅// 此前驅的特點是,要麼是葉子節點,要麼是只有左節點System.out.println(node.rightChild==null);List<BSTNode<Key>> stack=noRecurseInOrderTraverse(node.leftChild.data);BSTNode<Key> preNode=stack.get(stack.size()-1);BSTNode<Key> rightNode=node.rightChild;node.data=preNode.data;if(preNode.leftChild!=null){node.leftChild=preNode.leftChild;}if(preNode.parent!=null){if(preNode.parent.leftChild.data.compareTo(preNode.data)==0){preNode.parent.leftChild=null;}else{preNode.parent.rightChild=null;//這裡有問題}}node.rightChild=rightNode;System.out.println(node.rightChild==null);}return true;}public static void main(String[] args) {BST<Integer> bst = new BST<Integer>();bst.insert(new Integer(6));bst.insert(new Integer(5));bst.insert(new Integer(9));bst.insert(new Integer(2));bst.insert(new Integer(8));bst.insert(new Integer(10));bst.insert(new Integer(1));bst.insert(new Integer(4));bst.insert(new Integer(3));List<BSTNode<Integer>> stack = bst.noRecurseInOrderTraverse(null);for (Iterator<BSTNode<Integer>> iterator = stack.iterator(); iterator.hasNext();) {BSTNode<Integer> bstNode = (BSTNode<Integer>) iterator.next();System.out.print(bstNode.data + "---");}bst.delete(new Integer(5));System.out.println("刪除之後");stack = bst.noRecurseInOrderTraverse(null);for (Iterator<BSTNode<Integer>> iterator = stack.iterator(); iterator.hasNext();) {BSTNode<Integer> bstNode = (BSTNode<Integer>) iterator.next();System.out.print(bstNode.data + "---");}}}
通過上面的學習,我們可以看到,二叉搜尋樹受輸入順序影響很大,有可能就是形成了一個線性表,效率非常低。咋整呢?收看下一節,歡迎加qq:359311095討論
Redis研究-3.4 為何使用Redis跳躍表