二叉树
树结构出现的原因:
二叉树示意图:
常用用语:
叶子节点:没有子节点的节点。
节点的权:节点值。
路径:从根节点找到该节点的一条路。
树的高度:也就是最大层数。
二叉树概念:每个节点最多只能有两个子节点称为二叉树。
二叉树的节点分为左子节点和右子节点。
三种遍历方式
前序遍历:先输出父节点,然后遍历左子树右子树。即先输出该节点,如果左子节点不为空,递归遍历左子节点,如果右子节点不为空,递归遍历右子节点。
中序遍历:先遍历左子树,然后输出父节点,然后遍历右子树。即如果左子节点不为空,递归遍历左子节点。输出该节点。如果右子节点不为空,递归遍历右子节点。
后序遍历:先遍历左子树右子树,最后输出父节点。即如果左子节点不为空,递归遍历左子节点,如果右子节点不为空,递归遍历右子节点。最后输出该节点。
区分三种遍历方式:看何时输出根节点,如果一开始就输出根节点,为前序遍历;如果最后输出根节点,为后序遍历;如果在中间输出根节点,为中序遍历。
代码示例
//二叉树
class BinaryTree{
public Hero root;
//前序遍历
public void preOrder(){
root.preOrder();
}
//中序遍历
public void midOrder(){
root.midOrder();
}
//后序遍历
public void postOrder(){
root.postOrder();
}
}
//单个英雄节点
class Hero{
public int no;
public String name;
public Hero left;
public Hero right;
//前序遍历
public void preOrder(){
System.out.println(this);
if(this.left!=null){
this.left.preOrder();
}
if(this.right!=null){
this.right.preOrder();
}
}
//中序遍历
public void midOrder(){
if(this.left!=null){
this.left.midOrder();
}
System.out.println(this);
if(this.right!=null){
this.right.midOrder();
}
}
//后序遍历
public void postOrder(){
if(this.left!=null){
this.left.postOrder();
}
if(this.right!=null){
this.right.postOrder();
}
System.out.println(this);
}
public Hero(int no, String name) {
this.no = no;
this.name = name;
}
@Override
public String toString() {
return "Hero{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
三种查找方式
前序查找:先看本节点是不是,是就直接返回,不是的话考虑是否有左子节点,有的话对左子节点递归查询,查到就直接返回,没查到时考虑是否有右子节点,有的话对右子节点递归查询,最后返回结果。
中序查找:考虑是否有左子节点,有的话对左子节点递归查询,查到就直接返回,没查到时比较本结点是不是,是就直接返回,不是的话考虑是否有右子节点,有的话对右子节点递归查询,最后返回结果。
后续查找:考虑是否有左子节点,有的话对左子节点递归查询,查到就直接返回,没查到时考虑是否有右子节点,有的话对右子节点递归查询,查到就直接返回,没查到比较本节点,最后返回结果。
代码
节点方法
//前序查找
public Hero preOrderSearch(int no){
//先找自己
System.out.println("前序···");
if(this.no==no){
return this;
}
Hero resNode=null;
//没找到就找左子树
if(this.left!=null){
resNode = this.left.preOrderSearch(no);
}
//左子树找到了就不用继续
if(resNode!=null){
return resNode;
}
//没找到就找右子树
if(this.right!=null){
resNode=this.right.preOrderSearch(no);
}
//不管找没找到都返回
return resNode;
}
//中序查找
public Hero midOrderSearch(int no){
Hero resNode=null;
if(this.left!=null){
resNode = this.left.midOrderSearch(no);
}
if(resNode!=null){
return resNode;
}
System.out.println("中序···");
if(this.no==no){
return this;
}
if(this.right!=null){
resNode=this.right.midOrderSearch(no);
}
return resNode;
}
//后序查找
public Hero postOrderSearch(int no){
Hero resNode=null;
if(this.left!=null){
resNode = this.left.postOrderSearch(no);
}
if(resNode!=null){
return resNode;
}
if(this.right!=null){
resNode=this.right.postOrderSearch(no);
}
if(resNode!=null){
return resNode;
}
System.out.println("后序···");
if(this.no==no){
return this;
}
return resNode;
}
二叉树调用
//前序查找
public Hero preOrderSearch(int no){
if(root!=null){
Hero hero = root.preOrderSearch(no);
return hero;
}else {
return null;
}
}
//中序查找
public Hero midOrderSearch(int no){
if(root!=null){
Hero hero = root.midOrderSearch(no);
return hero;
}else {
return null;
}
}
//后序查找
public Hero postOrderSearch(int no){
if(root!=null){
Hero hero = root.postOrderSearch(no);
return hero;
}else {
return null;
}
}
删除节点的方法
删除的规则:如果是叶子节点,就直接删除该节点,如果不是叶子节点,就删除该子树。
思路:首先单独考虑空树和只有根节点的情况。其次,为了删除,我们应该定位到需删除节点的父节点,然后将父节点的左或右置空。所以默认判断的是根节点的下方节点。逻辑如下:如果左子节点不为空且就是要删除的,就把左子节点置空,返回;否则判断,如果右子节点不为空且是要删除的,把右子节点置空返回,否则,如果左子节点不为空,向左递归删除,然后向右递归删除。
存在的问题就是如果在左递归的过程中找到并删除了该节点,还会继续进行右递归,可以考虑用一个布尔值标记。
代码
节点代码
//删除节点
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);
}
}
树代码
//删除节点
public void delNode(int no){
if(root!=null){
if(root.no==no){
root=null;
return;
}else {
root.delNode(no);
}
}else {
System.out.println("空树");
}
}