數據結構之二叉查找樹及Java實現

一、二叉查找樹的介紹

二叉查找樹(Binary Search Tree),又被稱爲二叉搜索樹或二叉排序樹,是一種特殊的二叉樹,利用它可以很方便的對樹中節點進行排序和檢索。
二叉查找樹需要滿足以下的性質:

  • 若它的左子樹不爲空,則左子樹上的所有節點的值都小於根節點的值
  • 若它的右子樹不爲空,則右子樹上的所有節點的值都大於根節點的值
  • 它的左、右子樹也都是二叉查找樹

對於二叉查找樹,按照中序遍歷(左根右)就可以得到由小到大的序列。

二、二叉查找樹的實現

二叉查找樹與基於鏈表的二叉樹創建方法類似,不同之處在於添加了一個父節點,也就是說採用二叉樹的三叉鏈表存儲方法,每個節點包括left、right和parent指針,用於表示該節點的左右節點和父節點。二叉查找樹主要需要實現兩個功能:

  • 添加元素:添加元素後二叉樹依然有序,依然是二叉查找樹。
  • 刪除元素:刪除完成後二叉樹依然有序,依然是二叉查找樹。

1.添加元素

已知一個關鍵字值爲value的結點,若將其插入到二叉查找樹中,只要保證插入後仍符合二叉查找樹的定義即可。插入可以用下面的方法進行:

(1)若二叉排序樹是空樹,則value成爲二叉排序樹的根;
(2)若二叉排序樹非空,則將value與二叉排序樹的根進行比較。如果value的值小於根結點的值,則將左節點作爲新的當前節點,如果value的值大於根結點的值,則將右節點作爲新的當前節點。
(3)重複步驟2,直到找到合適的插入位置。

2.刪除元素

當程序從排序二叉樹中刪除一個節點之後,爲了讓它依然保持爲排序二叉樹,程序必須對該排序二叉樹進行維護。維護可分爲如下幾種情況:

(1)若被刪除的節點是葉子節點,則只需將它從其父節點中刪除即可。
(2)如果待刪除節點左子樹存在右子樹不存在,或者左子樹不存在右子樹存在,直接將其子樹中存在的一邊候補上來即可。
(3)若被刪除節點 P 的左、右子樹均非空,有兩種做法:

  • 將PL設爲P的父節點Q的左或右子節點(取決於P是其父節點Q的左或右子節點),將PR設爲P節點的中序前驅節點S的右子節點(S是PL最右下的節點,也就是PL子樹中最大的節點)。
  • 以P節點的中序前驅或後繼代替P所指節點,然後再從原排序二叉樹中刪去中序前驅或後繼節點。(也就是,用大於P的最小節點或小於P的最大節點代替P節點)。

實現代碼如下所示:

import java.util.ArrayList;
import java.util.List;


public class SortTree {

    class Node{
        private int data;
        private Node parent;
        private Node left;
        private Node right;
        public Node(int value){
            this.data = value;
            this.parent = null;
            this.left = null;
            this.right = null;
        }
    }
    private Node root = null;
    /**
     * 返回根節點
     * @return
     */
    public Node getRoot(){
        return root;
    }
    /**
     * 判斷是否爲空
     * @return
     */
    public boolean isEmpty(){
        return root==null;
    }
    /**
     * 返回樹的深度
     * @return
     */
    public int deep(){
        return deep(root);
    }
    private int deep(Node n){
        if(n == null){
            return 0;
        }
        if(n.left==null&&n.right==null){
            return 1;
        }
        //遞歸方式
        int deepLeft = deep(n.left);
        int deepRight = deep(n.right);
        return deepLeft>deepRight?deepLeft:deepRight;
    }
    //比較兩個值的大小,用於插入和刪除操作
    public int compare(int a,int b){
        return a>b?1:-1;
    }
    /**
     * 添加新節點
     * @param value 新節點的值
     * @return 返回是否成功
     */
    public boolean add(int value){
        //新節點newNode初始化
        Node newNode = new Node(value);
        //如果root爲空,則新節點爲root節點
        if(root == null){
            root = newNode;
            return true;
        }
        Node current = root;
        Node parent = null;
        int cmp = 0;
        //遍歷找到合適的插入節點位置
        do{
            parent = current;
            cmp = compare(value,current.data);
            if(cmp == 1){
                current = current.right;
            }
            else{
                current = current.left;
            }
        }
        while(current!=null);
        //設置新節點
        if(cmp == 1){
            parent.right = newNode;
            newNode.parent = parent;
        }
        else{
            parent.left = newNode;
            newNode.parent = parent;
        }
        return true;
    }
    /**
     * 找到某個值的節點,用於刪除操作
     * @param value 目標節點的值
     * @return 返回節點
     */
    public Node findNode(int value){
        if(root == null){
            return null;
        }
        Node curr = root;
        while(curr!=null){
            if(value<curr.data){
                curr = curr.left;
            }
            else if(value>curr.data){
                curr = curr.right;
            }
            else{
                return curr;
            }
        }
        return null;
    }
    public boolean del(int value){
        Node target = findNode(value);
        if(target == null){
            return false;
        }
        //如果刪除的節點沒有左節點也沒有右節點
        if(target.left==null&&target.right==null){
            //如果節點爲根節點
            if(target == root){
                root = null;
            }
            else{
                //要刪除的節點爲左節點
                if(target == target.parent.left){
                    target.parent.left = null;
                }
                //要刪除的節點爲右節點
                else{
                    target.parent.right = null;
                }
            }
        }
        //如果刪除的節點只有右節點
        if(target.left==null && target.right!=null){
            //要刪除的節點爲根節點
            if(target == root){
                root = target.right;
            }
            else{
                //要刪除的節點爲左節點
                if(target == target.parent.left){
                    target.parent.left = target.right;
                }
                //要刪除的節點爲右節點
                else{
                    target.parent.right = target.right;
                }
                target.right.parent = target.parent;
            }
        }
        //如果刪除的節點只有左節點
        if(target.left!=null&&target.right==null){
            if(target == root){
                root = root.left;
            }
            else{
                if(target == target.parent.left){
                    target.parent.left = target.left;
                }
                else{
                    target.parent.right = target.left;
                }
                target.left.parent = target.parent;
            }
        }
        //如果刪除的節點包含左節點和右節點
        //以P節點的中序前驅代替P所指節點,然後再從原排序二叉樹中刪去中序前驅節點,用大於P的最小節點代替P節點
        if(target.left!=null&&target.right!=null){
            //leftMaxNode用於保存左子樹中最大的節點
            Node leftMaxNode = target.left;
            //遍歷右節點,找到值最大的節點
            while(leftMaxNode.right!=null){
                leftMaxNode = leftMaxNode.right;
            }
            //如果target.left沒有右節點
            if(leftMaxNode == target.left){
                leftMaxNode.parent.left = null;

            }
            //如果找到了最大的右節點
            else{
                leftMaxNode.parent.right = null;

            }
            //左子樹的最大節點指向目標節點的父節點
            leftMaxNode.parent = target.parent;
            if(target ==target.parent.left){
                //如果目標節點爲左節點,左節點指向leftMaxNode
                target.parent.left = leftMaxNode;
            }
            else{
                //如果目標節點爲右節點,右節點指向leftMaxNode
                target.parent.right = leftMaxNode;
            }
            //leftMaxNode替代target的位置
            leftMaxNode.left = target.left;
            leftMaxNode.right = target.right;
            //target置空
            target.parent = target.left = target.right = null;
        }

        return true;
    }
    /**
     * 中序遍歷
     * @return 返回存儲Node節點的list
     */
    public List<Node> inIterator(){
        return inIterator(root);
    }
    public List<Node> inIterator(Node n){
        List<Node> list = new ArrayList<Node>();
        //遞歸方式
        if(n.left!=null){
            list.addAll(inIterator(n.left));
        }
        list.add(n);
        if(n.right!=null){
            list.addAll(inIterator(n.right));
        }
        return list;
    }

    public static void main(String args[]){
        SortTree st = new SortTree();
        /**
         *                 1
         *                      8
         *                  5      10 
         *               2    7  9    11
         *                3  6         
         */
        st.add(1);
        st.add(8);
        st.add(5);
        st.add(2);
        st.add(7);
        st.add(10);
        st.add(9);
        st.add(11);
        st.add(6);
        st.add(3);
        st.del(10);
        List<SortTree.Node> inlist = new ArrayList<SortTree.Node>();
        inlist = st.inIterator();
        for(SortTree.Node n:inlist){
            System.out.print(n.data + " ");
        }
        System.out.println();
    }

}

測試結果:

1 2 3 5 6 7 8 9 11 
發佈了67 篇原創文章 · 獲贊 32 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章