为什么要使用树的结构?
对于一个同类型的数据集合我们常常会使用数组或者链表的数据结构来存储数据,但无论是链表还是数组都有它的优缺点。
- 数组:
对于数组来说,它在定义的那一刻就要决定它的长度,但很多情况我们是不清楚要定义多长的数组的,因此就有了数组扩容的策略,像java的ArrayList就是,在存储的数据达到最大限度时,此时添加一个数据就会新建一个数组(长度按特定的策略来定,比如原数组的两倍)把原数组的数据(直接把全部数据拷贝到新数组)和新添的数据放入到新数组。
数组的优点就是检索方便(在连续的存储空间存放,只需根据下标即可快速检索某一个元素),但在特定的位置添加元素需要把部分数据移动才能添加(尾插还是方便的)和它需要固定长度。 - 链表
对于链表来说它不需要指定长度,它的节点可以在不连续的内存存放
,但是它检索某一个元素需要遍历整个链表,这个对比数组就显得很慢了。
链表的优点就是插入数据方便,只需要改变一下节点间的指针指向(即每个节点相连的那条链)即可。 - 树
树就解决了数组和链表存在的问题,它插入跟链表类似,只需要在特定的节点改变指针的指向即可。检索某一个存在的数据也不需要全部节点都遍历,尤其对于有序的二叉树,根节点的左右节点分别比它小和大,很快就能找到。
二叉树的前中后序遍历
(我认为无论前序、中序、后续的遍历都是相对于父节点来的)
- 前序遍历
1.1前序就先输出当前节点。
1.2再遍历它的左子树。
1.3最后遍历它的右子树。
对应图1.1的顺序:1->2->3->5->4
/**
* 前序遍历
* @param root 根节点
*/
public void preOrder(HeroNode root){
// 判断根节点是否为空
if (root==null){
System.out.println("二叉树为空!");
return;
}
//1.输出当前节点
System.out.println(root);
//2.遍历左子树
if (root.left!=null){
preOrder(root.left);
}
//3.遍历右子树
if (root.right!=null){
preOrder(root.right);
}
}
/**
* 前序查找
* @param root 根节点
* @param no 查找节点的序号
* @return 返回找到的节点
*/
public HeroNode preSearch(HeroNode root,int no){
if (root==null){
System.out.println("二叉树为空");
return null;
}
// 比较当前节点
if (root.no == no){
return root;
}
HeroNode result = null;
// 查找左子树
if (root.left!=null){
result = preSearch(root.left,no);
}
// 若在左子树里找到就结束
if (result != null){
return result;
}
// 查找右子树
if (root.right!=null){
result = preSearch(root.right,no);
}
return result;
}
- 中序遍历
2.1先遍历当前节点的左子树。
2.2再输出当前节点。
2.3最后遍历当前的右子树。
对应图1.1的顺序:2->1->5->3->4
// 中序遍历
public void infixOrder(HeroNode root){
// 判断根节点是否为空
if (root==null){
System.out.println("二叉树为空!");
return;
}
// 1.遍历左子树
if (root.left!=null){
infixOrder(root.left);
}
// 2.输出当前节点
System.out.println(root);
// 3.遍历右子树
if (root.right!=null){
infixOrder(root.right);
}
}
/**
* 中序查找
* @param root 根节点
* @param no 查找节点的序号
* @return 返回找到的节点
*/
public HeroNode infixSearch(HeroNode root,int no){
if (root==null){
System.out.println("二叉树为空");
return null;
}
HeroNode result = null;
// 查找左子树
if (root.left!=null){
result = infixSearch(root.left,no);
}
if (result != null){
return result;
}
System.out.println("中序查找");
// 比较当前节点
if (root.no == no){
return root;
}
// 查找右子树
if (root.right!=null){
result = infixSearch(root.right,no);
}
if (result!=null){
return result;
}
return null;
}
- 后序遍历
3.1先遍历当前节点的左子树。
3.2最后遍历当前的右子树。
3.3再输出当前节点。
对应图1.1的顺序:2->5->4->3->1
// 后续遍历
public void postOrder(HeroNode root){
if (root==null){
System.out.println("二叉树为空!");
return;
}
//1.遍历左子树
if (root.left!=null){
postOrder(root.left);
}
//2.遍历右子树
if (root.right!=null){
postOrder(root.right);
}
//3.输出当前节点
System.out.println(root);
}
/**
* 后序查找
* @param root 根节点
* @param no 查找节点的序号
* @return 返回找到的节点
*/
public HeroNode postSearch(HeroNode root,int no){
if (root==null){
System.out.println("二叉树为空");
return null;
}
HeroNode result = null;
// 查找左子树
if (root.left!=null){
result = postSearch(root.left,no);
}
if (result != null){
return result;
}
// 查找右子树
if (root.right!=null){
result = postSearch(root.right,no);
}
if (result!=null){
return result;
}
System.out.println("后序查找");
// 比较当前节点
if (root.no == no){
return root;
}
return null;
}
节点的删除:
这里直接删除对应节点及其左右子树
1.判断要删除的是否是根节点。
2.若不是根节点,判断当前节点的左右子节点是否是要删除的节点,是直接把当前节点的左子节点或者右子节点置空即可。
3.若当前节点的左右子节点都不是要删除的节点,递归遍历当前节点的左子树判断是否在左子树。
4.若要删除节点不在左子树,继续递归遍历右子树,判断是否在右子树
// 删除节点(可以看下面的全部代码)
public boolean del(int no){
// 判断是否删除的是根节点
if (root!=null){
if (root.no == no){
root = null;
return false;
}else {
// 遍历根节点的左右子树
return root.del(no);
}
}else {
return false;
}
}
}
public boolean del(int no){
// 删除的是否是当前节点的左子节点
if (this.left != null && this.left.no == no){
this.left = null;
return true;
}
// 删除的是否是当前节点的右子节点
if (this.right != null && this.right.no == no){
this.right = null;
return true;
}
// 向左子树查找目标删除
if (this.left!=null && this.left.del(no)){
return true;
}else return this.right != null && this.right.del(no);
// 向右子树查找目标删除
}
全部代码:
二叉树:
class BinaryTree{
public HeroNode root;
// 前序遍历
public void preOrder(HeroNode root){
if (root==null){
System.out.println("二叉树为空!");
return;
}
System.out.println(root);
if (root.left!=null){
preOrder(root.left);
}
if (root.right!=null){
preOrder(root.right);
}
}
// 前序查找
public HeroNode preSearch(HeroNode root,int no){
if (root==null){
System.out.println("二叉树为空");
return null;
}
System.out.println("前序查找");
// 比较当前节点
if (root.no == no){
return root;
}
HeroNode result = null;
// 查找左子树
if (root.left!=null){
result = preSearch(root.left,no);
}
if (result != null){
return result;
}
// 查找右子树
if (root.right!=null){
result = preSearch(root.right,no);
}
return result;
}
// 中序遍历
public void infixOrder(HeroNode root){
if (root==null){
System.out.println("二叉树为空!");
return;
}
if (root.left!=null){
infixOrder(root.left);
}
System.out.println(root);
if (root.right!=null){
infixOrder(root.right);
}
}
// 中序查找
public HeroNode infixSearch(HeroNode root,int no){
if (root==null){
System.out.println("二叉树为空");
return null;
}
HeroNode result = null;
// 查找左子树
if (root.left!=null){
result = infixSearch(root.left,no);
}
if (result != null){
return result;
}
System.out.println("中序查找");
// 比较当前节点
if (root.no == no){
return root;
}
// 查找右子树
if (root.right!=null){
result = infixSearch(root.right,no);
}
if (result!=null){
return result;
}
return null;
}
// 后续遍历
public void postOrder(HeroNode root){
if (root==null){
System.out.println("二叉树为空!");
return;
}
if (root.left!=null){
postOrder(root.left);
}
if (root.right!=null){
postOrder(root.right);
}
System.out.println(root);
}
// 后序查找
public HeroNode postSearch(HeroNode root,int no){
if (root==null){
System.out.println("二叉树为空");
return null;
}
HeroNode result = null;
// 查找左子树
if (root.left!=null){
result = postSearch(root.left,no);
}
if (result != null){
return result;
}
// 查找右子树
if (root.right!=null){
result = postSearch(root.right,no);
}
if (result!=null){
return result;
}
System.out.println("后序查找");
// 比较当前节点
if (root.no == no){
return root;
}
return null;
}
// 删除节点
public boolean del(int no){
// 判断是否是删除根节点
if (root!=null){
if (root.no == no){
root = null;
return false;
}else {
return root.del(no);
}
}else {
return false;
}
}
}
树节点基本的结构
class HeroNode {
public int no;
public String name;
public HeroNode left;
public HeroNode right;
public HeroNode(int no, String name) {
this.no = no;
this.name = name;
}
// 删除节点
public boolean del(int no){
// 删除的是否是左子节点
if (this.left != null && this.left.no == no){
this.left = null;
return true;
}
// 删除的是否是右子节点
if (this.right != null && this.right.no == no){
this.right = null;
return true;
}
// 向左子树查找目标删除
if (this.left!=null && this.left.del(no)){
return true;
}else return this.right != null && this.right.del(no);
// 向右子树查找目标删除
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
'\'' +
'}';
}
}
运行程序:
// 构建二叉树
HeroNode root = new HeroNode(1,"宋江");
HeroNode node2 = new HeroNode(2,"吴用");
HeroNode node3 = new HeroNode(3,"卢俊义");
HeroNode node4 = new HeroNode(4,"林冲");
HeroNode node5 = new HeroNode(5,"关胜");
root.left = node2;
root.right = node3;
node3.right = node4;
node3.left = node5;
BinaryTree binaryTree = new BinaryTree();
binaryTree.root = root;
System.out.println("前序遍历:");
binaryTree.preOrder(binaryTree.root);
System.out.println("中序遍历:");
binaryTree.infixOrder(binaryTree.root);
System.out.println("后序遍历:");
binaryTree.postOrder(binaryTree.root);
// 查找
System.out.println("查找序号为 5 的节点:"+binaryTree.preSearch(root, 5));
System.out.println("查找序号为 100 的节点:"+binaryTree.infixSearch(root, 100));
System.out.println("查找序号为 5 的节点:"+binaryTree.postSearch(root, 5));
// 删除节点
System.out.println("del前:");
binaryTree.preOrder(binaryTree.root);
System.out.println(binaryTree.del(5)?"成功删除":"删除失败,没有该节点!");
System.out.println("del后:");
binaryTree.preOrder(binaryTree.root);
结果:
仅仅是个人理解,有不对的地方欢迎大佬指出。