为什么需要树这种数据结构?
因为它能够提高数据的存储、读取的效率。比如二叉排序树,既可以保证数据的检索速度,也可以保证数据的插入、删除、修改速度,相比于数组这种存储方式,显然效率以及实用性更高。
二叉树的概念
树有很多种,且每个节点最多只能有两个子节点的一种形式称为二叉树,二叉树的子节点分为左节点和右节点,如果该二叉树的所有叶子结点都在最后一层,且结点总数=2^n-1(n为层数),则称之为满二叉树;如果该二叉树的所有叶子结点都在最后一层或者倒数第二层,且最后一层的叶子结点在左边连续,倒数第二层的叶子结点在右边连续,则称为完全二叉树。
关于二叉树的遍历
二叉树的遍历有三种方式:前序遍历、中序遍历、后序遍历。
前序遍历:先输出父节点,在遍历左子树,然后遍历右子树;
中序遍历:先遍历左子树,在输出父节点,然后遍历右子树;
中序遍历:先遍历左子树,在遍历右子树,然后输出父节点;
根据父节点的输出顺序,就可以快速分辨出是前序、中序还是后序
关于二叉树的一些操作,这里对二叉树的三种遍历方式,以及二叉树查找指定结点和删除结点进行一个简单的演示:
该二叉树示意图:
首先需要定义一个用来表示二叉树中各个节点的对象:它包含了我们所需要的一系列操作
//定义一个节点
public class TreeNode {
private int num;
private TreeNode left;
private TreeNode right;
public TreeNode(int num) {
this.num = num;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public TreeNode getLeft() {
return left;
}
public void setLeft(TreeNode left) {
this.left = left;
}
public TreeNode getRight() {
return right;
}
public void setRight(TreeNode right) {
this.right = right;
}
@Override
public String toString() {
return "Node{" +
"num=" + num +
'}';
}
//前序遍历
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);
}
/**
* 前序遍历查找指定元素
*
* @param num 要进行查找的元素
* @return 如果找到就返回该节点,否则返回null
*/
public TreeNode preOrderSearch(int num) {
System.out.println("前序遍历......");
//比较当前节点是不是要查找的元素
if (this.num == num) {
return this;
}
TreeNode resNode = null;//用于接收在递归过程中找到的目标元素
//如果不是,则需要判断当前节点的左子节点是否为空,如果不为空,则递归前序查找
if (this.left != null) {
resNode = this.left.preOrderSearch(num);
}
if (resNode != null) {
return resNode;
}
//如果也不是,则需要判断当前节点的右子节点是否为空,如果不为空,则递归前序查找
if (this.right != null) {
resNode = this.right.preOrderSearch(num);
}
if (resNode != null) {
return resNode;
}
return null;
}
/**
* 中序遍历查找指定元素
*
* @param num 要进行查找的元素
* @return 如果找到就返回该节点,否则返回null
*/
public TreeNode infixOrderSearch(int num) {
TreeNode resNode = null;//用于接收在递归过程中找到的目标元素
//判断当前节点的左子节点是否为空,如果不为空,则递归前序查找
if (this.left != null) {
resNode = this.left.infixOrderSearch(num);
}
if (resNode != null) {
return resNode;
}
System.out.println("中序遍历......");
//如果不是,则比较当前节点是不是要查找的元素
if (this.num == num) {
return this;
}
//如果也不是,则需要判断当前节点的右子节点是否为空,如果不为空,则递归前序查找
if (this.right != null) {
resNode = this.right.infixOrderSearch(num);
}
if (resNode != null) {
return resNode;
}
return null;
}
/**
* 中序遍历查找指定元素
*
* @param num 要进行查找的元素
* @return 如果找到就返回该节点,否则返回null
*/
public TreeNode postOrderSearch(int num) {
TreeNode resNode = null;//用于接收在递归过程中找到的目标元素
//判断当前节点的左子节点是否为空,如果不为空,则递归前序查找
if (this.left != null) {
resNode = this.left.postOrderSearch(num);
}
if (resNode != null) {
return resNode;
}
//如果不是,则需要判断当前节点的右子节点是否为空,如果不为空,则递归前序查找
if (this.right != null) {
resNode = this.right.postOrderSearch(num);
}
if (resNode != null) {
return resNode;
}
System.out.println("后序遍历......");
//如果也不是,则比较当前节点是不是要查找的元素
if (this.num == num) {
return this;
}
return null;
}
/**
* 删除指定节点
* 如果删除的是叶子结点,直接删除该节点即可;
* 如果删除的是非叶子结点,则直接删除该子树(这里先进行一个简单的操作);后期学到二叉排序树再考虑如何将该节点下的左右子树往上提的问题
*
* @param num 要删除的节点
*/
public void delNode(int num) {
//判断是否删除的是左子节点
if (this.left != null && this.left.getNum() == num) {
this.left = null;
return;
}
//判断是否删除的是右子节点
if (this.right != null && this.right.getNum() == num) {
this.right = null;
return;
}
//递归向左子树查找并删除
if (this.left != null) {
this.left.delNode(num);
}
//递归向右子树查找并删除
if (this.right != null) {
this.right.delNode(num);
}
}
}
然后创建一个二叉树对象
public class BinaryTree {
//根节点
private TreeNode root;
//设置根节点
public void setRoot(TreeNode root) {
this.root = root;
}
//前序遍历
public void preOrder() {
if (root != null) {
root.preOrder();
} else {
System.out.println("该二叉树为空,无法遍历!");
}
}
//中序遍历
public void infixOrder() {
if (root != null) {
root.infixOrder();
} else {
System.out.println("该二叉树为空,无法遍历!");
}
}
//后序遍历
public void postOrder() {
if (root != null) {
root.postOrder();
} else {
System.out.println("该二叉树为空,无法遍历!");
}
}
//前序遍历查找
public TreeNode preOrderSearch(int num) {
if (root != null) {
return root.preOrderSearch(num);
} else {
return null;
}
}
//中序遍历查找
public TreeNode infixOrderSearch(int num) {
if (root != null) {
return root.infixOrderSearch(num);
} else {
return null;
}
}
//中序遍历查找
public TreeNode postOrderSearch(int num) {
if (root != null) {
return root.postOrderSearch(num);
} else {
return null;
}
}
//删除节点
public void delNode(int num) {
if (root != null) {
//如果要删除的节点刚好是root节点,就相当于要清空该二叉树
if (root.getNum() == num) {
root = null;
} else {
root.delNode(num);
}
}
}
}
最后就是测试代码,对以上的功能进行一个测试:
public class BinaryTreeTest {
public static void main(String[] args) {
//先创建二叉树
BinaryTree binaryTree = new BinaryTree();
//创建需要的节点
TreeNode root = new TreeNode(50);
TreeNode treeNode_2 = new TreeNode(25);
TreeNode treeNode_3 = new TreeNode(75);
TreeNode treeNode_4 = new TreeNode(15);
TreeNode treeNode_5 = new TreeNode(35);
TreeNode treeNode_6 = new TreeNode(65);
TreeNode treeNode_7 = new TreeNode(85);
//这里手动创建二叉树,后期再进行以递归的方式创建二叉树
root.setLeft(treeNode_2);
root.setRight(treeNode_3);
treeNode_2.setLeft(treeNode_4);
treeNode_2.setRight(treeNode_5);
treeNode_3.setLeft(treeNode_6);
treeNode_3.setRight(treeNode_7);
//将根节点加入二叉树
binaryTree.setRoot(root);
//测试前序遍历
System.out.println("前序遍历");
binaryTree.preOrder();
//测试中序遍历
System.out.println("中序遍历");
binaryTree.infixOrder();
//测试后序遍历
System.out.println("后序遍历");
binaryTree.postOrder();
//测试前序遍历查找,第四次找到该元素
System.out.println("测试前序遍历查找");
TreeNode resNode = binaryTree.preOrderSearch(35);
judge(resNode);
//测试中序遍历查找,第三次找到该元素
System.out.println("测试中序遍历查找");
resNode = binaryTree.infixOrderSearch(35);
judge(resNode);
//测试后序遍历查找,第二次找到该元素
System.out.println("测试后序遍历查找");
resNode = binaryTree.postOrderSearch(35);
judge(resNode);
//测试删除节点
binaryTree.delNode(25);
System.out.println("删除后");
binaryTree.preOrder();
}
private static void judge(TreeNode resNode) {
if (resNode != null) {
System.out.println("找到了 : " + resNode);
} else {
System.out.println("没有找到该元素!");
}
}
}