java實現二叉搜索樹及二叉樹測試

java實現二叉搜索樹及二叉樹測試

二叉樹相關知識:

  1. 二叉樹是每個節點都不能有多於兩個兒子的樹
  2. 二叉樹的一個性質是一顆平均二叉樹的深度要比節點數N小得多
  3. 二叉樹的平均深度爲O(N\sqrt N)
  4. 二叉查找樹的平均深度爲O(logN\log N)
  5. 關於刪除節點
    a. 若刪除的節點是葉節點則直接刪除
    b. 若刪除的是有兩個兒子的節點則先用被刪除節點的右子樹的最小值覆蓋被刪除節點的值,之後珊瑚這個右子樹最小值節點【這個節點一定沒有左子樹,所以遞歸到此處直接執行c情況】
    c. 若刪除的是有一個兒子的節點則直接改變該節點的父節點的指向即可。
    d. 懶惰刪除:當一個節點要被刪除的時候依舊將其留在樹中,只是被標記爲被刪除。
實現代碼:
/**
 * 關於樹的實現
 * 已實現的方法:
 * 1) 遍歷【preorder,inorder,postorder】
 * 2) contains()
 * 3) findMin(),findMax()
 * 4) insertNode()
 * 5) removeNode()
 */

package tree;

public class MyBinaryTree<nodeType extends Comparable<? super nodeType>> {
    private BinaryNode<nodeType> mroot;

    public void setMroot(nodeType x) {
        mroot = new BinaryNode<>(x);
    }

    /**
     * 置空(初始化根節點)
     */
    public void makeEmpty() {
        mroot = null;
    }

    public void BinarySearchTree() {
        this.mroot = null;
    }

    /**
     * 判斷是否爲空
     *
     * @return
     */
    public boolean isEmpty() {
        return mroot == null;
    }

    /**
     * 樹節點數據結構
     * 與鏈表的節點類一樣,是一個嵌套類
     * 實現Comparable接口以實現比較功能
     *
     * @param <nodeType>
     */
    public static class BinaryNode<nodeType> {
        //數據定義:
        nodeType element;  //節點數據
        BinaryNode left;  //左節點
        BinaryNode right;  //右節點

        //Constructor
        public BinaryNode(nodeType element, BinaryNode left, BinaryNode right) {
            this.element = element;
            this.left = left;
            this.right = right;
        }

        public BinaryNode(nodeType element) {
            this(element, null, null);
        }
    }

    /**
     * 先序遍歷 NLR
     *
     * @param root 被遍歷的根節點
     */
    public void preoder(BinaryNode<nodeType> root) {
        if (root != null) {
            System.out.print(root.element + "\t");
            preoder(root.left);
            preoder(root.right);
        }
    }

    public void preorder() {
        preoder(mroot);
    }

    /**
     * 中序遍歷 LNR
     *
     * @param root
     */
    public void inorder(BinaryNode<nodeType> root) {
        if (root != null) {
            inorder(root.left);
            System.out.print(root.element + "\t");
            inorder(root.right);
        }
    }

    public void inorder() {
        inorder(mroot);
    }

    /**
     * 後序遍歷 LRN
     *
     * @param root 被遍歷的節點
     */
    public void postorder(BinaryNode<nodeType> root) {
        if (root != null) {
            postorder(root.left);
            postorder(root.right);
            System.out.print(root.element + "\t");
        }
    }

    public void postorder() {
        postorder(mroot);
    }

    /**
     * 檢查樹中是否包含元素x
     * 使用尾遞歸,可以用while代替:
     * 尾遞歸可以通過將代碼放到一個while循環中並且每個方法參數的一次賦值代替遞歸調用而被手工消除
     *
     * @param x    要搜索的元素
     * @param node 每個子樹的根節點
     * @return
     */
    public boolean contains(nodeType x, BinaryNode<nodeType> node) {
        //尾遞歸
        /*
        if (node == null)
            return false;
        if (x.compareTo(node.element) < 0)
            return contains(x,node.left);
        else if (x.compareTo(node.element) > 0)
            return contains(x,node.right);
        else return true;
        */
        //將尾遞歸改爲while
        while (true) {
            if (node == null) return false;
            if (x.compareTo(node.element) < 0)
                node = node.left;
            else if (x.compareTo(node.element) > 0)
                node = node.right;
            else
                return true;
        }
    }

    public boolean contains(nodeType x) {
        return contains(x, mroot);
    }

    /**
     * 尋找最小節點
     *
     * @param node 每個子樹的根節點
     * @return 返回最小節點
     */
    public BinaryNode findMin(BinaryNode<nodeType> node) {
        //if (isEmpty())  throw new BufferUnderflowException();
       /* while (true){
            if (root.left != null){
                root = root.left;
            }else {
                return root.element;
            }
        }*/
        if (node == null) return null;
        if (node.left == null) return node;
        return findMin(node.left);
    }

    public nodeType findMin() {
        return (nodeType) findMin(mroot).element;
    }

    /**
     * 尋找最大節點
     *
     * @param node 每個子樹的根節點
     * @return 返回最小節點
     */
    public BinaryNode findMax(BinaryNode<nodeType> node) {
        while (true) {
            if (node == null)
                return null;
            if (node.right == null)
                return node;
            else node = node.right;
        }
    }

    public nodeType findMax() {
        return (nodeType) findMax(mroot).element;
    }

    /**
     * 插入節點
     *
     * @param x    要插入的元素
     * @param root 根節點
     */
    public BinaryNode<nodeType> insertNode(nodeType x, BinaryNode<nodeType> root) {
        //若節點爲空【包含根節點爲空,root.left爲空,root.right爲空的情況】
        if (root == null)
            return new BinaryNode<nodeType>(x);
        if (x.compareTo(root.element) < 0)
            root.left = insertNode(x, root.left);
        else if (x.compareTo(root.element) > 0)
            root.right = insertNode(x, root.right);
        else
            ;  //重複值,不做任何操作
        return root;
    }

    /**
     * 在外部訪問insert()
     *
     * @param x
     */
    public void insertNode(nodeType x) {
        insertNode(x, mroot);
    }

    /**
     * 刪除節點,
     * 1) 葉節點直接刪除
     * 2) 子節點先用右子樹最小的值替換,再刪除幾點
     * 3) 懶惰刪除:被刪除節點還在樹中,只是標記爲被刪除
     *
     * @param x    被刪除的節點的element
     * @param root
     */
    public BinaryNode removeNode(nodeType x, BinaryNode<nodeType> root) {
        if (root == null)
            return root;  //未找到元素
        if (root.element.compareTo(x) < 0)
            root.right = removeNode(x, root.right);      //是否可以改寫爲 return removeNode(x, root.right)?
        else if (root.element.compareTo(x) > 0)
            root.left = removeNode(x, root.left);
        else if (root.right != null && root.left != null) { //被刪除節點有兩個子節點的情況
            root.element = (nodeType) findMin(root.right).element;
            root.right = removeNode(root.element, root.right);
        } else {
            //這裏包含了沒有子節點的情況,即root.rigth == root.left == null的情況
            //這裏也包含了只有右節點或者左節點的情況
            //只有此處修改了root,其他地方在不修改其作用域內的root值【不止修改element】
            root = (root.left == null) ? root.right : root.left;
        }
        return root;
    }

    public nodeType removeNode(nodeType x) {
        return (nodeType) removeNode(x, mroot).element;
    }
}
測試代碼:
/**
 * 二叉搜素樹測試
 */

package tree.test;

import tree.MyBinaryTree;

public class MyBinaryTreeTest {
    private static final int[] arr = {1, 4, 5, 2, 6};

    public static void main(String[] args) {
        MyBinaryTree<Integer> tree = new MyBinaryTree<Integer>();
        System.out.println("== 依次添加:");
        tree.setMroot(3);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + "\t");
            tree.insertNode(arr[i]);
        }
        System.out.print("\n先序遍歷:");
        tree.preorder();
        System.out.print("\n中序遍歷:");
        tree.inorder();
        System.out.print("\n後序遍歷:");
        tree.postorder();
        System.out.println();
        boolean contains = tree.contains(5);
        System.out.println(contains);
        System.out.println("MinValue:" + tree.findMin());
        System.out.println("MaxValue:" + tree.findMax());
        System.out.println("刪除節點:" + tree.removeNode(5));
        System.out.print("刪除節點後先序遍歷:");
        tree.preorder();
        System.out.print("\n刪除節點後中序遍歷:");
        tree.inorder();
        System.out.print("\n刪除節點後後序遍歷:");
        tree.postorder();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章