树
1、树的一些基本概念
- 树:一种数据结构,是由 n个有限的节点组成的一个具有层次关系的集合
- 节点:组成树的部分
- 父节点:该节点的上一个节点
- 子节点:该节点的下一个节点
- 根节点:没有父节点的节点
- 叶子节点:没有子节点的节点
- 分支节点:有子节点的节点
- 节点的权:该节点里面的值
- 路径:从根节点到当前节点的路线
- 层:根节点的层次为 1,其余节点等于该节点的父节点加 1
- 子树:从当前节点看,以它的一个子节点有根节点的树
- 树的高度:树中节点的最大层次
- 森林:0个或多个不相交的树组成,对森林加上一个跟,就成为了树,去掉跟,树就是森林
- 二叉树:每个节点最多只有两个节点的树
- 满二叉树:所有的叶子节点都在最后一层,并且节点的总数为 2^n - 1 (n为层数)
- 完全二叉树:所有的叶子节点都在最后一层或者是倒数第二层,并且最后一层的叶子节点都是左连续,倒数第二层的节点都是右连续
- 前序遍历:先输出父节点,在输出左节点,在输出右节点
- 中序遍历:先输出左节点,在输出父节点,在输出右节点
- 后序遍历:先输出左节点,在输出右节点,在输出父节点
- 前序查找:
- 1、先判断当前节点是否是要查找的节点,如果是,则返回当前节点
- 2、如果不是,则判断当前节点的左节点是否为空,若不为空,则递归前序查找,如果递归找到了要查找的节点,则返回
- 3、如果没找到,则判断当前节点的右节点是否为空,若不为空,则递归前序查找,如果递归找到了要查找的节点,则返回
- 4、如果还是没有找到,则表示当前树中没有符合要查找的节点,返回null
- 中序查找:
- 1、先判断当前节点的左节点是否为空,若不为空,则递归中序查找,如果递归找到了要查找的节点,则返回
- 2、如果没找到,则与当前节点进行比较,如果符合要查找的条件,则返回当前节点
- 3、如果不符合,则判断当前节点的右节点是否为空,若不为空,则递归中序查找,如果递归找到了要查找的节点,则返回
- 4、如果还是没有找到,则表示当前树中没有符合要查找的节点,返回null
- 后序查找:
- 1、先判断当前节点的左节点是否为空,若不为空,则递归后序查找,如果递归找到了要查找的节点,则返回
- 2、如果没找到,则判断当前节点的右节点是否为空,若不为空,则递归后序查找,如果递归找到了要查找的节点,则返回
- 3、如果没找到,则与当前节点进行比较,如果符合,则返回当前节点
- 4、如果还是没有找到,则表示当前树中没有符合要查找的节点,返回null
2、树的性质
- 二叉树的第 i层上的节点数目最多为 2i - 1个节点(i >= 1)
- 深度为 k的二叉树至多有 2k - 1个节点(k >= 1)
- 包含 n个节点的二叉树的高度至少为 (log2n) + 1
- 在任意一颗二叉树之中,若终端节点的个数为 n,度为 2的节点数为 m,则 n = m + 1
- 二叉排序树之中,若任意节点的子节点不为空,则左子树上所有节点的值都小于该节点,右子树上的所有节点 的值都大于该节点
- 没有键值相等的节点
3、对树的一些基本操作
3.1、前序遍历
//前序遍历
public void frontOrder() {
//首先输出父节点
System.out.println(this);
//如果当前节点的左子树不为空,递归进行左子树的遍历
if (this.getLeft() != null) {
this.left.frontOrder();
}
//如果当前节点的右子树不为空,递归进行右子树的遍历
if (this.getRight() != null) {
this.right.frontOrder();
}
}
中序遍历与后序遍历其实和前序遍历是一样的,只是一些代码的顺序稍微改变了而已
3.2、中序遍历
3.3、后序遍历
3.4、前序查找
/**
* 前序遍历查找的方法
*
* @param no 需要查找的节点的 no
* @return 如果找到了就返回该 Node,如果没找到,则返回null
*/
public Node frontOrderSearch(int no) {
//定义一个临时接收返回结果的变量
Node node = null;
//先判断当前节点是否等于要查找的节点,如果相等,则返回当前节点
if (this.no == no) {
return this;
}
//如果不想等,则判断当前节点的左节点是否为空,若不为空,则递归前序查找
if (this.left != null) {
node = this.left.frontOrderSearch(no);
}
//如果递归找到了要查找的节点,则返回
if (node != null) {
return node;
}
//如果没找到,则判断当前节点的右节点是否为空,若不为空,则递归前序查找
if (this.right != null) {
node = this.left.frontOrderSearch(no);
}
//如果递归找到了要查找的节点,则返回,如果还是没有找到,则表示当前树中没有符合要查找的节点,返回null
return node;
}
中序查找与后序后序其实和前序查找是一样的,只是一些代码的顺序稍微改变了而已
3.5、中序查找
3.6、后序查找
3.7、添加节点
//先创建一个二叉树
BinaryTree binaryTree = new BinaryTree();
//创建我们所需要的节点,第一个为根节点
Node root = new Node(1, "黑猫");
Node node2 = new Node(2, "白猫");
Node node3 = new Node(3, "黄猫");
Node node4 = new Node(4, "绿猫");
/*
目前我只会手动添加
*/
binaryTree.setRoot(root);
root.setLeft(node2);
root.setRight(node3);
node3.setRight(node4);
3.7、删除节点
/**
* 删除节点的方法
* 直接将要删除的节点与其子树一起删除了
*
* @param no 需要删除的节点的编号
*/
public void deleteNode(int no) {
//如果当前节点的左节点不为空,且当前节点的左节点就是需要删除的节点,而应该是去判断当前节点的子节点是否是需要删除的
if (this.left != null && this.left.no == no) {
this.left = null;
return;
}
//如果当前节点的右节点不为空,且当前节点的右节点就是需要删除的节点,那么直接将 this.right = null即可(结束递归)
if (this.right != null && this.right.no == no) {
this.right = null;
return;
}
//对左子树进行递归删除
if (this.left != null) {
this.left.deleteNode(no);
}
//对右子树进行递归删除
if (this.right != null) {
this.right.deleteNode(no);
}
}
4、完整源码
public class TreeDemo {
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, "绿猫");
binaryTree.setRoot(root);
root.setLeft(node2);
root.setRight(node3);
node3.setRight(node4);
//遍历树
System.out.println("前序遍历:");
binaryTree.frontOrder();
System.out.println("中序遍历:");
binaryTree.middleOrder();
System.out.println("后序遍历:");
binaryTree.rearOrder();
System.out.println("-----------------------------");
//查找树
System.out.println("前序查找:");
Node node = binaryTree.frontOrderSearch(1);
if (node != null) {
System.out.println("该编号信息为\n编号:" + node.getNo() + " 名字:" + node.getName());
} else {
System.out.println("没找到");
}
System.out.println("中序查找:");
binaryTree.middleOrderSearch(1);
if (node != null) {
System.out.println("该编号信息为\n编号:" + node.getNo() + " 名字:" + node.getName());
} else {
System.out.println("没找到");
}
System.out.println("后序查找:");
binaryTree.rearOrderSearch(1);
if (node != null) {
System.out.println("该编号信息为\n编号:" + node.getNo() + " 名字:" + node.getName());
} else {
System.out.println("没找到");
}
System.out.println("-----------------------------");
//删除节点
System.out.println("删除前,使用前序遍历:");
binaryTree.frontOrder();
binaryTree.deleteNode(2);
System.out.println("删除后,使用前序遍历:");
binaryTree.frontOrder();
System.out.println("-----------------------------");
}
}
//定义一个二叉树
class BinaryTree {
//定义一个根节点
private Node root;
public void setRoot(Node root) {
this.root = root;
}
//前序遍历
public void frontOrder() {
if (this.root != null) {
this.root.frontOrder();
} else {
System.out.println("该二叉树为空!");
}
}
//中序遍历
public void middleOrder() {
if (this.root != null) {
this.root.middleOrder();
} else {
System.out.println("该二叉树为空!");
}
}
//后序遍历
public void rearOrder() {
if (this.root != null) {
this.root.rearOrder();
} else {
System.out.println("该二叉树为空!");
}
}
//前序查找
public Node frontOrderSearch(int no) {
if (root != null) {
return root.frontOrderSearch(no);
} else {
return null;
}
}
//中序查找
public Node middleOrderSearch(int no) {
if (root != null) {
return root.middleOrderSearch(no);
} else {
return null;
}
}
//后序查找
public Node rearOrderSearch(int no) {
if (root != null) {
return root.rearOrderSearch(no);
} else {
return null;
}
}
//删除节点
public void deleteNode(int no){
if (root != null){
//如果只有一个root节点,这里立即判断root是不是就是要删除的节点
if (root.getNo() == no){
root = null;
}else {
//递归删除
root.deleteNode(no);
}
}else {
System.out.println("空树,不能删除!");
}
}
}
//创建一个节点类
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 + '\'' +
'}';
}
//前序遍历
public void frontOrder() {
//首先输出父节点
System.out.println(this);
//如果当前节点的左子树不为空,递归进行左子树的遍历
if (this.getLeft() != null) {
this.left.frontOrder();
}
//如果当前节点的右子树不为空,递归进行右子树的遍历
if (this.getRight() != null) {
this.right.frontOrder();
}
}
//中序遍历
public void middleOrder() {
//如果当前节点的左子树不为空,递归进行左子树的遍历
if (this.getLeft() != null) {
this.left.middleOrder();
}
//输出父节点
System.out.println(this);
//如果当前节点的右子树不为空,递归进行右子树的遍历
if (this.getRight() != null) {
this.right.middleOrder();
}
}
//后序遍历
public void rearOrder() {
//如果当前节点的左子树不为空,递归进行左子树的遍历
if (this.getLeft() != null) {
this.left.rearOrder();
}
//如果当前节点的右子树不为空,递归进行右子树的遍历
if (this.getRight() != null) {
this.right.rearOrder();
}
//输出父节点
System.out.println(this);
}
/**
* 前序遍历查找的方法
*
* @param no 需要查找的节点的 no
* @return 如果找到了就返回该Node,如果没找到,则返回null
*/
public Node frontOrderSearch(int no) {
//定义一个临时接收返回结果的变量
Node node = null;
//先判断当前节点是否等于要查找的节点,如果相等,则返回当前节点
if (this.no == no) {
return this;
}
//如果不想等,则判断当前节点的左节点是否为空,若不为空,则递归前序查找
if (this.left != null) {
node = this.left.frontOrderSearch(no);
}
//如果递归找到了要查找的节点,则返回
if (node != null) {
return node;
}
//如果没找到,则判断当前节点的右节点是否为空,若不为空,则递归前序查找
if (this.right != null) {
node = this.left.frontOrderSearch(no);
}
//如果递归找到了要查找的节点,则返回,如果还是没有找到,则表示当前树中没有符合要查找的节点,返回null
return node;
}
/**
* 中序遍历查找的方法
*
* @param no 需要查找的节点的 no
* @return 如果找到了就返回该Node,如果没找到,则返回null
*/
public Node middleOrderSearch(int no) {
//定义一个临时接收返回结果的变量
Node node = null;
//先判断当前节点的左节点是否为空,若不为空,则递归前序查找
if (this.left != null) {
node = this.left.middleOrderSearch(no);
}
//如果递归找到了要查找的节点,则返回
if (node != null) {
return node;
}
//如果没找到,则判断当前节点是否等于要查找的节点,如果相等,则返回当前节点
if (this.no == no) {
return this;
}
//如果没找到,则判断当前节点的右节点是否为空,若不为空,则递归前序查找
if (this.right != null) {
node = this.left.middleOrderSearch(no);
}
//如果递归找到了要查找的节点,则返回,如果还是没有找到,则表示当前树中没有符合要查找的节点,返回null
return node;
}
/**
* 后序遍历查找的方法
*
* @param no 需要查找的节点的 no
* @return 如果找到了就返回该Node,如果没找到,则返回null
*/
public Node rearOrderSearch(int no) {
//定义一个临时接收返回结果的变量
Node node = null;
//先判断当前节点的左节点是否为空,若不为空,则递归前序查找
if (this.left != null) {
node = this.left.rearOrderSearch(no);
}
//如果递归找到了要查找的节点,则返回
if (node != null) {
return node;
}
//如果没找到,则判断当前节点的右节点是否为空,若不为空,则递归前序查找
if (this.right != null) {
node = this.left.rearOrderSearch(no);
}
//如果没找到,则判断当前节点是否等于要查找的节点,如果相等,则返回当前节点
if (this.no == no) {
return this;
}
//如果递归找到了要查找的节点,则返回,如果还是没有找到,则表示当前树中没有符合要查找的节点,返回null
return node;
}
/**
* 删除节点的方法
*
* @param no 需要删除的节点的编号
*/
public void deleteNode(int no) {
//如果当前节点的左节点不为空,且当前节点的左节点就是需要删除的节点,而应该是去判断当前节点的子节点是否是需要删除的
if (this.left != null && this.left.no == no) {
this.left = null;
return;
}
//如果当前节点的右节点不为空,且当前节点的右节点就是需要删除的节点,那么直接将 this.right = null即可(结束递归)
if (this.right != null && this.right.no == no) {
this.right = null;
return;
}
//对左子树进行递归删除
if (this.left != null) {
this.left.deleteNode(no);
}
//对右子树进行递归删除
if (this.right != null) {
this.right.deleteNode(no);
}
}
}