二叉查找樹 Java實現

定義: 一棵二叉查找樹是一棵二叉樹,每一個節點都含有一個Comparable的鍵(以及對應的值)。 每一個節點的鍵都大於左子樹中任意節點的鍵而小於右子樹中任意節點的鍵。node

image

樹的術語:ide

Name Function
路徑 順着鏈接點的邊從一個節點走向另外一個節點,所通過的節點的順序排列就稱爲路徑。
樹頂端的節點就稱爲根,一棵樹只有一個根,若是要把一個節點和邊的集合定義爲樹,那麼從根到其餘任何一個節點都必須有一條路徑。
父節點 每一個節點(除了根)都剛好有一條邊向上鏈接到另外一個節點,上面的節點就稱爲下面節點的「父節點」。
子節點 每一個節點均可能有一條或多條邊向下鏈接其餘節點,下面的這些節點就稱爲它的「子節點」。
葉節點 沒有子節點的節點稱爲「葉子節點」或簡稱「葉節點」。樹只能有一個根,可是能夠有不少葉節點。
子樹 每一個節點均可以做爲子樹的根,它和它全部的子節點,子節點的子節點等都含在子樹中。
訪問 當程序控制流程到達某個節點的時候,就稱爲「訪問」這個節點,一般是爲了在這個節點處執行某種操做,例如查看節點某個數據字段的值或者顯示節點。
遍歷 遍歷樹意味着要遵循某種特定的順序訪問樹中的全部節點。
一個節點的層數是指從根開始到這個節點有多少「代」。
關鍵字 能夠看到,對象中一般會有一個數據域被指定爲關鍵字值。這個值一般用於查詢或者其餘操做。
二叉樹 若是樹中的每一個節點最多隻能有兩個子節點,這樣的樹就稱爲「二叉樹」。

性質:ui

  1. 若它的左子樹不爲空,則左子樹上的全部節點的值都小於它的根節點的值;
  2. 若它的右子樹不爲空,則右子樹上全部節點的值都大於它的根節點的值;
  3. 其餘的左右子樹也分別爲二叉查找樹;
  4. 二叉查找樹是動態查找表,在查找的過程當中可見添加和刪除相應的元素,在這些操做中須要保持二叉查找樹的以上性質。

根據其二叉樹的特性,節點類以下:this

public class Node {
    public int index;//關鍵字段
    public String data;//值
    public Node leftNode;//左節點
    public Node rightNode;//右節點

    @Override
    public boolean equals(Object obj) {
        return EqualsBuilder.reflectionEquals(this, obj);
    }

    @Override
    public int hashCode() {
        return HashCodeBuilder.reflectionHashCode(this);
    }
}

其中引用了commons-lang3包中的內容,做爲對象進行比較code

查找某個節點,至關於二分查找,若是小於當前節點,則走左邊,若是大於當前節點,則走右邊。當最後葉子節點尚未找到,則沒有找到對象

public Node findNode(int key){
    Node current = root;
    while(current.index != key){
        if(key < current.index){//左節點
            current = current.leftNode;
        }else{//右節點
            current = current.rightNode;
        }
        if(current == null){
            return null;
        }
    }
    return current;
}

插入節點,按照插入的節點都不會出現重複關鍵字。每一次插入都將變爲根節點的子節點,例如根節點關鍵字爲1,接下來插入的節點則變爲根節點的右子節點。blog

public void insertNode(int key,String value){
    Node node = new Node();
    node.index = key;
    node.data = value;
    
    if(root == null){
        root = node;
        return;
    }
    //找到插入節點的位置
    Node parent = root;
    Node current = root;
    while(true){
        parent = current;
        if(key == current.index){
            return;
        }
        if(key < current.index){//左節點
            current = current.leftNode;
            if(current == null){//當前節點已是葉子結點了
                parent.leftNode = node;    
                return;
            }
        }else{
            current = current.rightNode;
            if(current == null){
                parent.rightNode = node;
                return;
            }
        }
    }
}

遍歷節點,中序遍歷.索引

  1. 調用自身來遍歷節點的左子樹
  2. 訪問這個節點
  3. 掉用自身來遍歷節點的右子樹
public void inOrder(Node localRoot) {
    if (localRoot != null) {
        inOrder(localRoot.leftNode);
        System.out.println("索引:" + localRoot.index + ",值:" + localRoot.data);
        inOrder(localRoot.rightNode);
    }
}

刪除節點,分三種狀況:hash

  1. 刪除節點沒有子節點,那麼將父節點的左節點或者是右節點設置爲空
  2. 刪除節點只有一個子節點,刪除該節點後,該節點的子節點變爲父節點的子節點,若是刪除節點時父節點的左節點,那麼父節點的左節點指向該節點的子節點,反之則右節點指向刪除節點的子節點。
  3. 刪除節點有兩個字節點,刪除了該節點後,則須要選擇一個後繼節點,而且還不破壞該二叉樹的特性(左節點要小於右節點),通常是從刪除節點的右節點中找到一個後繼節點,而這個節點是右子樹的最小值。
public boolean delete(int key) {
        Node current = root;
        Node parent = root;
        boolean isLeftChild = true;
        //找到被刪除的節點,並標識該節點是否爲左節點
        while (current.index != key) {
            parent = current;
            if (key < current.index) {
                isLeftChild = true;
                current = current.leftNode;
            } else {
                isLeftChild = false;
                current = current.rightNode;
            }
            if (current == null) {
                return false;
            }
        }
        //第一種狀況,刪除節點爲子節點
        if (current.leftNode == null && current.rightNode == null) {
            if (current == root) {
                root = null;
            } else {
                if (isLeftChild) {
                    parent.leftNode = null;
                } else {
                    parent.rightNode = null;
                }
            }
        } else if ((current.leftNode != null && current.rightNode == null) || (current.leftNode == null && current.rightNode != null)) {
            //第二中狀況,刪除節點只包含一個子節點,則將子節點移動動當前節點中
            if (current.rightNode == null) {//刪除的節點的左節點有值,右節點爲空
                if (root == current) {
                    root = current.leftNode;
                } else {
                    if (isLeftChild) {
                        parent.leftNode = current.leftNode;
                    } else {
                        parent.rightNode = current.leftNode;
                    }
                }
            } else {//刪除的節點的右節點有值,左節點爲空
                if (root == current) {
                    root = current.rightNode;
                } else {
                    if (isLeftChild) {
                        parent.leftNode = current.rightNode;
                    } else {
                        parent.rightNode = current.rightNode;
                    }
                }
            }
        } else if (current.leftNode != null && current.rightNode != null) {
            //第三種狀況,刪除節點中有左右兩個節點
            //找到後繼節點
            Node processer = processer(current);
            if (current == root) {//刪除是根節點,則
                root = processer;
            } else {
                if (isLeftChild) {
                    parent.leftNode = processer;
                } else {
                    parent.rightNode = processer;
                }
            }
            //選中的節點的左節點與刪除節點的左節點相連
            processer.leftNode = current.leftNode;
        }
        return true;
    }
    
    //找到後繼節點
    private Node processer(Node delNode) {
        Node parent = delNode;
        Node success = delNode;
        Node current = delNode.rightNode;
        while (current != null) {
            // 後繼節點父節點首先保存後繼節點的狀態
            parent = current;
            success = current;
            // 後繼節點 不斷的向左更新
            current = current.leftNode;
        }
        // 假如咱們找到的後繼節點不直接是 要刪除節點的右節點  而是在其右節點那條子樹上面最小的一個節點
        if (success != delNode.rightNode) {
            //後繼節點的父節點斷開其與後繼節點左邊的引用,從新鏈接上後繼節點的右子節點(由於後繼節點是沒有左子節點的,鎖以要保存以前樹的狀態,還要把後繼節點的右子節點處理一下,無論 其存在不存在)
            parent.leftNode = success.rightNode;
            // 這時候後繼節點的右邊已經空了 上一條語句已經將其給了本身父節點的左子節點    而後讓後繼節點的右邊 鏈接要刪除節點的右子樹
            success.rightNode = delNode.rightNode;
        }
        return success;
    }
相關文章
相關標籤/搜索