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     }
  • 相关阅读:
    海量数据库及分区4——《12年资深DBA教你Oracle开发与优化——性能优化部分》
    海量数据库及分区4——《12年资深DBA教你Oracle开发与优化——性能优化部分》
    高级SQL优化(三) 常用优化工具 ——《12年资深DBA教你Oracle开发与优化——性能优化部分》
    海量数据库及分区2——《12年资深DBA教你Oracle开发与优化——性能优化部分》
    海量数据库及分区1——《12年资深DBA教你Oracle开发与优化——性能优化部分》
    高级SQL优化(三) 常用优化工具 ——《12年资深DBA教你Oracle开发与优化——性能优化部分》
    实习生招聘笔试
    海量数据库及分区1——《12年资深DBA教你Oracle开发与优化——性能优化部分》
    高级SQL优化(一) ——《12年资深DBA教你Oracle开发与优化——性能优化部分》
    海量数据库及分区2——《12年资深DBA教你Oracle开发与优化——性能优化部分》
  • 原文地址:https://www.cnblogs.com/CherishFX/p/4625382.html
Copyright © 2011-2022 走看看