数据结构小白之二叉树的遍历和查找

1.存储方式的分析

1.1 数组储存方式的分析

优点: 通过下标的访问,速度快,还可以使用二分法提高检索的速度

缺点: 如果要检索某个值,或者插入值。整个数组就会进行移动,从而造成较低的效率。同时如果需要扩容的话,从底层而言,每一次数组的扩容就要重新进行创建一个新的数组(参考ArrayList,底层维护了一个object类型的数组,所以它的添加操作效率不是很高)

1.2 链表存储方式的分析

优点: 在一定程度上对于数组做出优化(可以参考数据结构小白之链表),在进行插入和删除的时候只需要移动一个节点即可

缺点: 在检索具体的值时,效率依然比较低,比如在检索具体的值,需要从头节点进行遍历

1.3 树存储方式的分析

可以提高数据存储和读取的效率,比如利用二叉排序树,既可以保证数据的检索速度,也可以保证数据的插入,删除和修改的速度

扫描二维码关注公众号,回复: 9322814 查看本文章

2.二叉树

2.1 二叉树的特征

* 每一个节点最多只能有两个子节点(左子节点和右子节点)

* 如果二叉树的所有叶子节点都在最后一层,节点总数为 2*n-1(n为层数),称为满二叉树

* 如果二叉树的深度为k,除了第k层外,其他(1 到 k-1层节点都达到了最大值,且第k层所有的节点都连续集中在最左边,这样的树就是完全二叉树)

2.2 二叉树的遍历(核心在于递归)

二叉树的遍历分为前序,中序和后序遍历

前序遍历: 按照 根节点->左子节点->右子节点的顺序进行遍历

中序遍历: 按照 左子节点->根节点->右子节点的顺序进行遍历

后序遍历: 按照 左子节点->右子节点->根节点的顺序进行遍历


举个栗子

(1) 先去准备pojo(相应的树节点)

package tree_op;

import org.omg.CORBA.NO_IMPLEMENT;

//创建树的节点
public class HeroNode {
    private int no;
    
    private String name;
    private HeroNode left; //默认为null
    private HeroNode right;//默认为null

    //建立构造器
    public HeroNode(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 HeroNode getLeft() {
        return left;
    }

    public void setLeft(HeroNode left) {
        this.left = left;
    }

    public HeroNode getRight() {
        return right;
    }

    public void setRight(HeroNode right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "HeroNode[" +
                "no=" + no +
                ", name='" + name + '\'' +
                ']';
    }

    

(2)开始进行前序遍历(根->左->右)

 //编写前序遍历的方法 根->左->右
    public void preOrder() {
        //输出父节点
        System.out.println(this);
        //递归向左
        if (this.left != null) {

            this.left.preOrder();
        }
        //递归向右子树遍历
        if (this.right != null) {
            this.right.preOrder();
        }
    }

(3)开始进行中序遍历(左->根->右)

 //编写中序遍历的方法 左->根->右
    public void infixOrder() {
        //递归向左
        if (this.left != null) {
            this.left.infixOrder();
        }
        //输出父节点
        System.out.println(this);
        //递归向右
        if (this.right != null) {
            this.right.infixOrder();
        }
    }

(4) 开始进行后序遍历(左->右->根)

 //编写后序遍历的方法 左->右->根
    public void postOrder() {
        //遍历向左
        if (this.left != null) {
            this.left.postOrder();
        }
        //遍历向右
        if (this.right != null) {
            this.right.postOrder();
        }
        //中
        System.out.println(this);
    }

注: 二叉树的三种遍历操作逻辑上而言非常简单,就是按照遍历顺序给出相应的代码即可,代码的重复度也很高(如果愿意的话可以把对左子树 根 右子树的操作抽离出来,然后在这使用的就是三个方法的排列...)

//对于左节点的操作(1)
     if(this.left != null) {
          this.left.postOrder();
        }

//对于右节点的操作(2)
 
     if(this.right != null) {
         this.right.postOrder();
     }

//对于根的操作(3)
System.out.println(this);



前序遍历 3-1-2

中序遍历 1-3-2

后序遍历 1-2-3

2.3 二叉树的查找(核心在于递归)

(1) 开始进行前序查找

  //编写前序查找方法
    //比较->左递归->右递归
    //如果找到了就返回该node,如果没有找到返回null
    public HeroNode preOrderSearch(int no) {
        HeroNode resNode = null;
        //比较当前节点是不是
        if (this.no == no) {
            return this;
        }
        //判断当前节点的左节点是否为空,如果不为空则进行左递归

        if (this.left != null) {
            resNode = this.left.preOrderSearch(no);
        }
        if (resNode != null) {
            return resNode;
        }
        //如果resNode为空,则判断右子节点情况
        if (this.right != null) {
            resNode = this.right.preOrderSearch(no);
        }
        return resNode;
    }

(2) 开始进行中序查找

//编写中序查找算法
    //左递归->比较->右递归
    public HeroNode infixOrderSearch(int no) {
        HeroNode resNode = null;
        //开始进左递归查找
        if (this.left != null) {
            resNode = this.left.infixOrderSearch(no);
        }
        if (resNode != null) {
            return resNode;
        }
        //如果找到,则返回,如果没有找到,就和当前节点比较
        if (this.no == no) {
            return this;
        }
        //否则进行继续向右进行中序查找
        if (this.right != null) {
            resNode = this.right.infixOrderSearch(no);
        }
        return resNode;
    }

(3)开始进行后序查找

 //后序遍历查找
    //左递归->右递归->中
    public HeroNode postOrderSearch(int no){
        //左递归
        HeroNode 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;
        }
        //中
        if(this.no==no){
            return this;
        }
        return resNode;
    }

注: 二叉树的查找算法也是几个小方法的重复使用,依旧按照前序,中序,后序的不同,控制方法的调用时机

        //根节点操作 1

        if (this.no == no) {
            return this;
        }

        
        //左子节点操作 2
        
         if (this.left != null) {
            resNode = this.left.preOrderSearch(no);
        }
       

        //右子节点操作 3
        
         //如果resNode为空,则判断右子节点情况
        if (this.right != null) {
            resNode = this.right.preOrderSearch(no);
        }
       
        
        //判断resNode是否为null 4
         if (resNode != null) {
            return resNode;
        }
        
        //直接返回resNode (用于最后一轮判断) 5
        return resNode;

则 对于查找操作而言

1.前序查找: 1 2 4 3 5

2.中序查找: 2 4 1 3 5

3.后序查找  2 4 3 1 5

注: 这里仅仅给出了关于遍历和查找的核心代码,欢迎大家来我的github取完整代码:

https://github.com/Lzin/tree_structure/tree/master/src/tree_op

测试结果:

D:\lock\java\jdk\bin\java.exe -javaagent:D:\JetBrains\apps\IDEA-U\ch-0\183.5429.30\lib\idea_rt.jar=4988:D:\JetBrains\apps\IDEA-U\ch-0\183.5429.30\bin -Dfile.encoding=UTF-8 -classpath D:\lock\java\jdk\jre\lib\charsets.jar;D:\lock\java\jdk\jre\lib\deploy.jar;D:\lock\java\jdk\jre\lib\ext\access-bridge-64.jar;D:\lock\java\jdk\jre\lib\ext\cldrdata.jar;D:\lock\java\jdk\jre\lib\ext\dnsns.jar;D:\lock\java\jdk\jre\lib\ext\jaccess.jar;D:\lock\java\jdk\jre\lib\ext\jfxrt.jar;D:\lock\java\jdk\jre\lib\ext\localedata.jar;D:\lock\java\jdk\jre\lib\ext\nashorn.jar;D:\lock\java\jdk\jre\lib\ext\sunec.jar;D:\lock\java\jdk\jre\lib\ext\sunjce_provider.jar;D:\lock\java\jdk\jre\lib\ext\sunmscapi.jar;D:\lock\java\jdk\jre\lib\ext\sunpkcs11.jar;D:\lock\java\jdk\jre\lib\ext\zipfs.jar;D:\lock\java\jdk\jre\lib\javaws.jar;D:\lock\java\jdk\jre\lib\jce.jar;D:\lock\java\jdk\jre\lib\jfr.jar;D:\lock\java\jdk\jre\lib\jfxswt.jar;D:\lock\java\jdk\jre\lib\jsse.jar;D:\lock\java\jdk\jre\lib\management-agent.jar;D:\lock\java\jdk\jre\lib\plugin.jar;D:\lock\java\jdk\jre\lib\resources.jar;D:\lock\java\jdk\jre\lib\rt.jar;D:\java_algorithm\structure_code\tree_structure\out\production\tree_structure tree_op.BinaryTreeDemo
前序遍历:
HeroNode[no=1, name='p1']
HeroNode[no=2, name='p2']
HeroNode[no=3, name='p3']
HeroNode[no=5, name='p5']
HeroNode[no=4, name='p4']
========================
中序遍历:
HeroNode[no=2, name='p2']
HeroNode[no=1, name='p1']
HeroNode[no=5, name='p5']
HeroNode[no=3, name='p3']
HeroNode[no=4, name='p4']
========================
后序遍历:
HeroNode[no=2, name='p2']
HeroNode[no=5, name='p5']
HeroNode[no=4, name='p4']
HeroNode[no=3, name='p3']
HeroNode[no=1, name='p1']
========================
前序查找
查询到该节点的编号为5 该节点的名称为p5
========================
中序查找
查询到该节点的编号为4 该节点的名称为p4
========================
后序查找
未找到该结点

Process finished with exit code 0


 

发布了193 篇原创文章 · 获赞 70 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/Lzinner/article/details/103141891