(17)二叉樹和二叉搜索樹

本章介紹了內(nèi)容如下:

  • 二叉樹和完全二叉樹狞山,二叉搜索樹的概念
  • 二叉搜索樹的性質(zhì)
  • 二叉搜索樹的遍歷方式
  • 如何在二叉查找樹中查找最大值叉寂、最小值和給定的值萍启,
  • 如何找出某一個(gè)元素的前驅(qū)和后繼
  • 如何在二叉查找樹中進(jìn)行插入和刪除操作屏鳍。

在二叉搜索樹上執(zhí)行這些基本操作的時(shí)間與樹的高度成正比勘纯,一棵隨機(jī)構(gòu)造的二叉搜索樹的期望高度為O(lgn),從而基本動(dòng)態(tài)集合的操作平均時(shí)間為θ(lgn)钓瞭。

1. 二叉樹

二叉樹(Binary Tree)是一種特殊的樹型結(jié)構(gòu)驳遵,每個(gè)節(jié)點(diǎn)至多有兩棵子樹,且二叉樹的子樹有左右之分山涡,次序不能顛倒堤结。

由定義可知,二叉樹中不存在度(結(jié)點(diǎn)擁有的子樹數(shù)目)大于2的節(jié)點(diǎn)鸭丛。二叉樹形狀如下下圖所示:

二叉樹.png

二叉樹的性質(zhì)
(1)在二叉樹中的第i層上至多有2^(i-1)個(gè)結(jié)點(diǎn)(i>=1)
(2)深度為k的二叉樹至多有2^k-1個(gè)節(jié)點(diǎn)(k>=1)
(3)具有n個(gè)節(jié)點(diǎn)的完全二叉樹的深度為log2^n + 1竞穷。

滿二叉樹深度為k且具有2^k-1個(gè)結(jié)點(diǎn)的二叉樹。即滿二叉樹中的每一層上的結(jié)點(diǎn)數(shù)都是最大的結(jié)點(diǎn)數(shù)鳞溉。

完全二叉樹深度為k具有n個(gè)結(jié)點(diǎn)的二叉樹,當(dāng)且僅當(dāng)每一個(gè)結(jié)點(diǎn)與深度為k的滿二叉樹中的編號(hào)從1至n的結(jié)點(diǎn)一一對(duì)應(yīng)看政。
也就是除第 k層外允蚣,其它各層 (1~k-1) 的結(jié)點(diǎn)數(shù)都達(dá)到最大個(gè)數(shù),第 k層所有的結(jié)點(diǎn)都連續(xù)集中在最左邊榨崩,這就是完全二叉樹母蛛。

可以得到一般結(jié)論:滿二叉樹和完全二叉樹是兩種特殊形態(tài)的二叉樹前弯,滿二叉樹肯定是完全二叉樹恕出,但完全二叉樹不不一定是滿二叉樹浙巫。

舉例如下圖是所示:


二叉樹1.png

2的畴、二叉搜索樹的基本概念

二叉搜索樹是按照二叉樹結(jié)構(gòu)來組織的丧裁,因此可以用二叉鏈表結(jié)構(gòu)表示煎娇。

二叉樹與二叉搜索樹相比逊桦,二叉搜索樹需要滿足二叉搜索樹的性質(zhì)

二叉查找樹中的性質(zhì)
設(shè)x為二叉查找樹中的一個(gè)結(jié)點(diǎn)强经。如果y是x的左子樹中的一個(gè)結(jié)點(diǎn)匿情,則>key[y]≤key[x]汁果。如果y是x的右子樹中的一個(gè)結(jié)點(diǎn)据德,則key[x]≤key[y]

下面給出樹的節(jié)點(diǎn)的標(biāo)識(shí)的代碼

/**
 * @Project: 10.dataStructure
 * @description:  樹的節(jié)點(diǎn)
 * @author: sunkang
 * @create: 2018-08-19 15:08
 * @ModificationHistory who      when       What
 **/
public class TreeNode<E> {
    //存儲(chǔ)的值
    public E  key;
    public TreeNode<E> parent;
    public TreeNode<E> left;
    public TreeNode<E> right;
    @Override
    public String toString() {
        return "TreeNode{" +
                "key=" + key +
                ", parent=" + parent +
                ", left=" + left +
                ", right=" + right +
                '}';
    }
    public TreeNode(E key) {
        this.key = key;
    }
}

3棘利、二叉搜索樹的遍歷

二叉樹的遍歷方式有:前序遍歷善玫,中序遍歷茅郎,后續(xù)遍歷

中序遍歷是一種有序的遍歷方式系冗,
假設(shè)一個(gè)節(jié)點(diǎn)為p, 遍歷的方式為p-->p.left-->p.right

下面是遍歷的代碼實(shí)現(xiàn)

/**
     * 前序遍歷樹
     * @param node
     */
    public void preOrder(TreeNode<E> node){
        if(node != null){
            System.out.print(node.key+",");
            preOrder(node.left);
            preOrder(node.right);
        }
    }
    /**
     * 中序遍歷樹
     * @param node
     */
    public  void inOrder(TreeNode<E> node){
        if(node != null){
            inOrder(node.left);
            System.out.print(node.key+",");
            inOrder(node.right);
        }
    }
    /**
     * 后序遍歷樹
     * @param node
     */
    public  void postOrder(TreeNode<E> node){
        if(node != null){
            postOrder(node.left);
            postOrder(node.right);
            System.out.print(node.key+",");
        }
    }

4毕谴、二叉搜索樹的查詢

(1)查找

在二叉查找樹中查找一個(gè)給定的關(guān)鍵字k的過程與二分查找很類似,根據(jù)二叉查找樹在的關(guān)鍵字存放的特征框仔,很容易得出查找過程:首先是關(guān)鍵字k與樹根的關(guān)鍵字進(jìn)行比較离斩,如果k大比根的關(guān)鍵字大寻馏,則在根的右子樹中查找诚欠,否則在根的左子樹中查找轰绵,重復(fù)此過程左腔,直到找到與遇到空結(jié)點(diǎn)為止液样。例如下圖所示的查找關(guān)鍵字13的過程:(查找過程每次在左右子樹中做出選擇鞭莽,減少一半的工作量)

二叉樹查找.png

查找的遞歸代碼和非遞歸代碼如下

 /**
     * 查找特定值的節(jié)點(diǎn)
     * @param node
     * @param key
     * @return
     */
    public TreeNode<Integer> search(TreeNode<Integer> node, int key ){
        while (node != null && node.key != key){
            if(key < node.key){
                node = node.left;
            }else{
                node = node.right;
            }
        }
        return node;
    }

    /**
     * 通過遞歸的形式查找特定值的節(jié)點(diǎn)
     * @param node
     * @param key
     * @return
     */
    public TreeNode<Integer> searchByRecursion(TreeNode<Integer> node,int key){
        if( node == null || node.key == key){
            return node;
        }
        if(key <node.key){
           return  searchByRecursion(node.left,key);
        }else{
            return   searchByRecursion(node.right,key);
        }
    }
(2)查找最大關(guān)鍵字和最小關(guān)鍵字

根據(jù)二叉查找樹的特征,很容易查找出最大和最小關(guān)鍵字丹拯。查找二叉樹中的最小關(guān)鍵字:從根結(jié)點(diǎn)開始乖酬,沿著各個(gè)節(jié)點(diǎn)的left指針查找下去咬像,直到遇到NULL時(shí)結(jié)束县昂。如果一個(gè)結(jié)點(diǎn)x無左子樹倒彰,則以x為根的子樹中待讳,最小關(guān)鍵字就是key[x]创淡。查找二叉樹中的最大關(guān)鍵字:從根結(jié)點(diǎn)開始琳彩,沿著各個(gè)結(jié)點(diǎn)的right指針查找下去术辐,直到遇到NULL時(shí)結(jié)束
代碼如下:

   /**
     * 查找樹的最大節(jié)點(diǎn)
     * @param node
     * @return
     */
    public TreeNode<E> maximum(TreeNode<E> node){
        while(node!= null && node.right!=null){
            node = node.right;
        }
        return node;
    }

    /**
     * 查找樹的最小節(jié)點(diǎn)
     * @param node
     * @return
     */
    public TreeNode<Integer> minmum(TreeNode<Integer> node){
        while( node!= null && node.left !=null){
            node= node.left;
        }
        return node;
    }
(3)前驅(qū)和后繼

給定一個(gè)二叉查找樹中的結(jié)點(diǎn)辉词,找出在中序遍歷順序下某個(gè)節(jié)點(diǎn)的前驅(qū)和后繼瑞躺。如果樹中所有關(guān)鍵字都不相同幢哨,則某一結(jié)點(diǎn)x的前驅(qū)就是小于key[x]的所有關(guān)鍵字中最大的那個(gè)結(jié)點(diǎn)捞镰,后繼即是大于key[x]中的所有關(guān)鍵字中最小的那個(gè)結(jié)點(diǎn)岸售。

查找前驅(qū)步驟:先判斷x是否有左子樹凸丸,如果有則在left[x]中查找關(guān)鍵字最大的結(jié)點(diǎn)(左子樹最大)屎慢,即是x的前驅(qū)腻惠。如果沒有左子樹妖枚,則從x繼續(xù)向上執(zhí)行此操作绝页,直到遇到某個(gè)這樣一個(gè)節(jié)點(diǎn)续誉,該節(jié)點(diǎn)的左子樹的最大值即為x節(jié)點(diǎn)酷鸦。

 /**
     * 查找指定節(jié)點(diǎn)的后繼節(jié)點(diǎn)
     *
     * @return
     */
    public TreeNode<Integer> successor(TreeNode<Integer> node ){
        // 1.指定節(jié)點(diǎn)的右子樹的最小節(jié)點(diǎn)
        if( node != null &&  node.right != null){
            return minmum(node.right);
        }
        //2. 右節(jié)點(diǎn)不存在時(shí)嘹裂,往上面查找符合一個(gè)節(jié)點(diǎn)y寄狼,該節(jié)點(diǎn)的左子樹的最大值為該查找節(jié)點(diǎn)node泊愧,該y.key>node.key 
        TreeNode<Integer> p = node.parent;
        while ( p != null && p.right == node){
            node = p;
            p=p.parent;
        }
        return p;
    }
    /**
     * 查找指定節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)
     * @param node
     * @return
     */
    public TreeNode<E> preSuccessor(TreeNode<E> node){
        //1.指定節(jié)點(diǎn)的左子樹的最大節(jié)點(diǎn)
        if(node != null && node.left!= null){
            return  maximum(node.left);
        }
        //2.左節(jié)點(diǎn)不存在時(shí),往上面查找符合一個(gè)節(jié)點(diǎn)y豪筝,該節(jié)點(diǎn)的右子樹的最小值為該查找節(jié)點(diǎn),該y.key >node.key
        TreeNode<E> p = node.parent;
        if(  p !=null && p.left == node){
            node = p;
            p = p.parent;
        }
        return p;
    }

5敲街、二叉搜索樹的插入和刪除

(1)插入

插入結(jié)點(diǎn)的位置對(duì)應(yīng)著查找過程中查找不成功時(shí)候的結(jié)點(diǎn)位置聪富,因此需要從根結(jié)點(diǎn)開始查找?guī)Р迦虢Y(jié)點(diǎn)位置著蟹,找到位置后插入即可
下面為插入的代碼

/**
     * 插入節(jié)點(diǎn)
     * 1.根節(jié)點(diǎn)為null的情況
     * 2.找到插入節(jié)點(diǎn)的位置的父節(jié)點(diǎn)萧豆,插入父節(jié)點(diǎn)的左邊
     * 3.找到插入節(jié)點(diǎn)的位置的父節(jié)點(diǎn)涮雷,插入父節(jié)點(diǎn)的右邊
     * @param T   樹
     * @param node  插入節(jié)點(diǎn)
     */
    public void insert(BinaryTree<Integer> T, TreeNode<Integer> node){
        TreeNode<Integer> y = null;  //存儲(chǔ)插入節(jié)點(diǎn)的父節(jié)點(diǎn)
        TreeNode<Integer> x  = T.root;//表示當(dāng)前節(jié)點(diǎn)
        while(x != null){
            y = x; //記錄當(dāng)時(shí)的父節(jié)點(diǎn)
            if(node.key < x.key ){
                x = x.left;
            }else{
                x = x.right;
            }
        }
        node.parent = y; //插入的節(jié)點(diǎn)的父節(jié)點(diǎn)指向父節(jié)點(diǎn)
        //1.根節(jié)點(diǎn)為null的情況
       if(y == null){
            T.root  = node;
         } else  if( node.key < y.key){ //2.找到插入節(jié)點(diǎn)的位置的父節(jié)點(diǎn),插入父節(jié)點(diǎn)的左邊
            y.left= node;
        } else{ //3.找到插入節(jié)點(diǎn)的位置的父節(jié)點(diǎn)览爵,插入父節(jié)點(diǎn)的右邊
           y.right = node;
        }
    }
(2)刪除

從二叉查找樹中刪除給定的結(jié)點(diǎn)z蜓竹,分三種情況討論:

  • 結(jié)點(diǎn)z沒有左右子樹俱济,則修改其父節(jié)點(diǎn)p[z]蛛碌,使其為NULL左医。刪除過程如下圖所示:

    樹刪除情況1.png

  • 如果結(jié)點(diǎn)z只有一個(gè)子樹(左子樹或者右子樹)跛十,通過在其子結(jié)點(diǎn)與父節(jié)點(diǎn)建立一條鏈來刪除z芥映。刪除過程如下圖所示:

    樹刪除情況2.png

  • 如果z有兩個(gè)子女奈偏,需要找到后繼節(jié)點(diǎn)惊来,然后后繼節(jié)點(diǎn)來替換刪除節(jié)點(diǎn)裁蚁,但是后繼節(jié)點(diǎn)的位置有兩種情況
    (1)后繼節(jié)點(diǎn)是刪除節(jié)點(diǎn)的右子節(jié)點(diǎn)

    樹刪除情況3-1.png

(2)后繼節(jié)點(diǎn)是刪除節(jié)點(diǎn)的右子節(jié)點(diǎn)的最小的左子節(jié)點(diǎn)

樹刪除情況3-2.png

代碼如下:

/**
     * 刪除指定節(jié)點(diǎn)
     * 有三種情況
     * 1.刪除節(jié)點(diǎn)沒有子節(jié)點(diǎn)
     * 2.刪除節(jié)點(diǎn)有一個(gè)子節(jié)點(diǎn)
     * 3.刪除節(jié)點(diǎn)有兩個(gè)節(jié)點(diǎn)(1.刪除的節(jié)點(diǎn)的后繼節(jié)點(diǎn)就是該節(jié)點(diǎn)的右節(jié)點(diǎn) 2.刪除節(jié)點(diǎn)的后繼節(jié)點(diǎn)是該節(jié)點(diǎn)的右子樹的最大節(jié)點(diǎn)  )
     * @param T
     * @param node
     */
    public void delete(BinaryTree<Integer> T, TreeNode<Integer> node){
        //1.刪除節(jié)點(diǎn)的左子節(jié)點(diǎn)為null
        if( node.left == null){
            transplant(T,node,node.right);
        }else if( node.right == null){ //2.刪除節(jié)點(diǎn)的右子節(jié)點(diǎn)為null
            transplant(T,node,node.left);
        }else{
            TreeNode<Integer> successor = successor(node);
            if( successor.parent != node){ //4.刪除節(jié)點(diǎn)的有2個(gè)子節(jié)點(diǎn)移必,但左子樹的后繼節(jié)點(diǎn)不為該刪除節(jié)點(diǎn)的左子節(jié)點(diǎn)
                transplant(T,successor,successor.right);//交換后繼節(jié)點(diǎn)和后繼節(jié)點(diǎn)的左子節(jié)點(diǎn)崔泵,讓后繼節(jié)點(diǎn)和父節(jié)點(diǎn)相互指向后繼節(jié)點(diǎn)的左子節(jié)點(diǎn)
                //后繼節(jié)點(diǎn)相互指向刪除節(jié)點(diǎn)的右節(jié)點(diǎn)
                successor.right = node.right;
                successor.right.parent = successor;
            }
            //3刪除節(jié)點(diǎn)的有2個(gè)子節(jié)點(diǎn)憎瘸,但左子節(jié)點(diǎn)的數(shù)據(jù)為后繼節(jié)點(diǎn)
            //交換后繼節(jié)點(diǎn)與刪除節(jié)點(diǎn)
            transplant(T,successor,node);
            //讓后繼節(jié)點(diǎn)與刪除節(jié)點(diǎn)的左子節(jié)點(diǎn)相互引用
            successor.left=node.left;
            successor.left.parent = successor;
        }

    }
    /**
     * 替換節(jié)點(diǎn)
     * 有三種情況
     * 1.該原始節(jié)點(diǎn)為父節(jié)點(diǎn)為null,即為根節(jié)點(diǎn)
     * 2.該原始節(jié)點(diǎn)是父節(jié)點(diǎn)的左節(jié)點(diǎn)
     * 3.該原始節(jié)點(diǎn)是父節(jié)點(diǎn)的右節(jié)點(diǎn)
     *
     * @param T
     * @param orginalNode
     * @param replaceNode
     */
    private void  transplant(BinaryTree<Integer> T,TreeNode<Integer> orginalNode ,TreeNode<Integer> replaceNode){
        //1.該原始節(jié)點(diǎn)為父節(jié)點(diǎn)為null,即為根節(jié)點(diǎn)
        if(orginalNode !=null && orginalNode.parent ==null){
            T.root  = replaceNode;
         //   2.該原始節(jié)點(diǎn)是父節(jié)點(diǎn)的左節(jié)點(diǎn)
        }else if( orginalNode.parent.left == orginalNode){
            orginalNode.parent.left = replaceNode;
          // 3.該原始節(jié)點(diǎn)是父節(jié)點(diǎn)的右節(jié)點(diǎn)
        }else if( orginalNode.parent.right  == orginalNode){
            orginalNode.parent.right = replaceNode;
        }
        if(replaceNode != null){
            replaceNode.parent = orginalNode.parent;
        }
    }

6崎弃、完整的二叉樹的代碼

  • 樹節(jié)點(diǎn)TreeNode
/**
 * @Project: 10.dataStructure
 * @description:  樹的節(jié)點(diǎn)
 * @author: sunkang
 * @create: 2018-08-19 15:08
 * @ModificationHistory who      when       What
 **/
public class TreeNode<E> {
    //存儲(chǔ)的值
    public E  key;
    public TreeNode<E> parent;
    public TreeNode<E> left;
    public TreeNode<E> right;
    @Override
    public String toString() {
        return "TreeNode{" +
                "key=" + key +
                ", parent=" + parent +
                ", left=" + left +
                ", right=" + right +
                '}';
    }
    public TreeNode(E key) {
        this.key = key;
    }
}
  • 二叉搜索樹BinaryTree
/**
 * @Project: 10.dataStructure
 * @description:
 * @author: sunkang
 * @create: 2018-08-19 15:13
 * @ModificationHistory who      when       What
 **/
public class BinaryTree<E> {
    public  TreeNode<E> root;
    /**
     * 前序遍歷樹
     * @param node
     */
    public void preOrder(TreeNode<E> node){
        if(node != null){
            System.out.print(node.key+",");
            preOrder(node.left);
            preOrder(node.right);
        }
    }
    /**
     * 中序遍歷樹
     * @param node
     */
    public  void inOrder(TreeNode<E> node){
        if(node != null){
            inOrder(node.left);
            System.out.print(node.key+",");
            inOrder(node.right);
        }
    }
    /**
     * 后序遍歷樹
     * @param node
     */
    public  void postOrder(TreeNode<E> node){
        if(node != null){
            postOrder(node.left);
            postOrder(node.right);
            System.out.print(node.key+",");
        }
    }

    /**
     * 查找特定值的節(jié)點(diǎn)
     * @param node
     * @param key
     * @return
     */
    public TreeNode<Integer> search(TreeNode<Integer> node, int key ){
        while (node != null && node.key != key){
            if(key < node.key){
                node = node.left;
            }else{
                node = node.right;
            }
        }
        return node;
    }

    /**
     * 通過遞歸的形式查找特定值的節(jié)點(diǎn)
     * @param node
     * @param key
     * @return
     */
    public TreeNode<Integer> searchByRecursion(TreeNode<Integer> node,int key){
        if( node == null || node.key == key){
            return node;
        }
        if(key <node.key){
           return  searchByRecursion(node.left,key);
        }else{
            return   searchByRecursion(node.right,key);
        }
    }
    /**
     * 查找樹的最大節(jié)點(diǎn)
     * @param node
     * @return
     */
    public TreeNode<E> maximum(TreeNode<E> node){
        while(node!= null && node.right!=null){
            node = node.right;
        }
        return node;
    }

    /**
     * 查找樹的最小節(jié)點(diǎn)
     * @param node
     * @return
     */
    public TreeNode<Integer> minmum(TreeNode<Integer> node){
        while( node!= null && node.left !=null){
            node= node.left;
        }
        return node;
    }
    /**
     * 查找指定節(jié)點(diǎn)的后繼節(jié)點(diǎn)
     *
     * @return
     */
    public TreeNode<Integer> successor(TreeNode<Integer> node ){
        // 1.指定節(jié)點(diǎn)的右子樹的最小節(jié)點(diǎn)
        if( node != null &&  node.right != null){
            return minmum(node.right);
        }
        //2. 右節(jié)點(diǎn)不存在時(shí),往上面查找符合一個(gè)節(jié)點(diǎn)y塞弊,該節(jié)點(diǎn)的左子樹的最大值為該查找節(jié)點(diǎn)node游沿,該y.key>node.key
        TreeNode<Integer> p = node.parent;
        while ( p != null && p.right == node){
            node = p;
            p=p.parent;
        }
        return p;
    }
    /**
     * 查找指定節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)
     * @param node
     * @return
     */
    public TreeNode<E> preSuccessor(TreeNode<E> node){
        //1.指定節(jié)點(diǎn)的左子樹的最大節(jié)點(diǎn)
        if(node != null && node.left!= null){
            return  maximum(node.left);
        }
        //2.左節(jié)點(diǎn)不存在時(shí)诀黍,往上面查找符合一個(gè)節(jié)點(diǎn)y眯勾,該節(jié)點(diǎn)的右子樹的最小值為該查找節(jié)點(diǎn),該y.key >node.key
        TreeNode<E> p = node.parent;
        if(  p !=null && p.left == node){
            node = p;
            p = p.parent;
        }
        return p;
    }

    /**
     * 插入節(jié)點(diǎn)
     * 1.根節(jié)點(diǎn)為null的情況
     * 2.找到插入節(jié)點(diǎn)的位置的父節(jié)點(diǎn)吃环,插入父節(jié)點(diǎn)的左邊
     * 3.找到插入節(jié)點(diǎn)的位置的父節(jié)點(diǎn)郁轻,插入父節(jié)點(diǎn)的右邊
     * @param T   樹
     * @param node  插入節(jié)點(diǎn)
     */
    public void insert(BinaryTree<Integer> T, TreeNode<Integer> node){
        TreeNode<Integer> y = null;  //存儲(chǔ)插入節(jié)點(diǎn)的父節(jié)點(diǎn)
        TreeNode<Integer> x  = T.root;//表示當(dāng)前節(jié)點(diǎn)
        while(x != null){
            y = x; //記錄當(dāng)時(shí)的父節(jié)點(diǎn)
            if(node.key < x.key ){
                x = x.left;
            }else{
                x = x.right;
            }
        }
        node.parent = y; //插入的節(jié)點(diǎn)的父節(jié)點(diǎn)指向父節(jié)點(diǎn)
        //1.根節(jié)點(diǎn)為null的情況
       if(y == null){
            T.root  = node;
         } else  if( node.key < y.key){ //2.找到插入節(jié)點(diǎn)的位置的父節(jié)點(diǎn)好唯,插入父節(jié)點(diǎn)的左邊
            y.left= node;
        } else{ //3.找到插入節(jié)點(diǎn)的位置的父節(jié)點(diǎn)渠啊,插入父節(jié)點(diǎn)的右邊
           y.right = node;
        }
    }

    /**
     * 刪除指定節(jié)點(diǎn)
     * 有三種情況
     * 1.刪除節(jié)點(diǎn)沒有子節(jié)點(diǎn)
     * 2.刪除節(jié)點(diǎn)有一個(gè)子節(jié)點(diǎn)
     * 3.刪除節(jié)點(diǎn)有兩個(gè)節(jié)點(diǎn)(1.刪除的節(jié)點(diǎn)的后繼節(jié)點(diǎn)就是該節(jié)點(diǎn)的右節(jié)點(diǎn) 2.刪除節(jié)點(diǎn)的后繼節(jié)點(diǎn)是該節(jié)點(diǎn)的右子樹的最大節(jié)點(diǎn)  )
     * @param T
     * @param node
     */
    public void delete(BinaryTree<Integer> T, TreeNode<Integer> node){
        //1.刪除節(jié)點(diǎn)的左子節(jié)點(diǎn)為null
        if( node.left == null){
            transplant(T,node,node.right);
        }else if( node.right == null){ //2.刪除節(jié)點(diǎn)的右子節(jié)點(diǎn)為null
            transplant(T,node,node.left);
        }else{
            TreeNode<Integer> successor = successor(node);
            if( successor.parent != node){ //4.刪除節(jié)點(diǎn)的有2個(gè)子節(jié)點(diǎn),但左子樹的后繼節(jié)點(diǎn)不為該刪除節(jié)點(diǎn)的左子節(jié)點(diǎn)
                transplant(T,successor,successor.right);//交換后繼節(jié)點(diǎn)和后繼節(jié)點(diǎn)的左子節(jié)點(diǎn)拄氯,讓后繼節(jié)點(diǎn)和父節(jié)點(diǎn)相互指向后繼節(jié)點(diǎn)的左子節(jié)點(diǎn)
                //后繼節(jié)點(diǎn)相互指向刪除節(jié)點(diǎn)的右節(jié)點(diǎn)
                successor.right = node.right;
                successor.right.parent = successor;
            }
            //3刪除節(jié)點(diǎn)的有2個(gè)子節(jié)點(diǎn)译柏,但左子節(jié)點(diǎn)的數(shù)據(jù)為后繼節(jié)點(diǎn)
            //交換后繼節(jié)點(diǎn)與刪除節(jié)點(diǎn)
            transplant(T,successor,node);
            //讓后繼節(jié)點(diǎn)與刪除節(jié)點(diǎn)的左子節(jié)點(diǎn)相互引用
            successor.left=node.left;
            successor.left.parent = successor;
        }

    }
    /**
     * 替換節(jié)點(diǎn)
     * 有三種情況
     * 1.該原始節(jié)點(diǎn)為父節(jié)點(diǎn)為null,即為根節(jié)點(diǎn)
     * 2.該原始節(jié)點(diǎn)是父節(jié)點(diǎn)的左節(jié)點(diǎn)
     * 3.該原始節(jié)點(diǎn)是父節(jié)點(diǎn)的右節(jié)點(diǎn)
     *
     * @param T
     * @param orginalNode
     * @param replaceNode
     */
    private void  transplant(BinaryTree<Integer> T,TreeNode<Integer> orginalNode ,TreeNode<Integer> replaceNode){
        //1.該原始節(jié)點(diǎn)為父節(jié)點(diǎn)為null,即為根節(jié)點(diǎn)
        if(orginalNode !=null && orginalNode.parent ==null){
            T.root  = replaceNode;
         //   2.該原始節(jié)點(diǎn)是父節(jié)點(diǎn)的左節(jié)點(diǎn)
        }else if( orginalNode.parent.left == orginalNode){
            orginalNode.parent.left = replaceNode;
          // 3.該原始節(jié)點(diǎn)是父節(jié)點(diǎn)的右節(jié)點(diǎn)
        }else if( orginalNode.parent.right  == orginalNode){
            orginalNode.parent.right = replaceNode;
        }
        if(replaceNode != null){
            replaceNode.parent = orginalNode.parent;
        }
    }

    /***************************
     * 構(gòu)造下面的樹
     *       15
     *      /  \
     *     6    18
     *    / \   / \
     *   3   7 17 20
     *  /\    \
     * 2  4   13
     *        /
     *       9
     ***************************/
    public static void main(String[] args) {
        BinaryTree<Integer> binaryTree  = new BinaryTree<Integer>();
        Integer[] arr = new Integer[]{15,6,18,3,7,17,20,2,4,23,9};

        for(Integer key :arr){
            TreeNode<Integer> node = new TreeNode<>(key);
            binaryTree.insert(binaryTree,node);
        }

        System.out.println(binaryTree.maximum(binaryTree.root).key);

        System.out.println( binaryTree.minmum(binaryTree.root).key);

        System.out.println("前序排列:");
        //前序排列
        binaryTree.preOrder(binaryTree.root);
        System.out.println();
        //中序排列
        System.out.println("中序排列:");
        binaryTree.inOrder(binaryTree.root);
        System.out.println();
        //后序排列
        System.out.println("后序排列:");
        binaryTree.postOrder(binaryTree.root);
        System.out.println();
        System.out.print("后繼節(jié)點(diǎn):");
        System.out.println(binaryTree.successor(binaryTree.root).key);
        System.out.print("前序節(jié)點(diǎn):");
        System.out.println(binaryTree.preSuccessor(binaryTree.root).key);

        System.out.print("跟節(jié)點(diǎn)的后繼節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn):");
        System.out.println(binaryTree.preSuccessor(binaryTree.successor(binaryTree.root)).key);

        System.out.print("跟節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)的后繼節(jié)點(diǎn):");
        System.out.println(binaryTree.successor(binaryTree.preSuccessor(binaryTree.root)).key);
    }
}
7、參考的資料

https://www.cnblogs.com/Anker/archive/2013/01/28/2880581.html
https://www.cnblogs.com/ysocean/p/8032642.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末介衔,一起剝皮案震驚了整個(gè)濱河市炎咖,隨后出現(xiàn)的幾起案子寒波,更是在濱河造成了極大的恐慌俄烁,老刑警劉巖页屠,帶你破解...
    沈念sama閱讀 211,948評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卷中,死亡現(xiàn)場離奇詭異,居然都是意外死亡议忽,警方通過查閱死者的電腦和手機(jī)栈幸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門速址,熙熙樓的掌柜王于貴愁眉苦臉地迎上來由驹,“玉大人蔓榄,你說我怎么就攤上這事√悠牵” “怎么了伍俘?”我有些...
    開封第一講書人閱讀 157,490評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長秃流。 經(jīng)常有香客問我舶胀,道長碧注,這世上最難降的妖魔是什么萍丐? 我笑而不...
    開封第一講書人閱讀 56,521評(píng)論 1 284
  • 正文 為了忘掉前任逝变,我火速辦了婚禮,結(jié)果婚禮上拱层,老公的妹妹穿的比我還像新娘根灯。我一直安慰自己掺栅,他們只是感情好氧卧,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評(píng)論 6 386
  • 文/花漫 我一把揭開白布搏明。 她就那樣靜靜地躺著,像睡著了一般熏瞄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上由桌,一...
    開封第一講書人閱讀 49,842評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音剪廉,去河邊找鬼炕檩。 笑死笛质,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的跷究。 我是一名探鬼主播俊马,決...
    沈念sama閱讀 38,997評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼柴我,長吁一口氣:“原來是場噩夢啊……” “哼扩然!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起彤悔,我...
    開封第一講書人閱讀 37,741評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤晕窑,失蹤者是張志新(化名)和其女友劉穎杨赤,沒想到半個(gè)月后疾牲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體衙解,經(jīng)...
    沈念sama閱讀 44,203評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡舌剂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了荐绝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片低滩。...
    茶點(diǎn)故事閱讀 38,673評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡委造,死狀恐怖昏兆,靈堂內(nèi)的尸體忽然破棺而出爬虱,到底是詐尸還是另有隱情跑筝,我是刑警寧澤瞒滴,帶...
    沈念sama閱讀 34,339評(píng)論 4 330
  • 正文 年R本政府宣布虏两,位于F島的核電站世剖,受9級(jí)特大地震影響旁瘫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜惠况,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評(píng)論 3 313
  • 文/蒙蒙 一稠屠、第九天 我趴在偏房一處隱蔽的房頂上張望完箩。 院中可真熱鬧弊知,春花似錦秩彤、人聲如沸事哭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽丑念。三九已至脯倚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間恍涂,已是汗流浹背乳丰。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評(píng)論 1 266
  • 我被黑心中介騙來泰國打工产园, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人夜郁。 一個(gè)月前我還...
    沈念sama閱讀 46,394評(píng)論 2 360
  • 正文 我出身青樓什燕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親竞端。 傳聞我的和親對(duì)象是個(gè)殘疾皇子屎即,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容