基础概念  

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

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

    (1)若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;
    (2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
    (3)左、右子树也分别为二叉排序树;

二叉树的遍历

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

前序遍历

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

中序遍历

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

后序遍历

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

 

删除结点

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

  1.叶子结点;

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

  3.左右子树都有结点

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

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

代码实现

 package treeDemo;

 /**
  * Created by chengxiao on 2017/02/12.
  */
 public class BinaryTree {
     //根节点
     private Node root;
     /**
      * 树的结点
      */
     private static class Node{
         //数据域
         private long data;
         //左子结点
         private Node leftChild;
         //右子结点
         private Node rightChild;
         Node(long data){
             this.data = data;
         }
     }

     /**
      * 插入结点
      * @param data
      */
     public void insert(long data){
         Node newNode = new Node(data);
         Node currNode = root;
         Node parentNode;
         //如果是空树
         if(root == null){
             root = newNode;
             return;
         }
         while(true){
             parentNode = currNode;
             //向右搜寻
             if(data > currNode.data){
                 currNode = currNode.rightChild;
                 if(currNode == null){
                     parentNode.rightChild = newNode;
                     return;
                 }
             }else{
                 //向左搜寻
                 currNode = currNode.leftChild;
                 if(currNode == null){
                     parentNode.leftChild = newNode;
                     return;
                 }
             }
         }

     }

     /**
      * 前序遍历
      * @param currNode
      */
     public void preOrder(Node currNode){
         if(currNode == null){
             return;
         }
         System.out.print(currNode.data+" ");
         preOrder(currNode.leftChild);
         preOrder(currNode.rightChild);
     }

     /**
      * 中序遍历
      * @param currNode
      */
     public void inOrder(Node currNode){
         if(currNode == null){
             return;
         }
         inOrder(currNode.leftChild);
         System.out.print(currNode.data+" ");
         inOrder(currNode.rightChild);

     }

     /**
      * 查找结点
      * @param data
      * @return
      */
     public Node find(long data){
         Node currNode = root;
         while(currNode!=null){
             if(data>currNode.data){
                 currNode = currNode.rightChild;
             }else if(data<currNode.data){
                 currNode = currNode.leftChild;
             }else{
                 return currNode;
             }
         }
         return null;
     }

     /**
      * 后序遍历
      * @param currNode
      */
     public void postOrder(Node currNode){
         if(currNode == null){
             return;
         }
         postOrder(currNode.leftChild);
         postOrder(currNode.rightChild);
         System.out.print(currNode.data+" ");
     }

     /**
      * 删除结点 分为3种情况
      * 1.叶子结点
      * 2.该节点有一个子节点
      * 3.该节点有二个子节点
      * @param data
      */
     public boolean delete(long data) throws Exception {
         Node curr = root;
         //保持一个父节点的引用
         Node parent = curr;
         //删除为左结点还是右界定啊
         boolean isLeft = true;
         while(curr != null && curr.data!=data){
             parent = curr;
             if(data > curr.data){
                 curr = curr.rightChild;
                 isLeft = false;
             }else{
                 curr = curr.leftChild;
                 isLeft = true;
             }
         }
         if(curr==null){
             throw new Exception("要删除的结点不存在");
         }
         //第一种情况,要删除的结点为叶子结点
         if(curr.leftChild == null && curr.rightChild == null){
             if(curr == root){
                 root = null;
                 return true;
             }
             if(isLeft){
                 parent.leftChild = null;
             }else{
                 parent.rightChild = null;
             }
         }else if(curr.leftChild == null){
             //第二种情况,要删除的结点有一个子节点且是右子结点
             if(curr == root){
                 root = curr.rightChild;
                 return true;
             }
             if(isLeft){
                 parent.leftChild = curr.rightChild;
             }else{
                 parent.rightChild = curr.rightChild;
             }
         }else if(curr.rightChild == null){
             //第二种情况,要删除的结点有一个子节点且是左子结点
             if(curr == root){
                 root = curr.leftChild;
                 return true;
             }
             if(isLeft){
                 parent.leftChild = curr.leftChild;
             }else{
                 parent.rightChild = curr.leftChild;
             }
         }else{
             //第三种情况,也是最复杂的一种情况,要删除的结点有两个子节点,需要找寻中序后继结点
             Node succeeder = getSucceeder(curr);
             if(curr == root){
                 root = succeeder;
                 return  true;
             }
             if(isLeft){
                 parent.leftChild = succeeder;
             }else{
                 parent.rightChild = succeeder;
             }
             //当后继结点为删除结点的右子结点
             succeeder.leftChild = curr.leftChild;

         }
         return true;
     }
     public Node getSucceeder(Node delNode){
         Node succeeder = delNode;
         Node parent = delNode;
         Node currNode = delNode.rightChild;
         //寻找后继结点
         while(currNode != null){
             parent = succeeder;
             succeeder = currNode;
             currNode = currNode.leftChild;
         }
         //如果后继结点不是要删除结点的右子结点
         if(succeeder != delNode.rightChild){
             parent.leftChild = succeeder.rightChild;
             //将后继结点的左右子结点分别指向要删除结点的左右子节点
             succeeder.leftChild = delNode.leftChild;
             succeeder.rightChild = delNode.rightChild;
         }
         return succeeder;

     }
     public static void main(String []args) throws Exception {
         BinaryTree binaryTree = new BinaryTree();
         //插入操作
         binaryTree.insert(5);
         binaryTree.insert(2);
         binaryTree.insert(8);
         binaryTree.insert(1);
         binaryTree.insert(3);
         binaryTree.insert(6);
         binaryTree.insert(10);
         //前序遍历
         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();
         //查找结点
         Node node = binaryTree.find(10);
         System.out.println("找到结点,其值为:"+node.data);
         //删除结点
         binaryTree.delete(8);
         System.out.print("删除结点8,中序遍历:");
         binaryTree.preOrder(binaryTree.root);
     }
 }

执行结果

前序遍历:
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描述)之二叉树的更多相关文章

  1. 数据结构(Java描述)之线性表

    基础概念 数据结构:是相互之间存在一种或多种关系的数据元素的集合. 逻辑结构和物理结构 关于数据结构,我们可以从逻辑结构和物理结构这两个维度去描述 逻辑结构是数据对象中数据元素之间的关系,是从逻辑意义 ...

  2. 【Java】 大话数据结构(9) 树(二叉树、线索二叉树)

    本文根据<大话数据结构>一书,对Java版的二叉树.线索二叉树进行了一定程度的实现. 另: 二叉排序树(二叉搜索树) 平衡二叉树(AVL树) 二叉树的性质 性质1:二叉树第i层上的结点数目 ...

  3. Java描述表达式求值的两种解法:双栈结构和二叉树

    Java描述表达式求值的两种解法:双栈结构和二叉树 原题大意:表达式求值 求一个非负整数四则混合运算且含嵌套括号表达式的值.如: # 输入: 1+2*(6/2)-4 # 输出: 3.0 数据保证: 保 ...

  4. 数据结构(Java语言描述)-第一章:概述

    第一章 概述 1.0 序言 自己为啥要学数据结构嘞,我觉得主要有以下三个原因: 前段时间在看并发编程时,发现aqs,corrunthashmap等底层都用到了数据结构,主要的有队列,还有链表,学习数据 ...

  5. 数据结构与算法 java描述 第一章 算法及其复杂度

    目录 数据结构与算法 java描述 笔记 第一章 算法及其复杂度 算法的定义 算法性能的分析与评价 问题规模.运行时间及时间复杂度 渐进复杂度 大 O 记号 大Ω记号 Θ记号 空间复杂度 算法复杂度及 ...

  6. 利用Java实现表达式二叉树

    (*^-^*) 什么是二叉树,这里不再介绍,可以自行百度:二叉树.在这里利用java实现“表达式二叉树”. 表达式二叉树的定义 第一步先要搞懂表达式二叉树是个什么东东?举个栗子,表达式:(a+b×(c ...

  7. python数据结构之树和二叉树(先序遍历、中序遍历和后序遍历)

    python数据结构之树和二叉树(先序遍历.中序遍历和后序遍历) 树 树是\(n\)(\(n\ge 0\))个结点的有限集.在任意一棵非空树中,有且只有一个根结点. 二叉树是有限个元素的集合,该集合或 ...

  8. Java递归方法遍历二叉树的代码

    将内容过程中经常用的内容做个记录,如下内容内容是关于Java递归方法遍历二叉树的内容. package com.wzs; public class TestBinaryTree { public st ...

  9. Java实现查找二叉树&C++的做法

    写了个Java的查找二叉树,用递归做的,不用递归的还没弄出来.先贴一下.回头再研究. BTreeTest.java: public class BTreeTest{ class Node{ Node ...

  10. 纯数据结构Java实现(5/11)(Set&Map)

    纯数据结构Java实现(5/11)(Set&Map) Set 和 Map 都是抽象或者高级数据结构,至于底层是采用树还是散列则根据需要而定. 可以细想一下 TreeMap/HashMap, T ...

随机推荐

  1. iOS8学习笔记2--autolayout

    iOS支持的设备如今已经具有了很多的尺寸,针对这些不同的尺寸每一个都做一个独立的APP肯定是不现实的,于是苹果在iOS8之后推出了autolayout和sizeclass,同时还有VFL界面设计语言 ...

  2. 关于cin的用法一些小结

    在写二叉树的时候遇到if(!cin)那几个标志位弄得并不清楚,还遇到了诸如cin.clear()等函数,感觉C++又白学了,于是打算去网上搜了几篇靠谱的文章,有时候看来,一些事件处理类的工程代码,在A ...

  3. bzoj-1834 network 网络扩容 【网络流】

    这题就是复习下网络流. #include <bits/stdc++.h> #define rep(i, a, b) for (int i = a; i <= b; i++) #def ...

  4. easyui 异步json tree跨域访问问题解决

    最近在用easyui中的异步tree时发现了跨域访问问题,我们都知道jquery ajax提供get请求的跨域访问.所以解决easyui tree跨域访问的问题便是将数据通过jquery ajax将数 ...

  5. jquery弹窗插件

    .zhuti { position:absolute; z-index:; font-size:14px; border-radius:5px; box-shadow: 5px white; over ...

  6. 怎样编制excel序列目录

    怎样编制序列目录 原帖内容:http://www.excelpx.com/forum.php?mod=viewthread&tid=164190&extra=%26page%3D1&a ...

  7. ui主线程控件的更新就让这个activity的异步任务做完整

    项目中使用的SingleMessageView,控件实例化后,点击用户头像,此时跳转到UserInfo里查看这个用户的头像.用户名.签名.标签. 之前,师兄在SingleMessage里写了个头像的点 ...

  8. OC纯代码全手工打造ScroolView实现翻页

    OC纯代码全手工打造ScroolView实现翻页 1. 概述 分为三部分: 上部标题ScrollView 下部内容ScrollView 上部当前页 标示线 2. 效果 上下两部分都随着手势的滑动一块滑 ...

  9. CentOS下架设VNC服务器

    CentOS下架设VNC服务器1.什么是VNC服务器?百度百科:VNC (Virtual Network Computer)是虚拟网络计算机的缩写.它 是一款优秀的远程控制工具软件,由著名的 AT&a ...

  10. FarPoint Spread ChildView子视图

    有一种需求场景在很多地方都会用到,就是父子关系(头表和子表的关系),比如订单和订单明细. 做过winform的朋友第spread控件应该比较熟悉,或者了解.他的展示方式就通过一个关联关系就可以了,下面 ...