zoukankan      html  css  js  c++  java
  • Java实现链式存储的二叉查找树(递归方法)

    二叉查找树的定义:

      二叉查找树或者是一颗空树,或者是一颗具有以下特性的非空二叉树:

        1. 若左子树非空,则左子树上所有节点关键字值均小于根节点的关键字;

        2. 若右子树非空,则右子树上所有节点关键字值均大于根节点的关键字;

        3. 左、右子树本身也分别是一颗二叉查找树。

    二叉查找树的实现,功能有:

      1. 用一个数组去构建二叉查找树

      2. 二叉查找树的中序遍历和层次遍历

      3. 插入节点

      4. 查找节点

          5. 查找二叉树中的最大值和最小值

      6.  得到节点的直接父节点

      7. 得到节点的直接前驱和直接后继节点

      8. 删除节点

    树节点TreeNode的定义见:Java实现链式存储的二叉树

      1 import java.lang.Integer;
      2 import java.util.LinkedList;
      3 import java.util.Queue;
      4 import java.util.Scanner;
      5 
      6 /*
      7  * 二叉排序树(二叉查找树)的实现
      8  */
      9 public class BinarySearchTree {
     10     private TreeNode<Integer> root;  //根节点
     11     
     12     public BinarySearchTree(){
     13         root = null;
     14     }
     15     
     16     //用一个数组去构建二叉查找树
     17     public TreeNode<Integer> buildBST(Integer[] array){
     18         if(array.length == 0){
     19             return null;
     20         }else{
     21             root = null;  //初始化树为空树
     22             for(int i=0; i<array.length; i++){ //依次将每个元素插入
     23                 root = insertNode(root,array[i]);
     24             }
     25             return root;
     26         }        
     27     }
     28     
     29     //在二叉查找树中插入一个数据域为data的结点,新插入的结点一定是某个叶子节点
     30     public TreeNode<Integer> insertNode(TreeNode<Integer> node, Integer data){
     31         if(node == null){   //原树为空,新插入的记录为根节点
     32             node = new TreeNode<Integer>(data,null,null);        
     33         }else{
     34             if(node.getData() == data){   //树中存在相同关键字的结点,什么也不做
     35                 
     36             }else{
     37                 if(node.getData() > data){  //根节点>插入数据,插入到左子树中
     38                     node.setLchild(insertNode(node.getLchild(),data));
     39                 }else{ //根节点<插入数据,插入到右子树中
     40                     node.setRchild(insertNode(node.getRchild(),data)); 
     41                 }
     42             }        
     43         }
     44         return node;
     45     }
     46     
     47     //二叉查找树的中序遍历,可以得到一个递增的有序数列
     48     public void inOrder(TreeNode<Integer> node){
     49         if(node != null){
     50             inOrder(node.getLchild());
     51             System.out.print(node.getData()+" ");
     52             inOrder(node.getRchild());
     53         }
     54     }
     55     //二叉查找树的层次遍历
     56     public void levelOrder(TreeNode<Integer> root){
     57         Queue<TreeNode<Integer>> nodeQueue = new LinkedList<TreeNode<Integer>>();
     58         TreeNode<Integer> node = null;
     59         nodeQueue.add(root);  //将根节点入队    
     60         while(!nodeQueue.isEmpty()){  //队列不空循环
     61             node = nodeQueue.peek();
     62             System.out.print(node.getData()+" ");
     63             nodeQueue.poll();     //队头元素出队
     64             if(node.getLchild() != null){     //左子树不空,则左子树入队列
     65                 nodeQueue.add(node.getLchild());
     66             }
     67             if(node.getRchild() != null){     //右子树不空,则右子树入队列
     68                 nodeQueue.add(node.getRchild());
     69             }
     70         }
     71     }
     72     
     73     //查找数据域为data的结点,若不存在,返回null
     74     public TreeNode<Integer> searchNode(TreeNode<Integer> node, Integer data){
     75         while(node != null && node.getData() != data){
     76             if(node.getData() > data){
     77                 node = node.getLchild();  //根节点>数据,向左走
     78             }else{
     79                 node = node.getRchild();  //根节点<数据,向右走
     80             }
     81         }
     82         return node;
     83     }
     84     
     85     //查找最大值:不断地寻找右子节点
     86     public TreeNode<Integer> getMaxData(TreeNode<Integer> node){
     87         if(node.getRchild() == null){
     88             return node;
     89         }else{
     90             return getMaxData(node.getRchild());
     91         }
     92     }
     93     
     94     //查找最小值:不断地寻找左子节点
     95     public TreeNode<Integer> getMinData(TreeNode<Integer> node){
     96         if(node.getLchild() == null){
     97             return node;
     98         }else{
     99             return getMinData(node.getLchild());
    100         }
    101     }
    102     
    103     //得到数据域为data的结点的直接父节点parentNode
    104     public TreeNode<Integer> getParentNode(TreeNode<Integer> root, Integer data){
    105         TreeNode<Integer> parentNode = root;
    106         if(parentNode.getData() == data){   //根节点的父节点返回为null
    107             return null;
    108         }
    109         while(parentNode != null){
    110             //查找当前节点的父节点的左右子节点,若是相等,则返回该父节点
    111             if((parentNode.getLchild() != null && parentNode.getLchild().getData() == data) || 
    112                     (parentNode.getRchild() != null && parentNode.getRchild().getData() == data)){
    113                 return parentNode;
    114             }else{
    115                 if(parentNode.getData() > data){ //向左查找父节点
    116                     parentNode = parentNode.getLchild();
    117                 }else{
    118                     parentNode = parentNode.getRchild();  //向右查找父节点
    119                 }
    120             }            
    121         }
    122         return null;
    123     }
    124     
    125     /**
    126      * 得到结点node的直接前趋
    127      * a.该节点左子树不为空:其前驱节点为其左子树的最大元素
    128      * b.该节点左子树为空: 其前驱节点为其祖先节点(递归),且该祖先节点的右孩子也为其祖先节点
    129      *   (就是一直往其parent找,出现左拐后的那个祖先节点) 
    130      */
    131     public TreeNode<Integer> getPrecessor(TreeNode<Integer> root,TreeNode<Integer> node){
    132         if(node == null){
    133             return null;
    134         }
    135         //a.该节点左子树不为空:其前驱节点为其左子树的最大元素
    136         if(node.getLchild() != null){
    137             return getMaxData(node.getLchild());
    138         }else{  //b.该节点左子树为空: 其前驱节点为其祖先节点(递归)        
    139             TreeNode<Integer> parentNode = getParentNode(root, node.getData());
    140             while(parentNode != null && node == parentNode.getLchild()){
    141                 node = parentNode;
    142                 parentNode = getParentNode(root, parentNode.getData());
    143             }
    144             return parentNode;
    145         }
    146     }
    147     
    148     /**
    149      * 得到结点node的直接后继(后继节点就是比要删除的节点的关键值要大的节点集合中的最小值)
    150      * a.该节点右子树不为空,其后继节点为其右子树的最小元素
    151      * b.该节点右子树为空,则其后继节点为其祖先节点(递归),且此祖先节点的左孩子也是该节点的祖先节点,
    152      *   就是说一直往上找其祖先节点,直到出现右拐后的那个祖先节点: 
    153      */
    154     public TreeNode<Integer> getSuccessor(TreeNode<Integer> root,TreeNode<Integer> node){
    155         if(node == null){
    156             return null;
    157         }
    158         //a.该节点右子树不为空,其后继节点为其右子树的最小元素
    159         if(node.getRchild() != null){
    160             return getMinData(node.getRchild());
    161         }else{  //b.该节点右子树为空,则其后继节点为其最高祖先节点(递归)        
    162             TreeNode<Integer> parentNode = getParentNode(root, node.getData());
    163             while(parentNode != null && node == parentNode.getRchild()){
    164                 node = parentNode;
    165                 parentNode = getParentNode(root, parentNode.getData());
    166             }
    167             return parentNode;
    168         }
    169     }
    170     
    171     /**
    172      * 删除数据域为data的结点
    173      * 按三种情况处理:
    174      * a.如果被删除结点z是叶子节点,则直接删除,不会破坏二叉查找树的性质
    175      * b.如果节点z只有一颗左子树或右子树,则让z的子树成为z父节点的子树,代替z的位置
    176      * c.若结点z有左、右两颗子树,则令z的直接后继(或直接前驱)替代z,
    177      *   然后从二叉查找树中删去这个直接后继(或直接前驱),这样就转换为第一或第二种情况
    178      * @param node 二叉查找树的根节点
    179      * @param data 需要删除的结点的数据域
    180      * @return
    181      */
    182     public boolean deleteNode(TreeNode<Integer> node, Integer data){
    183         if(node == null){ //树为空
    184             throw new RuntimeException("树为空!");
    185         }
    186         TreeNode<Integer> delNode= searchNode(node, data);  //搜索需要删除的结点
    187         TreeNode<Integer> parent = null;
    188         if(delNode == null){  //如果树中不存在要删除的关键字
    189             throw new RuntimeException("树中不存在要删除的关键字!");
    190         }else{
    191             parent = getParentNode(node,data);  //得到删除节点的直接父节点
    192             //a.如果被删除结点z是叶子节点,则直接删除,不会破坏二叉查找树的性质         
    193             if(delNode.getLchild()==null && delNode.getRchild()==null){    
    194                 if(delNode==parent.getLchild()){  //被删除节点为其父节点的左孩子
    195                     parent.setLchild(null);
    196                 }else{    //被删除节点为其父节点的右孩子
    197                     parent.setRchild(null);
    198                 }
    199                 return true;
    200             }
    201             //b1.如果节点z只有一颗左子树,则让z的子树成为z父节点的子树,代替z的位置
    202             if(delNode.getLchild()!=null && delNode.getRchild()==null){
    203                 if(delNode==parent.getLchild()){ //被删除节点为其父节点的左孩子
    204                     parent.setLchild(delNode.getLchild());                    
    205                 }else{ //被删除节点为其父节点的右孩子
    206                     parent.setRchild(delNode.getLchild());
    207                 }
    208                 delNode.setLchild(null); //设置被删除结点的左孩子为null
    209                 return true;
    210             }
    211             //b2.如果节点z只有一颗右子树,则让z的子树成为z父节点的子树,代替z的位置
    212             if(delNode.getLchild()==null && delNode.getRchild()!=null){
    213                 if(delNode==parent.getLchild()){ //被删除节点为其父节点的左孩子
    214                     parent.setLchild(delNode.getRchild());
    215                 }else{  //被删除节点为其父节点的右孩子
    216                     parent.setRchild(delNode.getRchild());
    217                 }
    218                 delNode.setRchild(null); //设置被删除结点的右孩子为null
    219                 return true;
    220             }
    221             //c.若结点z有左、右两颗子树,则删除该结点的后继结点,并用该后继结点取代该结点
    222             if(delNode.getLchild()!=null && delNode.getRchild()!=null){
    223                 TreeNode<Integer> successorNode = getSuccessor(node,delNode); //得到被删除结点的后继节点
    224                 deleteNode(node,successorNode.getData()); //删除该结点的后继结点
    225                 delNode.setData(successorNode.getData()); //用该后继结点取代该结点
    226                 return true;
    227             }
    228         }
    229         return false;
    230     }
    231     
    232     
    233     public static void main(String args[]){
    234         Scanner input = new Scanner(System.in);
    235         Integer[] array = {8,3,10,1,6,14,4,7,13};
    236         BinarySearchTree bst = new BinarySearchTree();
    237         TreeNode<Integer> root = bst.buildBST(array);
    238         System.out.print("层次遍历:");  
    239         bst.levelOrder(root);
    240         System.out.print("
    "+"中序遍历:");  
    241         bst.inOrder(root);  
    242          System.out.println();
    243          System.out.print("得到最大值:");  
    244          System.out.println(bst.getMaxData(root).getData());  
    245          System.out.print("得到最小值:");  
    246          System.out.println(bst.getMinData(root).getData()); 
    247          System.out.print("向二叉查找树中插入一个节点,请输入需插入节点的数据域:");
    248          int data = input.nextInt();
    249          System.out.print("插入节点"+ data +"后,中序遍历的结果:");
    250          root = bst.insertNode(root, data); 
    251          bst.inOrder(root);  
    252          System.out.println("
    "+"在二叉查找树中查找元素,"+"请输入需要查找的结点值:");
    253          data = input.nextInt();
    254          if(bst.searchNode(root, data) == null){
    255              System.out.println("false");
    256          }else{
    257              System.out.println("true");
    258          }
    259          System.out.println("查找节点的直接父节点,"+"请输入需要查找的结点值:");
    260          data = input.nextInt();
    261          System.out.print("节点"+ data +"的父节点是:");
    262          if(bst.getParentNode(root, data) == null){
    263              System.out.println("null");
    264          }else{
    265              System.out.println(bst.getParentNode(root, data).getData());
    266          }
    267          System.out.println("删除结点,"+"请输入需要删除的结点值:");
    268          data = input.nextInt();
    269          if(bst.deleteNode(root, data)){
    270             System.out.print("删除结点后的层次遍历:");  
    271              bst.levelOrder(root);
    272              System.out.print("
    "+"删除结点后的中序遍历:");  
    273              bst.inOrder(root); 
    274          }      
    275     }    
    276 }

    程序运行结果:

    层次遍历:8 3 10 1 6 14 4 7 13 
    中序遍历:1 3 4 6 7 8 10 13 14 
    得到最大值:14
    得到最小值:1
    向二叉查找树中插入一个节点,请输入需插入节点的数据域:15
    插入节点15后,中序遍历的结果:1 3 4 6 7 8 10 13 14 15 
    在二叉查找树中查找元素,请输入需要查找的结点值:
    4
    true
    查找节点的直接父节点,请输入需要查找的结点值:
    10
    节点10的父节点是:8
    删除结点,请输入需要删除的结点值:
    4
    删除结点后的层次遍历:8 3 10 1 6 14 7 13 15 
    删除结点后的中序遍历:1 3 6 7 8 10 13 14 15 


    某些方法的非递归实现:

    1. 插入节点insertNode():

     1   //在二叉查找树中插入一个数据域为data的结点,新插入的结点一定是某个叶子节点
     2     public TreeNode<Integer> insertNode(TreeNode<Integer> node, Integer data){
     3         TreeNode<Integer> newNode = new TreeNode<Integer>(data,null,null);
     4         TreeNode<Integer> tmpNode = node;  //遍历节点
     5         TreeNode<Integer> pnode = null;   //记录当前节点的父节点    
     6         
     7         if(node == null){   //原树为空,新插入的记录为根节点
     8             node = newNode;    
     9             return node;
    10         }
    11         while(tmpNode != null){
    12             pnode = tmpNode;
    13             if(tmpNode.getData() == data){ //树中存在相同关键字的结点,什么也不做
    14                 return node;
    15             }else{
    16                 if(tmpNode.getData() > data){ //根节点>插入数据,插入到左子树中
    17                     tmpNode = tmpNode.getLchild();
    18                 }else{ //根节点<插入数据,插入到右子树中
    19                     tmpNode = tmpNode.getRchild();
    20                 }
    21             }
    22         }
    23         if(pnode.getData() > data){
    24             pnode.setLchild(newNode);
    25         }else{
    26             pnode.setRchild(newNode);
    27         }    
    28         return node;
    29     }

    2. 二叉查找树的中序遍历:

     1   //二叉查找树的中序遍历LNR,可以得到一个递增的有序数列
     2     public void inOrder(TreeNode<Integer> node){
     3         Stack<TreeNode<Integer>> nodeStack = new Stack<TreeNode<Integer>>();
     4         TreeNode<Integer> tempNode = node;  //遍历指针
     5         while(tempNode != null || !nodeStack.isEmpty()){
     6             if(tempNode != null){
     7                 nodeStack.push(tempNode);
     8                 tempNode = tempNode.getLchild();
     9             }else{
    10                 tempNode = nodeStack.pop();
    11                 System.out.print(tempNode.getData() + " ");
    12                 tempNode = tempNode.getRchild();
    13             }
    14         }
    15     }

    3. 得到二叉查找树的最大值和最小值:

     1     //查找最大值:不断地寻找右子节点
     2     public TreeNode<Integer> getMaxData(TreeNode<Integer> node){
     3         TreeNode<Integer> tempNode = node;
     4         while(tempNode.getRchild()!=null){
     5             tempNode = tempNode.getRchild();
     6         }
     7         return tempNode;
     8     }
     9     
    10     //查找最小值:不断地寻找左子节点
    11     public TreeNode<Integer> getMinData(TreeNode<Integer> node){
    12         TreeNode<Integer> tempNode = node;
    13         while(tempNode.getLchild() != null){
    14             tempNode = tempNode.getLchild();
    15         }
    16         return tempNode;
    17     }
  • 相关阅读:
    PHP保留小数的相关方法
    ASP.NET Core MVC 之过滤器(Filter)
    ASP.NET Core MVC 之控制器(Controller)
    ASP.NET Core MVC 之视图组件(View Component)
    ASP.NET Core MVC 之局部视图(Partial Views)
    标签助手(TagHelper)
    ASP.NET Core MVC 之布局(Layout)
    ASP.NET Core MVC 之视图(Views)
    ASP.NET Core MVC 之模型(Model)
    九卷读书:淘宝从小到大的发展 -重读《淘宝技术这十年》
  • 原文地址:https://www.cnblogs.com/CherishFX/p/4625382.html
Copyright © 2011-2022 走看看