@

二叉树模型

二叉树是树的一种应用,一个节点可以有两个孩子:左孩子,右孩子,并且除了根节点以外每个节点都有一个父节点。当然这种简单的二叉树不能解决让树保持平衡状态,例如你一直往树的左边添加元素,就会导致查找效率的减慢。,如何解决这个问题,下一篇文章再说。

二叉树的实现

  • 二叉树的实现类
import java.util.LinkedList;

/**
* 二叉查找树
* @param <E> 泛型节点
*/
public class BinaryTree<E extends Comparable> implements Tree<E> {
/**
* @param root 根节点
*/
private Node<E> root; /**
* 内部类
* 继承Comparable类来比较泛型的大小
* 如果泛型是一个包含个多基本类型的对象,你需要重写compareTo方法
* @param <E> 泛型类型的节点
*/
private class Node<E extends Comparable> {
E data;
Node<E> left;
Node<E> right; public void setLeft(Node<E> left) {
this.left = left;
} public void setRight(Node<E> right) {
this.right = right;
} public int getNum() {
return num;
} public void setNum(int num) {
this.num = num;
} int num;
public Node<E> getLeft() {
return left;
} public Node<E> getRight() {
return right;
} public Node(E data){
this.data=data;
} public Node(E data, Node next, Node pre) {
this.data = data;
this.left = next;
this.right = pre;
} public Node(){
left=null;
right=null;
data=null;
num=1;
}
public E getData() {
return data;
} public void setData(E data) {
this.data = data;
} } /**
* 判空方法
* @return true 树为空 false 树不为空
*/
@Override
public boolean isEmpty() {
if(root==null){
return true;
}else{
return false;
}
} /**
* 使树为空
* 个人理解:
* 树中其他节点有实例对象存储在实例池中
* 根节点不仅有实例对象,而且还要引用类型,存在java栈的本地变量表中
* 之间使根节点为NULL
*/
@Override
public void makeEmpty() {
root=null;
} /**
* 判断某个泛型类型的节点在树中
* @param p 泛型类型
* @return true 包含在节点中 false 不包含
*/
@Override
public boolean contains(E p) {
return contain(p,root);
} /**
* contains方法的具体实现
* 采用递归实现
* @param p 需要判断是否存在书中泛型元素
* @param r 树中的节点
* @return true 包含 false 不包含
*/
private boolean contain(E p,Node<E> r){
Node<E> temp=root;
if(root==null){
return false;
}else{
if(temp.getData().compareTo(p)>0){
contain(p,temp.getLeft());
}else if(temp.getData().compareTo(p)<0){
contain(p,temp.getRight());
}else if(temp.getData().compareTo(p)==0&&temp.num>0){
return true;
}
}
return false;
} /**
* 找到树中最小的元素
* @return 树中最小的元素
*/
@Override
public E findMin() {
if(isEmpty()){
System.out.println("树为空");
}else {
/**
* 因为采用懒惰删除,所以树中最小的元素不仅要是最左面的哪一个
* 而且还要是num>0的哪一个
*/
Node<E> p = root;
Node<E> temp = root;
while (p.getLeft() != null) {
p = p.getLeft();
if (p.num > 0) {
temp = p;
}
}
return temp.getData(); }
throw new NullPointerException();
} /**
* 找到树中最大的元素
* 和寻找最小的原理一样
* @return 树中最大的元素
*/
@Override
public E findMax() {
if(isEmpty()){
System.out.println("树为空");
}else {
Node<E> p = root;
Node<E> temp = root;
while (p.getRight() != null) {
p = p.getRight();
if (p.num > 0) {
temp = p;
}
}
return temp.getData();
}
throw new NullPointerException();
} /**
* 树的前序遍历
* 根左右
*/
@Override
public void preTraversal() {
System.out.print("前序遍历:");
preTraversal(root);
System.out.println();
} /**
* 前序遍历的具体实现方法
* @param p 使用递归时进入下一个节点
*/
private void preTraversal(Node<E> p) {
if(isEmpty()){
System.out.println("树为空");
}else {
if (p.num > 0) {
System.out.print(p.getData() + " ");
}
if (p.getLeft() != null) {
preTraversal(p.getLeft());
}
if (p.getRight() != null) {
preTraversal(p.getRight());
}
return;
} } /**
* 树的层序遍历
* 即从根开始一层一层的遍历
*/
public void seqTraverse(){
if(isEmpty()){
System.out.println("树为空");
}else {
System.out.print("层序遍历:");
LinkedList<Node<E>> temp = new LinkedList<>();
Node<E> p = root;
temp.add(p);
while (!temp.isEmpty()) {
Node<E> a = temp.pop();
if (a.num > 0) {
System.out.print(a.getData() + " ");
}
if (a.getLeft() != null) {
temp.add(a.getLeft());
}
if (a.getRight() != null) {
temp.add(a.getRight());
}
}
System.out.println();
}
} /**
* 后序遍历的实现方法
* @param p 使用递归时进入下一个节点
*/
private void posTraversal(Node<E> p) {
if(isEmpty()){
System.out.println("树为空");
}else {
if (p.getLeft() != null) {
posTraversal(p.getLeft());
}
if (p.getRight() != null) {
posTraversal(p.getRight());
}
if (p.num > 0) {
System.out.print(p.getData() + " ");
}
return;
}
} /**
* 中序遍历的实现方法
* @param p 使用递归时进入下一个节点
*/
private void cenTraversal(Node<E> p) {
if(isEmpty()){
System.out.println("树为空");
}else {
if (p.getLeft() != null) {
cenTraversal(p.getLeft());
}
if (p.num > 0) {
System.out.print(p.getData() + " ");
}
if (p.getRight() != null) {
cenTraversal(p.getRight());
} return;
}
} /**
* 树的后序遍历
* 左右根
*/
@Override
public void posTraversal() {
System.out.print("后序遍历:");
posTraversal(root);
System.out.println();
}
/**
* 树的中序遍历
* 左根右
*/
@Override
public void cenTraversal() {
System.out.print("中序遍历:");
cenTraversal(root);
System.out.println();
} /**
* 向树中插入元素
* @param p 待插入的元素
*/
@Override
public void insert(E p) {
/**
* 主要为了将num加一
*/
Node temp=new Node<>(p);
temp.num++;
/**
* 根为空,就给根赋值
*/
if(root==null) {
root=temp;
}else{
/**
*如果元素小于当前的d元素就往左递归
* 反之就向右递归
* 如果相等就num加一
*/
Node<E > d=root;
while (d!=null){
if(p.compareTo(d.getData())<0){
if(d.getLeft()==null){
d.setLeft(temp);
break;
}
d=d.getLeft();
}else if(p.compareTo(d.getData())>0){
if(d.getRight()==null){
d.setRight(temp);
break;
}
d=d.getRight();
}else{
d.num++;
}
}
}
} /**
*删除树中的一个节点
* 采用懒惰删除对num的节点进行减一
* @param p 需要删除的内容
*/
@Override
public void remove(E p) {
removePri(p,root);
}
private void removePri(E p,Node<E> r){
Node temp=r;
if(temp.getData().compareTo(p)>0){
removePri(p,temp.getLeft());
}else if(temp.getData().compareTo(p)<0){
removePri(p,temp.getRight());
}else {
temp.num--;
}
} }
  • 接口的类
/**
* 树的接口
* @param <E> 泛型的类型
*/
public interface Tree<E > {
/**
* 判空函数
* @return true 树为空 false 树不为空
*/
boolean isEmpty(); /**
* 使树为空
*/
void makeEmpty(); /**
* 检查书中是否包含p这个元素
* @param p 泛型元素
* @return true 包含 false 不包含
*/
boolean contains(E p); /**
* 找到树中最小元素
* @return 最小的元素
*/
E findMin();
/**
* 找到树中最大元素
* @return 最大的元素
*/
E findMax(); /**
* 前序遍历
*/
void preTraversal(); /**
* 后序遍历
*/
void posTraversal(); /**
* 中序遍历
* 森林里没有中序遍历,森林无法判断哪个是中间
* 所以可以不实现
*/
default void cenTraversal() { } /**
* 插入元素
* @param p 待插入的元素
*/
void insert(E p); /**
* 删除方法
* @param p 待删除的元素
*/
void remove(E p); }

对于代码的测试

import java.util.Stack;
public class Main {
public static void main(String[] args) {
BinaryTree<Integer> dd=new BinaryTree<>();
dd.insert(4);
dd.insert(2);
dd.insert(3);
dd.insert(1);
dd.insert(6);
dd.insert(5);
dd.insert(7);
// dd.preTraversal();
//dd.cenTraversal();
// dd.posTraversal();
// dd.seqTraverse();
System.out.println(dd.findMin()); ;
System.out.println(dd.findMax());
dd.remove(2);
dd.remove(6);
}
}

如上图的代码构成了如下的树:

graph LR
A((4)) --> B((2))
A --> C((6))
B-->d((1))
B-->e((3))
C-->g((5))
C-->j((7))
  • 调用findMax和findMin方法结果如下:

  • 调用remove方法前后的区别

    -调用前

    调用后:



    可见被删除的元素没有输出

树的三种遍历

四种遍历分别是先序遍历、中序遍历、后序遍历、层序遍历

先序遍历就是:对节点的除了工作是在它的诸儿子节点被处理前进行的,也就是说先访问根再访问左子树然后访问右子树。

中序遍历:对节点的除了工作是在它的左儿子节点被处理后进行的,也就是说先访问左节点再访问根节点然后访问右节点。

后序遍历:对节点的除了工作是在它的诸儿子节点被处理后进行的,也就是说先访问左再访问根子树然后访问右子树。

层序遍历:和前三种遍历方式不一样,层序遍历需要借用队列,从根节点读入,然后出队,只要出队的元素有左节点,左节点就入队,若还有右节点,右节点就入队,直到队列为空。

java:数据结构(四)二叉查找树以及树的三种遍历的更多相关文章

  1. golang数据结构之树的三种遍历方式

    tree.go package tree import ( "fmt" ) type TreeNode struct { ID int Val int Left *TreeNode ...

  2. Java集合框架Collection(1)ArrayList的三种遍历方法

    ArrayList是java最重要的数据结构之一,日常工作中经常用到的就是ArrayList的遍历,经过总结,发现大致有三种,上代码: package com.company; import java ...

  3. 树的三种遍历方式(C语言实现)

    //************************************************************************* // [前序]遍历算法 //二叉树不空,先访问根 ...

  4. Java中Map的三种遍历方法

    Map的三种遍历方法: 1. 使用keySet遍历,while循环: 2. 使用entrySet遍历,while循环: 3. 使用for循环遍历.   告诉您们一个小秘密: (下↓面是测试代码,最爱看 ...

  5. Java中List集合的三种遍历方式(全网最详)

    List集合在Java日常开发中是必不可少的,只要懂得运用各种各样的方法就可以大大提高我们开发的效率,适当活用各种方法才会使我们开发事半功倍. 我总结了三种List集合的遍历方式,下面一一来介绍. 首 ...

  6. Java List /ArrayList 三种遍历方法

    java list三种遍历方法性能比较http://www.cnblogs.com/riskyer/p/3320357.html JAVA LIST 遍历http://blog.csdn.net/lo ...

  7. 2017.10.25 Java List /ArrayList 三种遍历方法

    java list三种遍历方法性能比较 学习java语言list遍历的三种方法,顺便测试各种遍历方法的性能,测试方法为在ArrayList中插入记录,然后遍历ArrayList,测试代码如下: pac ...

  8. Java 数组元素逆序Reverse的三种方式

    Java 数组元素逆序Reverse的三种方式   本文链接:https://blog.csdn.net/xHibiki/article/details/82930521 题目 代码实现 说明 int ...

  9. 创建B树,动态添加节点,并使用三种遍历算法对树进行遍历

    ks17:algorithm apple$ cat btree_test.c ///********************************************************** ...

随机推荐

  1. Loading class `com.mysql.jdbc.Driver'. This is deprecated. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.

    简单介绍 声明:使用JDK9.MYSQL8.idea 报错处理 报错信息如下 原因 提示信息表明数据库驱动com.mysql.jdbc.Driver已经被弃用了.应当使用新的驱动com.mysql.c ...

  2. JavaScript中的循环和闭包

    看一段比较经典的错误代码: // 希望获取页面上的所有div,在点击的时输出对应的编号 var oDom = document.querySelectorAll("div"); / ...

  3. keepass可以在浏览器中使用吗?

    KeePass不提供浏览器扩展,因此当您访问登录页面时,它不会弹出并提示您.您可以将登录信息从KeePass复制粘贴到网页上的相应框中,甚至可以使用拖放操作将用户名和密码移至上方,但这不是最方便的解决 ...

  4. Dom对象与jQuery对象的互转

    1.Dom对象转换为jQuery对象 a.直接获取视频,得到就是jQuery对象 $('video'); b.我们已经使用原生js,获取过来 Dom对象 var myvide = document.q ...

  5. 求s=a+aa+aaa+aaaa+aa...a的值,其中a是一个数字。例如2+22+222+2222+22222。

    方法一: var num = ""; var nums = []; var a = Number(prompt());//所要拼接的数字 var b = Number(prompt ...

  6. jenkins报错:Problem accessing /jenkins/. Reason: HTTP ERROR 404

    这是一个Jenkins的Bug.临时解决方法是:在浏览器中手工输入:http://<ip>:<port>.不要访问"/jenkins"这个路径.

  7. Windows10 下利用Hyper-V安装CentOS系统

    开启Windows10的Hyper-v功能(需要重启电脑) 控制面板→程序→启用或关闭Windows功能→打开Hyper-v→确定 创建虚拟机 在Windows管理工具中找到Hyper-v管理器并双击 ...

  8. 【python爬虫】cookie & session

    一.什么是cookie? cookie是指网站为了鉴别用户身份,进行会话跟踪而存储在客户端本地的数据. 二.什么是session? 本来的含义是指有始有终的一些列动作,而在web中,session对象 ...

  9. Jupyter notebook 使用

    1. 安装代码自动补全 需安装 nbextensions 插件,网站:https://jupyter-contrib-nbextensions.readthedocs.io/en/latest/ins ...

  10. 【TCP/IP网络编程】:02套接字类型与协议设置

    本篇文章主要介绍创建套接字函数相关的3个输入参数的含义,它们最终确定了套接字通信所采用的协议.同时,也简单对比了TCP和UDP传输方式的区别. 什么是协议?协议是对话中使用的通信规则,而在计算机领域则 ...