X8-1、java数据结构---二叉树【2020-12-31】

总目录:地址如下看总纲

https://www.jianshu.com/p/929ca9e209e8

1、树的常用术语

image.png

1、节点:就是节点,没什么鸡毛好讲
2、根节点:就是最上层,祖宗
3、父节点:
4、子节点:
5、叶子节点:没有子节点的节点
6、节点的权:就是节点值(大多时候是个对象)
7、路径:从root节点找到该节点的路线
8、层
9、子树
10、树的高度:最大层数
11、森林 :多颗子树构成森林

2、二叉树的概念

  1. 树有很多种,每个节点最多只能有两个子节点的一种形式称为二叉树。
  2. 二叉树的子节点分为左节点和右节点。
  3. 以下三种均为二叉树:


    image.png
  4. 如果该二叉树的所有叶子节点都在最后一层,并且结点总数= 2^n -1 , n 为层数,则我们称为满二叉树


    image.png
  5. 如果该二叉树的所有叶子节点都在最后一层或者倒数第二层,而且最后一层的叶子节点在左边连续,倒数第二层的叶子节点在右边连续,我们称为完全二叉树。


    image.png

3、二叉树的遍历说明

  1. 前序遍历: 先输出父节点,再遍历左子树和右子树
  2. 中序遍历: 先遍历左子树,再输出父节点,再遍历右子树
  3. 后序遍历: 先遍历左子树,再遍历右子树,最后输出父节点
  • 小结: 看输出父节点的顺序,就确定是前序,中序还是后序

4、前,中,后序遍历详解

  1. 创建一颗二叉树
  2. 前序遍历
    2.1 先输出当前节点(初始的时候是root节点)
    2.2 如果左子节点不为空,则递归继续前序遍历
    2.3 如果右子节点不为空,则递归继续前序遍历
  3. 中序遍历
    3.1 如果当前节点的左子节点不为空,则递归中序遍历
    3.2 输出当前节点
    3.3 如果当前节点的右子节点不为空,则递归中序遍历
  4. 后序遍历
    4.1 如果当前节点的左子节点不为空,则递归后序遍历
    4.2 如果当前节点的右子节点不为空,则递归后序遍历
    4.3 输出当前节点


    image.png

5、前,中,后序代码实现

package com.kk.datastructure.tree;

/**
 * title: 二叉树的前,中,后序遍历
 * @author 阿K
 * 2020年12月29日 下午11:29:56 
 */
public class BinaryTreeDemo {

    public static void main(String[] args) {

        // 创建二叉树
        BinaryTree binaryTree = new BinaryTree();

        // 创建需要的结点
        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.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);
        binaryTree.setRoot(root);
        
        System.out.println("前序遍历,预计结果:1,2,3,5,4");
        binaryTree.perOrder();

        System.out.println("中序遍历,预计结果:2,1,5,3,4");
        binaryTree.infixOrder();

        System.out.println("后序遍历,预计结果:2,5,4,3,1");
        binaryTree.postOrder();
    }
}

// 定义二叉树 
class BinaryTree {

    private HeroNode root;// 根节点

    public void setRoot(HeroNode root) {
        this.root = root;
    }

    // 前序遍历
    public void perOrder() {
        if (this.root != null) {
            this.root.perOrder();
        } else {
            System.out.println("二叉树为空,无法遍历~");
        }
    }

    // 中序遍历
    public void infixOrder() {
        if (this.root != null) {
            this.root.infixOrder();
        } else {
            System.out.println("二叉树为空,无法遍历~");
        }
    }

    // 后序遍历
    public void postOrder() {
        if (this.root != null) {
            this.root.postOrder();
        } else {
            System.out.println("二叉树为空,无法遍历~");
        }
    }
}

// 定义节点
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 void perOrder() {
        // 1.先输出入父节点
        System.out.println(this);
        // 2.递归左子树前序遍历
        if (this.left != null) {
            this.left.perOrder();
        }
        // 3.递归右子树前序遍历
        if (this.right != null) {
            this.right.perOrder();
        }
    }

    // 节点的中序遍历
    public void infixOrder() {
        // 1.递归左子树中序遍历
        if (this.left != null) {
            this.left.infixOrder();
        }
        // 2.输出父节点
        System.out.println(this);
        // 3.递归右子树中序遍历
        if (this.right != null) {
            this.right.infixOrder();
        }
    }

    // 节点的后序遍历
    public void postOrder() {
        // 1.递归左子树后序遍历
        if (this.right != null) {
            this.left.postOrder();
        }
        // 2.递归右子树后序遍历
        if (this.right != null) {
            this.right.postOrder();
        }
        // 3.输出父节点
        System.out.println(this);
    }
    
    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 + "]";
    }
}

6、前,中,后序查找思路

  1. 前序查找思路:
    1、先判断当前结点的 no 是否等于要查找的
    2、如果是相等,则返回当前结点
    3、如果不等,则判断当前结点的左子节点是否为空,如果不为空,则递归前序查找4、如果左递归前序查找,找到结点,则返回,否继续判断,当前的结点的右子节点是否为空,如果不空,则继续向右递归前序查找.
  2. 中序查找思路:
    1、判断当前结点的左子节点是否为空,如果不为空,则递归中序查找
    2、如果找到,则返回,如果没有找到,就和当前结点比较,如果是则返回当前结点,否则继续进行右递归的中序查找
    3、如果右递归中序查找,找到就返回,否则返回 null
  3. 后序查找思路
    1、判断当前结点的左子节点是否为空,如果不为空,则递归后序查找
    2、如果找到,就返回,如果没有找到,就判断当前结点的右子节点是否为空,如果不为空,则右递归进行后序查找,如果找到,就返回
    3、最后和当前结点进行比较,是则返回,否则返回 null


    image.png

7、前,中,后序查找代码

package com.kk.datastructure.tree;


/**
 * title: 前中后序查找
 * @author 阿K
 * 2020年12月30日 下午11:49:44 
 */
public class BinaryTreeDemo2 {

    public static void main(String[] args) {

        // 创建二叉树
        BinaryTree binaryTree = new BinaryTree();

        // 创建需要的结点
        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.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);
        binaryTree.setRoot(root);

        System.out.println("前序遍历,预计统计次数:4 ===> 查找顺序 1,2,3,5,4");
        binaryTree.perOrderSearch(5);

        System.out.println("中序遍历,预计统计次数:3 ===》 查找顺序 2,1,5,3,4");
        binaryTree.infixOrderSearch(5);
//
        System.out.println("后序遍历,预计统计次数:2 ===》 查找顺序 2,5, 4,3,1");
        binaryTree.postOrderSearch(5);
    }
}

// 定义二叉树 
class BinaryTree {

    private HeroNode root;// 根节点

    public void setRoot(HeroNode root) {
        this.root = root;
    }

    // 前序遍历查找
    public HeroNode perOrderSearch(int no) {
        if (root != null) {
            return root.perOrderSearch(no);
        } else {
            return null;
        }
    }

    // 中序遍历查找
    public HeroNode infixOrderSearch(int no) {
        if (root != null) {
            return root.infixOrderSearch(no);
        } else {
            return null;
        }
    }

    // 后序遍历查找
    public HeroNode postOrderSearch(int no) {
        if (root != null) {
            return root.postOrderSearch(no);
        } else {
            return null;
        }
    }
}

// 定义节点
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;
    }

    /**
     * 须知:关于查找次数的统计
     * 为了有效的统计出正确的次数,应该放在 if(this.no == no) 上面
     * @param no
     * @return
     */

    // 节点的前序遍历查找
    public HeroNode perOrderSearch(int no) {
        System.out.println("记录前序遍历查找,打印次数决定查找运行次数!");
        // 比较当前节点是否
        if (this.no == no) {
            return this;
        }
        // 1、判断当前节点的左子节点是否为空,若不为空,则左递归前序查找
        // 2、若左递归前序查找,找到节点则返回
        HeroNode resNode = null;
        if (this.left != null) {
            resNode = this.left.perOrderSearch(no);
        }
        if (resNode != null) {// 说明在左子树上找到了
            return resNode;
        }
        // 1、当前节点的右子树是否为空,若不为空,则右递归前序查找
        // 2、若右递归前序查找,找到节点则返回
        if (this.right != null) {
            resNode = this.right.perOrderSearch(no);
        }
        return resNode;
    }

    // 节点的中序遍历查找
    public HeroNode infixOrderSearch(int no) {
        // 1、判断当前左节点是否为空,若不为空,则继续左递归中序遍历查找
        HeroNode resNode = null;
        if (this.left != null) {
            resNode = this.left.infixOrderSearch(no);
        }
        // 2、若不为空,则返回(既找到)
        if (resNode != null) {
            return resNode;
        }
        System.out.println("进入中序查找的次数统计");
        // 3、和当前节点进行比较,若是则返回
        if (this.no == no) {
            return this;
        }
        // 4、若不是,则继续右递归中序遍历查找
        if (this.right != null) {
            resNode = this.right.infixOrderSearch(no);
        }
        return resNode;
    }

    // 节点的后序遍历查找
    public HeroNode postOrderSearch(int no) {
        // 1、判断当前左节点是否为空,若不为空,则继续左递归后序遍历查找
        HeroNode resNode =null;
        if(this.left!=null) {
            resNode = this.left.postOrderSearch(no);
        }
        // 2、若不为空,则返回
        if(resNode !=null) {
            return resNode;
        }
        // 3、若当前左子树没有找到,则开始右子树后序递归遍历查找
        if(this.right!=null) {
            resNode = this.right.postOrderSearch(no);
        }
        // 4、若不为空,则返回
        if(resNode!=null) {
            return resNode;
        }
        System.out.println("进入后序号查找次数统计");
        // 5、若左右子树都没有找到,就比较当前节点是否
        if(this.no==no) {
            return this;
        }
        return resNode;
    }

    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 + "]";
    }
}

8、删除树节点思路分析(入门版)

  1. 规定
    1、若删除的节点是叶子节点,则删除该节点
    2、若删除的节点是非叶子节点,则删除该子树
  2. 思路:
    1、判空,若只有一个 root 根节点存在,则等价于二叉树置空
    2、故当前入门级二叉树是单向的,所以我们是只判断当前节点的子节点是否为需要删除的
    3、若当前节点的左子节点不为空,并且左子节点就是要删除的节点,就将 this.left =null(既删除),并返回(return 结束递归)
    4、若当前节点的右子节点不为空,并且右子节点就是要删除的节点,就将 this.rigth=null (既删除),并返回(return 结束递归)
    5、若 以上两步都没有删除节点,那么就进行左子树递归删除
    6、如若以上三步都没有删除,则进行右子树递归删除

9、删除树节点代码(入门版)

package com.kk.datastructure.tree;


/**
 * title: 删除节点
 * @author 阿K
 * 2020年12月31日 下午11:57:45 
 */
public class BinaryTreeDemo3 {

    public static void main(String[] args) {

        // 创建二叉树
        BinaryTree binaryTree = new BinaryTree();

        // 创建需要的结点
        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.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);
        binaryTree.setRoot(root);
        
        // 删除测试。。
        System.out.println("删除前:");
        binaryTree.perOrder();
        binaryTree.delNode(3);
        System.out.println("删除后:");
        binaryTree.perOrder();

    }
}

// 定义二叉树 
class BinaryTree {

    // 前序遍历
    public void perOrder() {
        if (this.root != null) {
            this.root.perOrder();
        } else {
            System.out.println("二叉树为空,无法遍历~");
        }
    }

    private HeroNode root;// 根节点

    public void setRoot(HeroNode root) {
        this.root = root;
    }

    // 删除节点
    public void delNode(int no) {
        if (this.root != null) {
            // 1、判空,若只有一个 root 根节点存在,则等价于二叉树置空
            if (root.getNo() == no) {
                root = null;
            } else {
                // 否则 递归删除
                root.delNode(no);
            }

        } else {
            System.out.println("当前二叉树为空,删个鸡毛??");
        }
    }

}

// 定义节点
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;
    }

    // 节点删除
    // 1、若删除的节点是叶子节点,则删除该节点
    // 2、若删除的节点是非叶子节点,则删除该子树
    public void delNode(int no) {

//      2、故当前入门级二叉树是单向的,所以我们是只判断当前节点的子节点是否为需要删除的
//      3、若当前节点的左子节点不为空,并且左子节点就是要删除的节点,就将 this.left =null(既删除),并返回(return 结束递归)
//      4、若当前节点的右子节点不为空,并且右子节点就是要删除的节点,就将 this.rigth=null (既删除),并返回(return 结束递归)
//      5、若 以上两步都没有删除节点,那么就进行左子树递归删除
//      6、如若以上三步都没有删除,则进行右子树递归删除
        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 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;
    }

    // 节点的前序遍历
    public void perOrder() {
        // 1.先输出入父节点
        System.out.println(this);
        // 2.递归左子树前序遍历
        if (this.left != null) {
            this.left.perOrder();
        }
        // 3.递归右子树前序遍历
        if (this.right != null) {
            this.right.perOrder();
        }
    }

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容