二叉树
二叉树了解
1 数组对比链表存储方式分析
1.1 数组
- 优点:通过下标方式访问元素, 速度快。 对于有序数组, 还可使用二分查找提高检索速度
- 缺点: 如果要检索具体某个值, 或者插入值(按一定顺序)会整体移动,效率较低
1.2链表
- 优点: 在一定程度上对数组存储方式有优化(比如: 插入一个数值节点, 只需要将插入节点, 链接到链表中即可,删除效率也很好)。
- 缺点:在进行检索时, 效率仍然较低, 比如(检索某个值, 需要从头节点开始遍历)
1.3二叉树
- 能提高数据存储, 读取的效率, 比如利用 二叉排序树(Binary Sort Tree), 既可以保证数据的检索速度, 同时也可以保证数据的插入, 删除, 修改的速度
2 树的常用术语
- 节点 (每一个节点)
- 根节点 (最上级节点)
- 父节点 (节点的上一节点)
- 子节点 (父节点的下一节点)
- 叶子节点 (没有子节点的节点)
- 节点的权(节点值)
- 路径(从 root 节点找到该节点的路线)
- 层 (节点在第几层)
- 满二叉树
- 完全二叉树
二叉树遍历
思路
- 前序遍历:先输出父节点, 再遍历左子树和右子树
- 中序遍历:先遍历左子树, 再输出父节点, 再遍历右子树
- 后序遍历:先遍历左子树, 再遍历右子树, 最后输出父节点
- 定义二叉树及遍历方法
static class HeroNode {
private int no;
private String name;
private HeroNode left;
private HeroNode right;
//get set .....
//构造 .....
//toString ....
//前序遍历的方法
public void preOrder() {
System.out.println(this);
//递归向左子树遍历
if (this.left != null) {
this.left.preOrder();
}
//递归向右子树遍历
if (this.right != null) {
this.right.preOrder();
}
}
//中序遍历
public void infixOrder() {
//递归向左子树遍历
if (this.left != null) {
this.left.infixOrder();
}
//输出父节点
System.out.println(this);
//递归向右子树遍历
if (this.right != null) {
this.right.infixOrder();
}
}
//后序遍历
public void postOrder() {
//递归向左子树遍历
if (this.left != null) {
this.left.postOrder();
}
//递归向右子树遍历
if (this.right != null) {
this.right.postOrder();
}
//输出父节点
System.out.println(this);
}
}
- 定义root为整个二叉树入口
//定义二叉树
static class BinaryTree {
//根节点
private HeroNode root;
public void setRoot(HeroNode root) {
this.root = root;
}
//前序遍历
public void BinaryPreOrder() {
System.out.println("==============前序遍历");
if (this.root != null) {
this.root.preOrder();
} else {
System.out.println("二叉树为空");
}
}
//中序遍历
public void BinaryInfixOrder() {
System.out.println("==============中序遍历");
if (this.root != null) {
this.root.infixOrder();
} else {
System.out.println("二叉树为空");
}
}
//后序遍历
public void BinaryPostOrder() {
System.out.println("==============后序遍历");
System.out.println();
if (this.root != null) {
this.root.postOrder();
} else {
System.out.println("二叉树为空");
}
//前序查找
public void BinaryPreSearch(int no) {
System.out.println("==============前序查找");
HeroNode heroNode = this.root.preSearch(no);
if (heroNode == null){
System.out.println("没找到");
}else {
System.out.println(heroNode);
}
}
//中序查找
public void BinaryInfixSearch(int no) {
System.out.println("==============中序查找");
HeroNode heroNode = this.root.infixSearch(no);
if (heroNode == null){
System.out.println("没找到");
}else {
System.out.println(heroNode);
}
}
//后序查找
public void BinaryPostSearch(int no) {
System.out.println("==============后序查找");
HeroNode heroNode = this.root.postSearch(no);
if (heroNode == null){
System.out.println("没找到");
}else {
System.out.println(heroNode);
}
}
}
- 测试(手动加了些节点)
public static void main(String[] args) {
BinaryTree tree = new BinaryTree();
HeroNode root = new HeroNode(1, "宋江");
HeroNode heroNode2 = new HeroNode(2, "吴用");
HeroNode heroNode3 = new HeroNode(3, "卢俊义");
HeroNode heroNode4 = new HeroNode(4, "林冲");
HeroNode heroNode5 = new HeroNode(5, "鲁智深");
HeroNode heroNode6 = new HeroNode(6, "鲁智深1");
HeroNode heroNode7 = new HeroNode(7, "鲁智深2");
HeroNode heroNode8 = new HeroNode(8, "鲁智深3");
HeroNode heroNode9 = new HeroNode(9, "鲁智深4");
root.left = heroNode2;
root.right = heroNode3;
heroNode4.left = heroNode6;
heroNode4.right = heroNode7;
heroNode3.left = heroNode4;
heroNode5.left = heroNode8;
heroNode5.right = heroNode9;
heroNode3.right = heroNode5;
tree.root = root;
//
tree.setRoot(root);
//前序遍历
tree.BinaryPreOrder();
}
- 二叉树查找节点
/*
前序查找
*/
public HeroNode preSearch(int no){
System.out.println("前序查找");
//比较当前节点是不是
if (this.no == no){
return this;
}
//1.判断当前节点的左子节点是否为空,不过不为空就继续前序查找
//2.如果查到了节点就返回
HeroNode resNode = null;
if (this.left != null){
resNode = this.left.preSearch(no);
}
if (resNode != null){
return resNode;
}
//1.左递归前序查找,找到节点则返回,否继续判断
//2.当前的节点的右子节点是否为空,不过不空则继续向右递归查找
if (this.right != null){
resNode = this.right.preSearch(no);
}
return resNode;
}
/*
中序遍历查找
*/
public HeroNode infixSearch(int no){
HeroNode resNode = null;
if (this.left != null){
resNode = this.left.infixSearch(no);
}
if (resNode != null){
return resNode;
}
if(this.no == no){
return this;
}
if (this.right != null){
resNode = this.right.infixSearch(no);
}
return resNode;
}
/*
后序查找
*/
public HeroNode postSearch(int no){
HeroNode resNode = null;
if (this.left != null){
resNode = this.left.postSearch(no);
}
if (resNode != null) {
return resNode;
}
if (this.right != null){
resNode = this.right.postSearch(no);
}
if (resNode != null){
return resNode;
}
if (this.no == no){
return this;
}
return resNode;
}
/*
递归删除节点
1:因为此二叉树是单向的,所以判断当前节点的子节点是否是需要删除节点,而不能判断当前节点是不是需要删除的节点
2:如果当前节点的子节点不为空,并且左子节点就是需要删除的节点,就将this.left=null,并返回(递归结束)
3: 如果当前节点的右子节点不为空,并且右子节点就是需要删除的节点,就讲this.right=null,返回(递归结束)
4: 如果第二步和第三步都没有执行删除,那么就需要向左子树递归删除
5:如果第四步也没有执行删除,则向右递归删除
*/
public void delNode(int no){
if (this.left != null && this.left.no == no){
this.left = null;
return;
}
if (this.right != null && this.right.no == no){
this.right = null;
return;
}
if (this.left != null){
this.left.delNode(no);
}
if (this.right != null){
this.right.delNode(no);
}
}
- 课后作业:只删除要删除节点
//上一种删除无法做到删除某一节点保留子节点
/*
我的:解决思路找到要删除节点的上一节点
1:若要删除节点无子节点则,上一节点的下一节点=null即可
2:若要删除节点又子节点,取其某一个叶子节点,优先取左子树的。然后让叶子节点补到要删除节点
*/
public void delNodePlus(int no){
if (this.left != null && this.left.no == no){
HeroNode placeNode = this.left.getLeatNode();
if (placeNode != null){
placeNode.left = this.left == null ? this.left : this.left.left;
placeNode.right = this.left == null ? this.left : this.left.right;
this.left = placeNode;
}else {
this.left = null;
}
return;
}
if (this.right != null && this.right.no == no){
HeroNode placeNode = this.right.getLeatNode();
if (placeNode != null){
placeNode.right = this.right == null ? this.right:this.right.right;
placeNode.left = this.right == null ? this.right:this.right.left;
this.right = placeNode;
}else {
this.right = null;
}
return;
}
if (this.left != null){
this.left.delNodePlus(no);
}
if (this.right != null){
this.right.delNodePlus(no);
}
}
//递归获得节点的左叶子节点
public HeroNode getLeatNode(){
if (this.left != null){
if (this.left.left == null){
HeroNode leftLastNode = this.left;
this.left = null;
return leftLastNode;
}else{
return this.left.getLeatNode();
}
}
return null;
}