这是我参与8月更文挑战的第23天,活动详情查看:8月更文挑战”
树
为什么需要树中结构呢?
数组存储方式的解析:
1、 优点:通过下标方式访问元素,速度快,对于有序数组还可以使用二分查找提高检索速度。
2、 缺点:如果要检索具体某个值,或者插入值会整理移动,效率较低
链式存储方式分析:
- 优点:在一定程序上对数组存储方式有优化,比如插入一个数值时,只需要讲插入点接到链表中即可,删除效率也是同理效果好。
- 缺点:在进行检索时,效率仍然很低,检索某一个值时,需要从链表头一直做检索。
树存储方式分析:
能提高数据存储,读取的效率,比如可以使用二叉树既可以保证数据检索速度,同时也可以保证数据的插入,删除,修改的速度。
二叉树
1.二叉树概念
二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。二叉树特点是每个结点最多只能有两棵子树,且有左右之分
2. 树示意图
常用术语:
1、 结点
2、 根结点
3、 父结点
4、 子结点
5、 叶子结点(没有子结点的结点)
6、 结点的权(结点值)
7、 路径(从root结点找到目标结点的线路)
8、 层
9、 子树
10、 树的高度(最大层数)
11、 森林(多颗子树构成森林)
3. 二叉树介绍
1、树有很多种,每个结点最多只能有两个子结点的一种形式称之为二叉树
2、 二叉树分为左结点和右结点
3、 如果该二叉树的所有叶子结点都在最后一层,并且结点总数是2^n-1,n是层数,则我们称之为满二叉树
4、 如果该二叉树的所有叶子结点都在最后一层或者倒数第二层,而且最后一层的叶子结点在左边连续,倒数第二层的叶子结点在右边连续,我们称之为全完二叉树。
4. 二叉树应用案例
可以使用前序,中序,后序对下面的二叉树进行遍历
1、 前序遍历:先输出父结点,在遍历左子树和右子树
2、 中序遍历:先遍历左子树,在遍历父结点,再遍历右子树
3、 后序遍历:先遍历左子树,再遍历右子树,最后遍历父结点
结论:看父结点输出顺序即是某序遍历
Node
/**
* author:韩国庆
* date:2021/3/22 16:55
* version:1.0
*/
public class Node {
private int no;
private String name;
private Node left;
private Node right;
public Node(int no,String name){
this.no = no;
this.name = name;
}
public int getNo(){
return no;
}
public void setNo(int no){
this.no = no;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRight() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
@Override
public String toString() {
return "Node{" +
"no=" + no +
", name='" + name + '\'' +
", left=" + left +
", right=" + right +
'}';
}
/**
* 前序遍历
*/
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);
}
}
BinaryTree
/**
* author:韩国庆
* date:2021/3/22 16:47
* version:1.0
*/
public class BinaryTree {
private Node root;
public void setRoot(Node root) {
this.root = root;
}
/**
* 前序遍历
*/
public void preOrder(){
if (this.root !=null){
this.root.preOrder();
}else {
System.out.println("二叉树为空,无法遍历");
}
}
public void infixOrder(){
if (this.root !=null){
this.root.infixOrder();
}else {
System.out.println("二叉树为空,无法遍历");
}
}
public void postOrder(){
if (this.root !=null){
this.root.postOrder();
}else {
System.out.println("二叉树为空,无法遍历");
}
}
}
Test
/**
* author:韩国庆
* date:2021/3/23 14:23
* version:1.0
*/
public class Test {
public static void main(String[] args) {
BinaryTree binaryTree = new BinaryTree();
Node root = new Node(1,"孙尚香");
Node node2 = new Node(2,"夏侯惇");
Node node3 = new Node(3,"貂蝉");
Node node4 = new Node(4,"吕布");
Node node5 = new Node(5,"虞姬");
Node node6 = new Node(6,"王昭君");
root.setLeft(node2);
node2.setLeft(node3);
root.setRight(node4);
node4.setRight(node5);
node2.setLeft(node6);
binaryTree.setRoot(root);
/**
* 前序
*/
binaryTree.preOrder();
System.out.println("-----------------------------------------------");
binaryTree.infixOrder();
System.out.println("-----------------------------------------------");
binaryTree.postOrder();
}
}
二叉树 查询结点
根据后序,中序,前序来进行查找
Node
package com.bjpowernode.binary;
/**
* author:韩国庆
* date:2021/3/22 16:55
* version:1.0
*/
public class Node {
private int no;
private String name;
private Node left;
private Node right;
public Node(int no,String name){
this.no = no;
this.name = name;
}
public int getNo(){
return no;
}
public void setNo(int no){
this.no = no;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRight() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
@Override
public String toString() {
return "Node{" +
"no=" + no +
", name='" + name + '\'' +
", left=" + left +
", right=" + right +
'}';
}
/**
* 前序遍历
*/
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);
}
/**
* 前序遍历查找
*/
public Node preOrderSearch(int no){
/**
* 判断是否是当前结点
*/
if (this.no == no){
return this;
}
/**
* 判断当前结点的左子结点是否为空,如果不是空,则递归前序查找
如果左递归前序查找,找到结点,则返回
*/
Node lNode = null;
if (this.left !=null){
lNode = this.left.preOrderSearch(no);
}
if (lNode !=null){
return lNode;
}
/**
* 左递归前序查找,找到结点,则返回否则继续判断
*
* 当前的结点的右子结点是否为空,如果不空,则继续像右递归前序查找
*/
if (this.right !=null){
lNode = this.right.preOrderSearch(no);
}
return lNode;
}
/**
* 中序遍历查找
*/
public Node infixOrderSearch(int no){
/**
* 判断当前结点左子结点是否为空,如果不为空,则递归中序查找
*/
Node node = null;
if (this.left !=null){
node = this.left.infixOrderSearch(no);
}
if (node !=null){
return node;
}
if (this.no == no){
return this;
}
if (this.right !=null){
node = this.right.infixOrderSearch(no);
}
return node;
}
/**
* 后序遍历查找
*/
public Node postOrderSearch(int no){
Node node = null;
if (this.left !=null){
node = this.left.postOrderSearch(no);
}
if (node !=null){
return node;
}
/**
* 如果左边子树没有找到,则向右边子树递归进行后序遍历查找
*/
if (this.right !=null){
node = this.right.postOrderSearch(no);
}
if (node !=null){
return node;
}
/**
* 如果左右子树都没有找到,那么判断当前结点是否是呢
*/
if (this.no==no){
return this;
}
return node;
}
}
BinaryTree
public class BinaryTree {
private Node root;
public void setRoot(Node root) {
this.root = root;
}
/**
* 前序遍历
*/
public void preOrder(){
if (this.root !=null){
this.root.preOrder();
}else {
System.out.println("二叉树为空,无法遍历");
}
}
public void infixOrder(){
if (this.root !=null){
this.root.infixOrder();
}else {
System.out.println("二叉树为空,无法遍历");
}
}
public void postOrder(){
if (this.root !=null){
this.root.postOrder();
}else {
System.out.println("二叉树为空,无法遍历");
}
}
/**
* 前序
*/
public Node preOrderNode(int no){
if (root !=null){
return root.preOrderSearch(no);
}else {
return null;
}
}
/**
* 中序
*/
public Node infixOrderNode(int no){
if (root !=null){
return root.infixOrderSearch(no);
}else {
return null;
}
}
/**
* 后序
*/
public Node postOrderNode(int no){
if (root !=null){
return root.postOrderSearch(no);
}else {
return null;
}
}
}
test
* author:韩国庆
* date:2021/3/23 14:23
* version:1.0
*/
public class Test {
public static void main(String[] args) {
BinaryTree binaryTree = new BinaryTree();
Node root = new Node(1,"孙尚香");
Node node2 = new Node(2,"夏侯惇");
Node node3 = new Node(3,"貂蝉");
Node node4 = new Node(4,"吕布");
Node node5 = new Node(5,"虞姬");
Node node6 = new Node(6,"王昭君");
root.setLeft(node2);
node2.setLeft(node3);
root.setRight(node4);
node4.setRight(node5);
node2.setLeft(node6);
binaryTree.setRoot(root);
/**
* 前序
*/
/*binaryTree.preOrder();
System.out.println("-----------------------------------------------");
binaryTree.infixOrder();
System.out.println("-----------------------------------------------");
binaryTree.postOrder();*/
/**
* 根据id 前序查找
*/
Node node = binaryTree.preOrderNode(5);
if (node !=null){
System.out.printf("信息为:id=%d name%s",node.getNo(),node.getName());
}else {
System.out.println("没有找到");
}
/**
* 后序查找
*/
Node infix = binaryTree.infixOrderNode(5);
if (infix !=null){
System.out.printf("信息为:id=%d name%s",infix.getNo(),infix.getName());
}else {
System.out.println("没有找到");
}
Node post = binaryTree.postOrderNode(5);
if (post !=null){
System.out.printf("信息为:id=%d name%s",post.getNo(),post.getName());
}else {
System.out.println("没有找到");
}
}
}
二叉树 删除结点
删除结点可划分为两种情况:
1、 删除的结点是叶子结点,那么删除当前几点即可。
2、 删除的结点是非叶子结点,那么需要删除该子树。
删除需求:
1、 删除5号结点
Node类增加删除方法
public void delNode(int no){
/**
* 注意:
* 1、二叉树是单向的,所以我们是判断当前结点的子结点是否需要删除,而不能去判断当前这个结点是不是需要删除结点
* 2、如果当前结点左子结点不为空,并且左子结点就是需要删除的结点,则将this.left = null,返回即可
* 3、如果当前结点右子结点不为空,并且右子结点就是需要删除的结点,就将this.right = null,返回即可
* 4、如果2,3步没有执行,那么需要向左子树进行递归删除
* 5、如果第4步也没有删除结点,则向右子树进行递归删除
*/
/**
* 第2步操作
*/
if (this.left !=null &&this.left.no==no){
this.left = null;
return;
}
/**
* 第3步操作
*/
if (this.right !=null && this.right.no==no){
this.right = null;
return;
}
/**
* 第4步:向左子树递归删除
*/
if (this.left !=null){
this.left.delNode(no);
}
/**
* 第5步:向右子树递归删除
*/
if (this.right !=null){
this.right.delNode(no);
}
}
BinaryTree类增加删除方法
if (root !=null){
if (root.getNo()==no){
root = null;
}else {
root.delNode(no);
}
}else {
System.out.println("树为空,不能操作删除");
}
}
测试