• 数据结构(二)之二叉树


    基础概念  

      二叉树(binary tree)是一棵树,其中每个结点都不能有多于两个儿子。

      二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:

        (1)若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;

        (2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;

        (3)左、右子树也分别为二叉排序树;

     

    二叉树的遍历

      二叉树的遍历是指从根节点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次。二叉树的遍历方式有很多,主要有前序遍历,中序遍历,后序遍历。

    前序遍历

      前序遍历的规则是:若二叉树为空,则空操作返回,否则先访问根节点,然后前序遍历左子树,再前序遍历右子树

     

    中序遍历

       中序遍历的规则是:若树为空,则空操作返回;否则从根节点开始(注意并不是先访问根节点),中序遍历根节点的左子树,然后是访问根节点,最后中序遍历右子树。可以看到,如果是二叉排序树,中序遍历的结果就是个有序序列。

     

     

    后序遍历

      后序遍历的规则是:若树为空,则空操作返回;然后先遍历左子树,再遍历右子树,最后访问根结点,在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点。

     

    删除结点

      对于二叉排序树的其他操作,比如插入,遍历等,比较容易理解;而删除操作相对复杂些。对于要删除的结点,有以下三种情况:

        1.叶子结点;

        2.仅有左子树或右子树的结点;

        3.左右子树都有结点;

      对于1(要删除结点为叶子结点)直接删除,即直接解除父节点的引用即可,对于第2种情况(要删除的结点仅有一个儿子),只需用子结点替换掉父节点即可;而对于要删除的结点有两个儿子的情况,比较常用处理逻辑为,在其子树中找寻一个结点来替换,而这个结点我们成为中序后继结点。

     

     

      可以看到,我们找到的这个用来替换的结点,可以是删除结点的右子树的最小结点(6),也可以是其左子树的最大结点(4),这样可以保证替换后树的整体结构不用发生变化。为什么称为中序后继结点呢?我们来看下这棵树的中序遍历结果 1-2-3-4-5-6-7-8-9可以很清晰的看到,其实要找的这个结点,可以是结点5的前驱或者后继。

    代码实现

      1 package treeDemo;
      2 
      3 /**
      4  * Created by chengxiao on 2017/02/12.
      5  */
      6 public class BinaryTree {
      7     //根节点
      8     private Node root;
      9     /**
     10      * 树的结点
     11      */
     12     private static class Node{
     13         //数据域
     14         private long data;
     15         //左子结点
     16         private Node leftChild;
     17         //右子结点
     18         private Node rightChild;
     19         Node(long data){
     20             this.data = data;
     21         }
     22     }
     23 
     24     /**
     25      * 插入结点
     26      * @param data
     27      */
     28     public void insert(long data){
     29         Node newNode = new Node(data);
     30         Node currNode = root;
     31         Node parentNode;
     32         //如果是空树
     33         if(root == null){
     34             root = newNode;
     35             return;
     36         }
     37         while(true){
     38             parentNode = currNode;
     39             //向右搜寻
     40             if(data > currNode.data){
     41                 currNode = currNode.rightChild;
     42                 if(currNode == null){
     43                     parentNode.rightChild = newNode;
     44                     return;
     45                 }
     46             }else{
     47                 //向左搜寻
     48                 currNode = currNode.leftChild;
     49                 if(currNode == null){
     50                     parentNode.leftChild = newNode;
     51                     return;
     52                 }
     53             }
     54         }
     55 
     56     }
     57 
     58     /**
     59      * 前序遍历
     60      * @param currNode
     61      */
     62     public void preOrder(Node currNode){
     63         if(currNode == null){
     64             return;
     65         }
     66         System.out.print(currNode.data+" ");
     67         preOrder(currNode.leftChild);
     68         preOrder(currNode.rightChild);
     69     }
     70 
     71     /**
     72      * 中序遍历
     73      * @param currNode
     74      */
     75     public void inOrder(Node currNode){
     76         if(currNode == null){
     77             return;
     78         }
     79         inOrder(currNode.leftChild);
     80         System.out.print(currNode.data+" ");
     81         inOrder(currNode.rightChild);
     82 
     83     }
     84 
     85     /**
     86      * 后序遍历
     87      * @param currNode
     88      */
     89     public void postOrder(Node currNode){
     90         if(currNode == null){
     91             return;
     92         }
     93         postOrder(currNode.leftChild);
     94         postOrder(currNode.rightChild);
     95         System.out.print(currNode.data+" ");
     96     }
     97 
     98     /**
     99      * 查找结点
    100      * @param data
    101      * @return
    102      */
    103     public Node find(long data){
    104         Node currNode = root;
    105         while(currNode!=null){
    106             if(data>currNode.data){
    107                 currNode = currNode.rightChild;
    108             }else if(data<currNode.data){
    109                 currNode = currNode.leftChild;
    110             }else{
    111                 return currNode;
    112             }
    113         }
    114         return null;
    115     }
    116 
    117     /**
    118      * 删除结点 分为3种情况
    119      * 1.叶子结点
    120      * 2.该节点有一个子节点
    121      * 3.该节点有二个子节点
    122      * @param data
    123      */
    124     public boolean delete(long data) throws Exception {
    125         Node curr = root;
    126         //保持一个父节点的引用
    127         Node parent = curr;
    128         //删除结点是左子结点还是右子结点,
    129         boolean isLeft = true;
    130         while(curr != null && curr.data!=data){
    131             parent = curr;
    132             if(data > curr.data){
    133                 curr = curr.rightChild;
    134                 isLeft = false;
    135             }else{
    136                 curr = curr.leftChild;
    137                 isLeft = true;
    138             }
    139         }
    140         if(curr==null){
    141             throw new Exception("要删除的结点不存在");
    142         }
    143         //第一种情况,要删除的结点为叶子结点
    144         if(curr.leftChild == null && curr.rightChild == null){
    145             if(curr == root){
    146                 root = null;
    147                 return true;
    148             }
    149             if(isLeft){
    150                 parent.leftChild = null;
    151             }else{
    152                 parent.rightChild = null;
    153             }
    154         }else if(curr.leftChild == null){
    155             //第二种情况,要删除的结点有一个子节点且是右子结点
    156             if(curr == root){
    157                 root = curr.rightChild;
    158                 return true;
    159             }
    160             if(isLeft){
    161                 parent.leftChild = curr.rightChild;
    162             }else{
    163                 parent.rightChild = curr.rightChild;
    164             }
    165         }else if(curr.rightChild == null){
    166             //第二种情况,要删除的结点有一个子节点且是左子结点
    167             if(curr == root){
    168                 root = curr.leftChild;
    169                 return true;
    170             }
    171             if(isLeft){
    172                 parent.leftChild = curr.leftChild;
    173             }else{
    174                 parent.rightChild = curr.leftChild;
    175             }
    176         }else{
    177             //第三种情况,也是最复杂的一种情况,要删除的结点有两个子节点,需要找寻中序后继结点
    178             Node succeeder = getSucceeder(curr);
    179             if(curr == root){
    180                 root = succeeder;
    181                 return  true;
    182             }
    183             if(isLeft){
    184                 parent.leftChild = succeeder;
    185             }else{
    186                 parent.rightChild = succeeder;
    187             }
    188             //当后继结点为删除结点的右子结点
    189             succeeder.leftChild = curr.leftChild;
    190 
    191         }
    192         return true;
    193     }
    194     public Node getSucceeder(Node delNode){
    195         Node succeeder = delNode;
    196         Node parent = delNode;
    197         Node currNode = delNode.rightChild;
    198         //寻找后继结点
    199         while(currNode != null){
    200             parent = succeeder;
    201             succeeder = currNode;
    202             currNode = currNode.leftChild;
    203         }
    204         //如果后继结点不是要删除结点的右子结点
    205         if(succeeder != delNode.rightChild){
    206             parent.leftChild = succeeder.rightChild;
    207             //将后继结点的左右子结点分别指向要删除结点的左右子节点
    208             succeeder.leftChild = delNode.leftChild;
    209             succeeder.rightChild = delNode.rightChild;
    210         }
    211         return succeeder;
    212 
    213     }
    214     public static void main(String []args) throws Exception {
    215         BinaryTree binaryTree = new BinaryTree();
    216         //插入操作
    217         binaryTree.insert(5);
    218         binaryTree.insert(2);
    219         binaryTree.insert(8);
    220         binaryTree.insert(1);
    221         binaryTree.insert(3);
    222         binaryTree.insert(6);
    223         binaryTree.insert(10);
    224         //前序遍历
    225         System.out.println("前序遍历:");
    226         binaryTree.preOrder(binaryTree.root);
    227         System.out.println();
    228         //中序遍历
    229         System.out.println("中序遍历:");
    230         binaryTree.inOrder(binaryTree.root);
    231         System.out.println();
    232         //后序遍历
    233         System.out.println("后序遍历:");
    234         binaryTree.postOrder(binaryTree.root);
    235         System.out.println();
    236         //查找结点
    237         Node node = binaryTree.find(10);
    238         System.out.println("找到结点,其值为:"+node.data);
    239         //删除结点
    240         binaryTree.delete(8);
    241         System.out.print("删除结点8,中序遍历:");
    242         binaryTree.preOrder(binaryTree.root);
    243     }
    244 }
    二叉树的基本操作

    执行结果

    前序遍历:
    5 2 1 3 8 6 10 
    中序遍历:
    1 2 3 5 6 8 10 
    后序遍历:
    1 3 2 6 10 8 5 
    找到结点,其值为:10
    删除结点8,中序遍历:5 2 1 3 10 6 
  • 相关阅读:
    编程语言的精髓
    进销存-库存表-算法
    JAVA 将JSON数组转化成JAVA数组
    使input文本框不可编辑的3种方法
    如何让一个DIV固定在另一个DIV的底部
    错误笔记既 onclick()事件写方法传jstl表达式作为变量
    错误笔记
    SQL查询语句左连接
    查已有表的建表语句
    使用(文件上传域)MultipartFile接受文件时的判空方式
  • 原文地址:https://www.cnblogs.com/chengxiao/p/6395265.html
Copyright © 2020-2023  润新知