一、为什么需要树这种数据结构


数组存储方式的分析
        优点:通过下标方式访问元素,速度快。 对于有序数组 ,还可使用二分查找提高检索速度。
        缺点:如果要检索具体某个值,或者插入值( 按一定顺序 ) 会整体移动 ,效率较低



2 ) 链式存储方式的分析
        优点:在一定程度上对数组存储方式有优化( 比如:插入一个数值节点,只需要将插入节点,链接到链表中即可, 删除效率也很好 ) 。
        缺点:在进行检索时,效率仍然较低,比如( 检索某个值,需要从头节点开始遍历)         


        树存储方式的分析
                能提高数据存储,读取的效率,  比如利用 二叉排序树(Binary Sort Tree),既可以保证数据的检索速度,同时也可以保证数据的插入,删除,修改的速度。

java二叉链表存储二叉树 java二叉树有什么作用_java二叉链表存储二叉树

二、二叉树的概念


树有很多种,每个节点 最多只能有两个子节点 的一种形式称为二叉树。


二叉树的子节点分为左节点和右节点。


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


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


 

java二叉链表存储二叉树 java二叉树有什么作用_数据结构_02

 

java二叉链表存储二叉树 java二叉树有什么作用_二叉树_03

 三、二叉树的定义

1.先定义节点:

class Node{
    private int no;
    private String name;
    private Node left;
    private Node right;

    public Node(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 Node getLeft() {
        return left;
    }

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

    public Node getRight() {
        return right;
    }

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

 2.再定义二叉树

lass BinaryTree{
    private Node root;

    public Node getRoot() {
        return root;
    }

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

    public BinaryTree(Node root) {
        this.root = root;
    }

四、二叉树的遍历

        前序遍历: 先输出父节点,再遍历左子树和右子树

        中序遍历: 先遍历左子树,再输出父节点,再遍历右子树

        后序遍历: 先遍历左子树,再遍历右子树,最后输出父节点

        小结: 看输出父节点的顺序,就确定是前序,中序还是后序:

java二叉链表存储二叉树 java二叉树有什么作用_数据结构_04

1.Node类中

//前序遍历
    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 afterOrder(){
        if(this.left!=null){//左
            this.left.afterOrder();
        }
        if(this.right!=null){//右
            this.right.afterOrder();
        }
        System.out.println(this);//根
    }

2.BinaryTree类中

//前序遍历
    public void preOrder(){
        if(this.root!=null){
            this.root.preOrder();
        }else System.out.println("二叉树为空!");
    }
    //中序遍历
    public void infixOrder(){
        if(this.root!=null){
            this.root.infixOrder();
        }else System.out.println("二叉树为空!");
    }
    //后序遍历
    public void afterOrder(){
        if(this.root!=null){
            this.root.afterOrder();
        }else System.out.println("二叉树为空!");
    }

 3.测试

@Test
    public void testOrder(){
        Node root=new Node(1,"a");
        Node node1=new Node(2,"b");
        Node node2=new Node(3,"c");
        Node node3=new Node(4,"d");
        BinaryTree tree=new BinaryTree(root);
        root.setLeft(node1);
        root.setRight(node2);
        node2.setRight(node3);
        tree.preOrder();//1,2,3,4
        tree.infixOrder();//2,1,3,4
        tree.afterOrder();//2,4,3,1


Node{no=1, name='a'}
Node{no=2, name='b'}
Node{no=3, name='c'}
Node{no=4, name='d'}

Node{no=2, name='b'}
Node{no=1, name='a'}
Node{no=3, name='c'}
Node{no=4, name='d'}

Node{no=2, name='b'}
Node{no=4, name='d'}
Node{no=3, name='c'}
Node{no=1, name='a'}

五、二叉树查找节点

类似于遍历

1.Node类中

//前序查找
    public Node preOrderSearch(int no){
        //先遍历根节点,如果找到就返回
        if(this.no==no) return this;
        Node temp=null;
        //没找到就看是否有左节点,有的话就对左子节点做前序遍历
        if(this.left!=null){
            temp=this.left.preOrderSearch(no);
        }
        //在左子节点找到了目标,则返回
        if(temp!=null) return temp;
        //没找到就看是否有右节点,有的话就对右子节点做前序遍历
        if(this.right!=null){
            temp=this.right.preOrderSearch(no);
        }
        //遍历完右子节点,不管是否为空都返回
        return temp;
    }
    //中序查找
    public Node infixOrderSearch(int no){
        Node temp=null;
        //先遍历左节点
        if(this.left!=null){
            temp=this.left.infixOrderSearch(no);
        }
        if(temp!=null) return temp;
        //再遍历根节点
        if(this.no==no) return this;
        //最后遍历右节点
        if(this.right!=null){
            temp=this.right.infixOrderSearch(no);
        }
        return temp;
    }
    //后序查找
    public Node afterOrderSearch(int no){
        Node temp=null;
        //先遍历左节点
        if(this.left!=null){
            temp=this.left.afterOrderSearch(no);
        }
        if(temp!=null) return temp;
        //再遍历右节点
        if(this.right!=null){
            temp=this.right.afterOrderSearch(no);
        }
        if(temp!=null) return temp;
        //最后遍历根节点
        if(this.no==no) return this;
        return temp;
    }

2.BinaryTree类中

//前序遍历查找
    public Node preOrderSearch(int no){
        if(this.root!=null){
            return this.root.preOrderSearch(no);
        }else return null;
    }
    //中序遍历查找
    public Node infixOrderSearch(int no){
        if(this.root!=null){
            return this.root.infixOrderSearch(no);
        }else return null;
    }
    //后序遍历查找
    public Node afterOrderSearch(int no){
        if(this.root!=null){
            return this.root.afterOrderSearch(no);
        }else return null;
    }

3.测试

@Test
    public void testSearch(){
        Node root=new Node(1,"a");
        Node node1=new Node(2,"b");
        Node node2=new Node(3,"c");
        Node node3=new Node(4,"d");
        BinaryTree tree=new BinaryTree(root);
        root.setLeft(node1);
        root.setRight(node2);
        node2.setRight(node3);
        System.out.println("前序查找:"+tree.preOrderSearch(5));
        System.out.println("中序查找:"+tree.preOrderSearch(4));
        System.out.println("后序查找:"+tree.preOrderSearch(4));
    }


前序查找:null
中序查找:Node{no=4, name='d'}
后序查找:Node{no=4, name='d'}

六、二叉树删除节点

1).删除的是子节点,那么就直接删除

2).删除的是中间节点,那就连带它的子树全部删除

1.Node类

//删除节点(包括其子节点)
    public Boolean delNode(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.delNode(no);
        }
        //如果还是没找到,就再遍历到该节点的右子节点继续判断
        if(this.right!=null){
            this.right.delNode(no);
        }
        return false;
    }

2.BinaryTree类

//删除节点(包括其子节点)
    public Boolean delNode(int no){
        //根节点不为空
        if(root!=null){
            //要删除根节点
            if(root.getNo()==no){
                root=null;
                return true;
                //要删除的不是根节点
            }else return root.delNode(no);
        }else {
            System.out.println("树为空,不能删除!");
            return false;
        }
    }

3.测试

@Test
    public void testDel(){
        Node root=new Node(1,"a");
        Node node1=new Node(2,"b");
        Node node2=new Node(3,"c");
        Node node3=new Node(4,"d");
        BinaryTree tree=new BinaryTree(root);
        root.setLeft(node1);
        root.setRight(node2);
        node2.setRight(node3);
        System.out.println("删除前:");
        tree.preOrder();
        tree.delNode(3);
        System.out.println("删除后:");
        tree.preOrder();
    }


删除前:
Node{no=1, name='a'}
Node{no=2, name='b'}
Node{no=3, name='c'}
Node{no=4, name='d'}
删除后:
Node{no=1, name='a'}
Node{no=2, name='b'}

总结

        二叉树的遍历、查找、初级删除操作都有清晰的步骤可循,都可以用递归以较少的代码量完成。但注意要判断节点是否为空,否则会有空指针异常的风险。