package com.learn.btree;
/**
* 二叉链表的节点
* @author Leon.Sun
*
*/
public class Node {
/**
* 节点的值
*/
// private Object value;
Object value;
/**
* 左孩子
* 左子树的引用
* 同样为了处理方便,我们把private去掉
*/
// private Node leftChild;
Node leftChild;
/**
* 右子树的引用
*/
// private Node rightChild;
Node rightChild;
public Node() {
super();
}
public Node(Object value) {
super();
this.value = value;
}
public Node(Object value, Node leftChild, Node rightChild) {
super();
this.value = value;
this.leftChild = leftChild;
this.rightChild = rightChild;
}
/**
* 这个toString该怎么写呢
* 输出三个属性的值就可以了
*/
@Override
public String toString() {
return "Node [value=" + value + ", leftChild=" + leftChild + ", rightChild=" + rightChild + "]";
}
}
package com.learn.btree;
/**
* 二叉树接口
* 可以有不同的实现类,每个类可以使用不同的存储结构,比如顺序结构、链式结构
* 链式结构可以是二叉的,也可以是三叉的
* @author Leon.Sun
*
*/
public interface BinaryTree {
/**
* 是否空树
* 你的树是空的吗,空是什么意思
* 有没有根节点,有根就不是空的了
* @return
*/
public boolean isEmpty();
/**
* 树结点数量
* 树里面有几个节点
* @return
*/
public int size();
/**
* 获取二叉树的高度
* 得到树的高度
* @return
*/
public int getHeight();
/**
* 查询指定值的结点
* 在树里面去找一个值
* 我去找20,要告诉我这里没有20才可以
* @param value
* @return
*/
public Node findKey(int value); // 查找
/**
* 前序递归遍历
* 这三个遍历要采用递归来实现
*/
public void preOrderTraverse();
/**
* 中序遍历递归操作
*/
public void inOrderTraverse();
/**
* 后序遍历递归操作
*/
public void postOrderTraverse();
/**
* 后序遍历递归操作
* 这个是重载,一个有参,一个无参
* @param node 树根结点
*/
public void postOrderTraverse(Node node);
/**
* 中序遍历非递归操作
* 1)对于任意节点current,若该节点不为空则将该节点压栈,并将左子树节点置为current,重复此操作,直到current为空。
* 2)若左子树为空,栈顶节点出栈,访问节点后将该节点的右子树置为current
* 3) 重复1、2步操作,直到current为空且栈内节点为空。
*/
public void inOrderByStack();
/**
* 前序遍历非递归操作
* 1)对于任意节点current,若该节点不为空则访问该节点后再将节点压栈,并将左子树节点置为current,重复此操作,直到current为空。
* 2)若左子树为空,栈顶节点出栈,将该节点的右子树置为current
* 3) 重复1、2步操作,直到current为空且栈内节点为空。
*/
public void preOrderByStack();
/**
* 后序遍历非递归操作
* 1)对于任意节点current,若该节点不为空则访问该节点后再将节点压栈,并将左子树节点置为current,重复此操作,直到current为空。
* 2)若左子树为空,取栈顶节点的右子树,如果右子树为空或右子树刚访问过,则访问该节点,并将preNode置为该节点
* 3) 重复1、2步操作,直到current为空且栈内节点为空。
*/
public void postOrderByStack();
/**
* 按照层次遍历二叉树
* 这个需要借助队列来实现,这里还是包含很多技能点的
*/
public void levelOrderByStack();
}
package com.learn.btree;
/**
* 他要实现一下2我们的BinaryTree,要实现这个接口
* 你把这些方法掌握了,那二叉树的基本操作呢就基本上知道了
* 但不是所有的操作,基本操作都知道了
* 这是一个链式二叉树的实现类
*
* 这个类里面我们做一个操作,
* @author Leon.Sun
*
*/
public class LinkedBinaryTree implements BinaryTree {
/**
* 根节点
*/
private Node root;
/**
* 我每次增加一个节点的时候他就加1
* 但是为什么我们目前没有写size,
* 因为目前没有增加的方法,
* 我们创建这棵树,是直接做的,
* 当然还有简单的方法,创建这棵树时候,你把7传进来
* 但是这么做也没有什么技术含量,这个size我们每次=求就可以了
*/
// private int size;
/**
* 然后我们再给他提供一个构造方法
*/
public LinkedBinaryTree(Node root) {
super();
this.root = root;
}
public LinkedBinaryTree() {
super();
}
/**
* 这里永远返回false,
* 怎么样这棵树才是空的呢,树是空的,一个节点也没有
*/
@Override
public boolean isEmpty() {
/**
* 就是root等于null
* 如果root等于的话他就是一个空的树
*/
return root==null;
}
/**
* 我们的这个size该怎么办,
*/
@Override
public int size() {
System.out.println("二叉树节点的个数: ");
/**
* 你想一下他的数量应该是多少
* 你怎么知道他的结构是7呢,
* 因为左边是2,右边是4,
* 整个就是2+4+根1等于7
* 你怎么知道这个是4呢,
* 左边是1,右边是2,再加上根1,等于4
* 你怎么知道这个是2呢,
* 左边是0,右边是1,再加上这个根1,等于2
* 你怎么知道7是1呢,
* 左边是0,右边是0,再加上1等于1
* 好像和我们刚才求高度是一样的
* 只不过高度里面是求两个里面最大的树
* 我们现在是两个数相加
*
*/
return this.size(root);
}
/**
* 这里还是写成private不对外公布了
* 这里面要写一个Node,
* 写Node root吧,root可读性强
*/
private int size(Node root) {
/**
* 如果root等于null还是返回0
*/
if(root==null) {
return 0;
}else {
/**
* 获取左子树的size
*/
int nl = this.size(root.leftChild);
/**
* 获取右子树的size
*/
int nr = this.size(root.rightChild);
/**
* 返回的是谁,
* 返回的是nl+nr+1
* 返回左子树右子树size只和再加1
*/
return nl+nr+1;
}
}
/**
* 二叉树的高度
*/
@Override
public int getHeight() {
/**
* 我们在这里输出一句话来
*/
System.out.println("二叉树的高度是: ");
/**
* return这个值,
* 这个值怎么办,和刚才一样提供一个辅助的方法
*/
return this.getHeight(root);
}
/**
* 提供一个辅助的方法
* 这边传入一个Node root
* @param root
* @return
*/
private int getHeight(Node root) {
/**
* 首先是有前提条件的,root不能等于null
* 如果root不等于null我们就做这个事了
*/
if(root==null) {
/**
* 如果root等于null了
* 那就return o呗
* 递归要有一个结束条件
*/
return 0;
}else {
/**
* 获取左子树的高度
*
* 得到一个值,
* 这里是l不是1,
*/
int nl = this.getHeight(root.leftChild);
/**
* 获取右子树的高度
*/
int nr = this.getHeight(root.rightChild);
/**
* 返回谁,返回左子树,右子树较大高度并加1
*
* 最后返回谁
* 如果nl大于nr就返回nl加1
* 如果nl小于nr就返回nr加1
* 你看又是递归,也不怎么复杂
*/
return nl>nr ? nl+1 : nr+1;
}
}
@Override
public Node findKey(int value) {
return null;
}
/**
* 先序 遍历分几步,分三步
*/
@Override
public void preOrderTraverse() {
/**
* 输出根节点的值
* 我们只要value的值1,
* 这样1就出来了
* 这是有条件的,如果root要是等于空呢,
* 空节点你就别输出了,这也算递归的一个结构条件
*
* 我们只要简单的变一下就可以了,把这里的ln去了
*/
if(root!=null) {
System.out.print(root.value + " ");
/**
* 对左子树进行先序遍历
* 左边玩不了就不会走右边
*
* 对做子树进行遍历怎么办
* 这跟左也没有关系啊,
*
* 如果root不等于null,创建做子树
*
*/
if(root.leftChild!=null) {
/**
* 创建左子树,左子树本来就是存在的
* 对做子树进行先序遍历
* 构建一个二叉树,根是左子树的根
* 拿着root.leftChild做根
*/
BinaryTree leftTree = new LinkedBinaryTree(root.leftChild);
/**
* 拿着左子树,4做根,
* 这是一颗二叉树了,对它进行遍历
* 对root.leftChild进行遍历
* 对左子树进行先序遍历
*/
leftTree.preOrderTraverse();
}
/**
* 对右子树进行先序遍历
* 只要这三步就可以
*
* preOrderTraverse这跟右子树也没有关系,
* 什么左子树什么右子树,this是当前这棵树
* 他找的是当前这棵树,当前这棵树是谁啊,
* 就是btree,你这么来做的话可能会死循环了,
*
* 但是你这么写的话代码有点繁琐,条件都是一样的,
*/
if(root.rightChild!=null) {
BinaryTree rightTree = new LinkedBinaryTree(root.rightChild);
rightTree.preOrderTraverse();
}
}
}
/**
* 这里面怎么办,
*/
@Override
public void inOrderTraverse() {
/**
* 再调用一个方法,不管别的我先输出一个中序遍历
*/
System.out.println("中序遍历");
/**
* 第一次调用的时候是不是需要把整个二叉树的根传进去
* 是不是有参数的,它是对整个树进行二叉遍历的
*/
this.inOrderTraverse(root);
/**
* 输出换行
*/
System.out.println();
}
/**
* 我们拿过来复制一下,这个方法是辅助的
* 可以不对外开发,直接写成private
* 传一个Node root
* 这个是不是有根啊
*
* 我再提供一个辅助的方法,还要传参
* 开发者考虑这些就可以了,然后再递归调用
* 说来说去还要看结果的运行,
* 别到时运行结果是错的那就不好了
*/
private void inOrderTraverse(Node root) {
/**
* 再调用一个方法
* 如果root不等于null做三件事
*
* 三条语句加一个判断,就可以了
* 是不是都用到了递归,我们体会到递归的强大之处
*/
if(root!=null) {
/**
* 遍历左子树
* 怎么遍历左子树,这里面本来就有参数
*
* 你知道你调用inOrderTraverse(root.leftChild)
* 这句话意味着什么吗,刚开始root传的是谁,传的是1,
* 下一份传的是谁啊,相当于拿着4作为根了去遍历一下
* 递归要用了,调了好多次了,到了叶子的时候还要调,当我你们拿到7做根的时候
* 拿到node7做根了,如果我拿到node7做根,传了node7叶子节点
* node7不等于空,所以对他的左子树进行遍历,左子树这个时候已经是null了
*
*/
if(root.leftChild!=null) {
this.inOrderTraverse(root.leftChild);
}
/**
* 输出根的值
* 输出他的值,他的值是7
*/
System.out.print(root.value + " ");
/**
* 遍历右子树,右子树也是null了,
*/
if(root.rightChild!=null) {
this.inOrderTraverse(root.rightChild);
}
}
}
@Override
public void postOrderTraverse() {
/**
* 输出一下后序遍历
*/
System.out.println("后序遍历");
/**
* 第一次调用是root的根
*/
this.postOrderTraverse(root);
/**
* 输出玩之后再来一个换行
*/
System.out.println();
}
/**
* 我们再提供一个辅助的方法,这个辅助方法可以不对外面的调用者开发
* 这里面要传一个参数,但是这个root是谁不确定,这个方法本来就有了
* 有了我就直接用它了
*/
@Override
public void postOrderTraverse(Node node) {
/**
* 如果node等于null,
*
* 这里不是root了,而是node了,
*/
if(node!=null) {
/**
* 后序遍历该怎么办
* 遍历左子树
*/
if(node.leftChild!=null) {
/**
* 左孩子当做一个棵树来遍历
*/
this.postOrderTraverse(node.leftChild);
}
/**
* 遍历右子树
*/
if(node.rightChild!=null) {
this.postOrderTraverse(node.rightChild);
}
/**
* 输出根的值
* 先写这一个
* 不加ln,加个空格
*/
System.out.print(node.value + " ");
}
}
@Override
public void inOrderByStack() {
}
@Override
public void preOrderByStack() {
}
@Override
public void postOrderByStack() {
}
@Override
public void levelOrderByStack() {
}
}
package com.learn.btree;
/**
* 在这个测试类里面我们要干什么,
* 这是一个测试类,我们按照这些功能依次来实现
* @author Leon.Sun
*
*/
public class Test {
public static void main(String[] args) {
/**
* 你想进行遍历,我想遍历一下这个数
* 我想看看这个数一共有几层,前提是创建一个二叉树
* 怎么来创建一个二叉树呢,就把这个二叉树创建起来,
* 那该怎么办呢,一个节点一个节点的创建出来,然后指明他们的关系不就可以了吗
* 从叶子开始吧,从5开始,Node5是不是有构造方法,告诉我他的值是多少
* 值是5,左右都没有,这是5了,
*/
Node node5 = new Node(5,null,null);
/**
* 再来创建一个4呗
* 值是多少,值是4,左孩子没有,右孩子是node5
* 是不是指向他啊,这么久成了
*/
Node node4 = new Node(4,null,node5);
/**
* node3也是叶子节点
*/
Node node3 = new Node(3,null,null);
/**
* 下面从7开始吧
* 7是叶子节点,那左右都是null
* 值是7
*/
// Node node7 = new Node(7,null,null);
/**
* node6,值是6,左孩子没有,右孩子是node7
*/
// Node node6 = new Node(6,null,node7);
/**
* node6就变成叶子了
*/
Node node6 = new Node(6,null,null);
/**
* 2还没有呢,怎么办,
* node2值是2,左是node3,右是node6
*/
Node node2 = new Node(2,node3,node6);
/**
* 最后还差一个node1,值是1
* 左边指向的是node4,右边指向的是node2
* 我们相当于把一棵树给搭建好了
* 但是搭建好了之后和LinkedBinargyTree没有一点关系
* 好像只是把这些节点按照一定的规则排到一起了,你下边还要做一件事
* 这棵树你得知道根是谁,只要我们能够找到根,基本上就可以找到别的节点
*/
Node node1 = new Node(1,node4,node2);
/**
* 怎么把我们搭建的一大堆变成一棵树呢,跟我们的类发生关联
* BinaryTree接口,把node1传进去
* 这么一棵树就搭建好了,这个也太没有技术含量了吧
* 如果有100个节点的话,我们要加100棵树吗,
* 因为我们这棵树是没有特点的,他没有说左边的小右边的大
* 值是不是没有规律的,比如我们学过集合了,是不是学过TreeSet,
* 人家里面的树都是有序的,左边的永远小于根,根永远小于右边的,
* 人家都是递归定义的,这种情况下你随便给个值,不需要我们拼接这个数
* 这个大家知道,
*/
BinaryTree btree = new LinkedBinaryTree(node1);
/**
* 比如我们把这个语句复制一份,我这里没有传这个根过去
* 是不是空的二叉树一个节点也没有,他就是一颗空数组
* 二叉树我们已经实现两个操作了,创建二叉树没有多大含量
*
* 我们试一个特殊的情况,这什么意思啊
* 创建了一个二叉树,他是一个空的
* 那他的节点数量是0,所以我们有时候写代码的时候要健壮
* 某些边界值一定要考虑到,看我们的代码是不是完善
* 这是0的,
*/
// BinaryTree btree = new LinkedBinaryTree();
/**
* 判断二叉树是否为空,这个简单
* 判断二叉树是否为空这个怎么办,输出一下呗
*/
System.out.println(btree.isEmpty());
System.out.println("先序遍历:");
/**
* 先序遍历递归
* 什么叫先序遍历,preOrderTraverse这个就是先序遍历
* 先序遍历的结果是多少,1452367
*
* 但是还是不满意,main方法是调用者来写的,
* 能不能我就调用一个方法,你把所有的事情都给我做了
* 这是增加测试者的负担,还要创建一个左子树和右子树,
* 分别进行遍历,我们针对这个做一些改进,
*/
btree.preOrderTraverse();
System.out.println();
System.out.println("中序遍历:");
/**
* 中序遍历递归
* 中序遍历的结果是这个:4513267
* 结果必须对,中序遍历你只需要做一件事
* 这一句话就够了,有人说你这一句话不太对
* 因为你这里也没有传任何值
*
* 外层不需要传任何的参数,调这个方法就可以了,
* 所有的细节在这里都可以搞定,
*/
btree.inOrderTraverse();
/**
* 后序遍历递归
* 后序遍历:5437621
*
* 到这里我们就把先序中序后序遍历都给写出来了
* 刚开始怎么办,刚开始我们写了一个比较复杂的,
*
*/
btree.postOrderTraverse();
/**
* 中序遍历非递归
* 中序遍历如果采用非递归的遍历
* 需要借助栈
*
* 我们再实现一个功能,这个非递归的我们先等一等吧
* 我们先看一下别的
*/
/**
* 按照层次遍历,借助队列
* 先把这些层次记住
*/
/**
* 在二叉树中查找某个值
*/
/**
* 二叉树的高度,他总共有几层
*
* 二叉树的高度是几啊你觉得,
* 我要得到这棵树的高度,这个该怎么写
* 这棵树的高度是4,4层,我怎么知道这棵树的高度是4
* 要用递归,这和递归有关系吗,递归,树的高度就这么来
* 整棵树的高度是4,我怎么知道是4,先看他左子树的高度是几
* 目前是2,左子树是两层,是2,右子树的高度是几,是3,
* 左子树的高度是2,右子树的高度是3,从里面选一个大的,是3
* 别忘了,还有一个根呢,根是几,根是1,3+1=4
* 有人说你怎么知道右子树是3,左子树这个是1,这个高度是2
* 左子树是1,右子树是2,两个里面找一个大的是几,是2
* 再加上这个根加1,是不是等于3,这好像还是递归,
* 你怎么知道67这棵树的高度是2呢,因为左子树是0,右子树是1
* 一个0一个1,1再加上这个根,2就是这么得到的,
* 你怎么知道7的高度是1,因为他的左子树是0,右子树是0
* 两个最大的是0,再加上个根,等于1,递归
* 首先大家要有这个思想,因为树的定义本来就是递归的,
* 所以解决问题往递归来想,如果这个能够想清楚,代码估计就差不多了
*
* 二叉树的高度是4,是4吧,怎么不是4,把7去了就变成3
* 我们把7去一下,那就变成3了,如果出来个3就差不多了
*
*/
System.out.println(btree.getHeight());
/**
* 二叉树的节点数量
* 把我们要实现二叉树的框架,都给大家搭建出来了
*
* 返回二叉树的节点数量,他用了多少个节点
* 节点数量输出一下,这里面我们有一个方法,
* 我完全可以这么来写
*
* 二叉树的节点数是7个
*/
System.out.println(btree.size());
}
}