树-从入学到入坟

1、树的概念

1.1、树的常用术语

image-20210321111622792

1.2、二叉树

image-20210321111822436

image-20210321112121534

2、树的遍历

image-20210321112333142

image-20210321112707161

2.1、前中后序遍历代码实现



public class BinnaryTree {
    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, "灵宠");

        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        binaryTree.setRoot(root);

        System.out.println("前序遍历");
        binaryTree.preOrder();

        System.out.println("中序遍历");
        binaryTree.infixOrder();

        System.out.println("后序遍历");
        binaryTree.suffixOrder();
    }
}

//定义二叉树
class BinaryTree{
    private HeroNode root;

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

    public void preOrder(){
        if (this.root!=null){
            root.preOrder();
        }else {
            System.out.println("当前二叉树为空,无法遍历");
        }
    }
    public void infixOrder(){
        if (this.root!=null){
            root.infixOrder();
        }else {
            System.out.println("当前二叉树为空,无法遍历");
        }
    }
    public void suffixOrder(){
        if (this.root!=null){
            root.suffixOrder();
        }else {
            System.out.println("当前二叉树为空,无法遍历");
        }
    }
}

//创建HeroNode节点
class HeroNode {
    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    private int no;
    private String 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;
    }

    private HeroNode left;
    private HeroNode right;

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }//当输出是对象的时候,这个时候toString作用就很大了

    //前序遍历
    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 suffixOrder(){

        if (this.left!=null){
            this.left.suffixOrder();
        }
        if (this.right!=null){
            this.right.suffixOrder();
        }
        System.out.println(this);//先输出父节点
    }
}


3、树的查找

3.1、思路分析

image-20210321120851949

image-20210321121836936

3.2、代码实现

package com.qingchengxiaoye.tree;

public class BinnaryTree {
    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, "灵宠");

        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        binaryTree.setRoot(root);

        System.out.println("前序遍历");
        binaryTree.preOrder();

        System.out.println("中序遍历");
        binaryTree.infixOrder();

        System.out.println("后序遍历");
        binaryTree.suffixOrder();

        System.out.println("前序查找");
        HeroNode resultNode = binaryTree.preOrderSearch(4);
        if (resultNode!=null){
            System.out.printf("找到了,信息为no=%d  name=%s",resultNode.getNo(),resultNode.getName());
        }else {
            System.out.printf("没有找到");
        }
        
    }
}

//定义二叉树
class BinaryTree {
    private HeroNode root;

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

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

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

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

    public HeroNode preOrderSearch(int no) {
            if (root!=null){
                HeroNode node = this.root.preOrderSearch(no);
                return node;
            }
            return null;
    }
    public HeroNode infixOrderSearch(int no) {
        if (root!=null){
            HeroNode node = this.root.infixOrderSearch(no);
            return node;
        }
        return null;
    }
    public HeroNode suffixOrderSearch(int no) {
        if (root!=null){
            HeroNode node = this.root.suffixOrderSearch(no);
            return node;
        }
        return null;
    }
}

//创建HeroNode节点
class HeroNode {
    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    private int no;
    private String 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;
    }

    private HeroNode left;
    private HeroNode right;

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }//当输出是对象的时候,这个时候toString作用就很大了

    //前序遍历
    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 suffixOrder() {

        if (this.left != null) {
            this.left.suffixOrder();
        }
        if (this.right != null) {
            this.right.suffixOrder();
        }
        System.out.println(this);//先输出父节点
    }

    //前序遍历查找
    public HeroNode preOrderSearch(int no) {
        if (this.no == no) {
            return this;
        }
        HeroNode 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 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;
    }

    public HeroNode suffixOrderSearch(int no) {
        HeroNode resNode = null;

        if (this.left != null) {
            resNode = this.left.suffixOrderSearch(no);
        }
        if (resNode != null) {
            return resNode;
        }

        if (this.right != null) {
            resNode = this.right.suffixOrderSearch(no);
        }
        if (resNode != null) {
            return resNode;
        }

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

        return resNode;
    }
}


4、删除节点

4.1、思路分析

image-20210321150108449

image-20210321150830047

4.2、代码实现

package com.qingchengxiaoye.tree;

public class BinnaryTree {
    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, "灵宠");

        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        binaryTree.setRoot(root);

        System.out.println("前序遍历");
        binaryTree.preOrder();

        System.out.println("中序遍历");
        binaryTree.infixOrder();

        System.out.println("后序遍历");
        binaryTree.suffixOrder();

        System.out.println("前序查找");
        HeroNode resultNode = binaryTree.preOrderSearch(4);
        if (resultNode != null) {
            System.out.printf("找到了,信息为no=%d  name=%s", resultNode.getNo(), resultNode.getName());
        } else {
            System.out.printf("没有找到");
        }

    }
}

//定义二叉树
class BinaryTree {
    private HeroNode root;

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

    //删除节点
    public void delNode(int no){
        if (root!=null){
            if (root.getNo()==no){
                root=null;
            }else {
                root.delNode(no);
            }
        }else {
            System.out.println("这是空树,无法进行删除");
        }
    }

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

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

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

    public HeroNode preOrderSearch(int no) {
        if (root != null) {
            HeroNode node = this.root.preOrderSearch(no);
            return node;
        }
        return null;
    }

    public HeroNode infixOrderSearch(int no) {
        if (root != null) {
            HeroNode node = this.root.infixOrderSearch(no);
            return node;
        }
        return null;
    }

    public HeroNode suffixOrderSearch(int no) {
        if (root != null) {
            HeroNode node = this.root.suffixOrderSearch(no);
            return node;
        }
        return null;
    }
}

//创建HeroNode节点
class HeroNode {
    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    private int no;
    private String 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;
    }

    private HeroNode left;
    private HeroNode right;

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }//当输出是对象的时候,这个时候toString作用就很大了

    //递归删除节点
    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 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 suffixOrder() {

        if (this.left != null) {
            this.left.suffixOrder();
        }
        if (this.right != null) {
            this.right.suffixOrder();
        }
        System.out.println(this);//先输出父节点
    }

    //前序遍历查找
    public HeroNode preOrderSearch(int no) {
        if (this.no == no) {
            return this;
        }
        HeroNode 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 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;
    }

    public HeroNode suffixOrderSearch(int no) {
        HeroNode resNode = null;

        if (this.left != null) {
            resNode = this.left.suffixOrderSearch(no);
        }
        if (resNode != null) {
            return resNode;
        }

        if (this.right != null) {
            resNode = this.right.suffixOrderSearch(no);
        }
        if (resNode != null) {
            return resNode;
        }

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

        return resNode;
    }
}


5、顺序存储二叉树

5.1、思路分析

image-20210321154605601

image-20210321154921399

5.2、代码实现

package com.qingchengxiaoye.tree;

public class ArrBinnaryTree {
    public static void main(String[] args) {
        int[] arr={1,2,3,4,5,6,7};
        ArrTree arrTree = new ArrTree(arr);
        arrTree.preOrder();
    }

}

class ArrTree{
    private int[] arr;

    public ArrTree(int[] arr) {
        this.arr = arr;
    }
    public void preOrder(){
        this.preOrder(0);
    }
    
    public void preOrder(int index){
        if (arr==null||arr.length==0){
            System.out.println("数组为空,不能按照二叉树进行遍历");
            return;
        }

        System.out.println(arr[index]);
        if ((index*2+1)<arr.length){
            preOrder(index*2+1);
        }
        if ((index*2+2)<arr.length){
            preOrder(index*2+2);
        }

    }
}

6、线索化二叉树

6.1、思路分析

image-20210321170622919

image-20210321170609019

image-20210321171539729

6.2、代码实现

package com.qingchengxiaoye.tree1;

public class ThreadedBinaryTreeDemo {
    public static void main(String[] args) {
        HeroNode root = new HeroNode(1, "1");
        HeroNode node2 = new HeroNode(3, "3");
        HeroNode node3 = new HeroNode(6, "6");
        HeroNode node4 = new HeroNode(8, "8");
        HeroNode node5 = new HeroNode(10, "10");
        HeroNode node6 = new HeroNode(14, "14");

        //先手动创建
        root.setLeft(node2);
        root.setRight(node3);
        node2.setLeft(node4);
        node2.setRight(node5);
        node3.setLeft(node6);

        ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
        threadedBinaryTree.setRoot(root);

        threadedBinaryTree.threadedNodes();

        System.out.println(node5.getLeft());
        System.out.println(node5.getRight());
    }
}

//定义二叉树
class ThreadedBinaryTree {
    private HeroNode root;

    //为了实现线索化,需要创建指向当前节点前驱节点的指针
    //在进行递归的时候,pre总是保留前一个节点
    private HeroNode pre = null;

    public void setRoot(HeroNode root) {
        this.root = root;
    }
    public void threadedNodes() {
        this.threadedNodes(root);
    }
    //编写对二叉树进行中序线索化
    public void threadedNodes(HeroNode node) {//node就是当前需要线索化的节点
        if (node == null) {
            return;
        }
        //先线索化左子树
        threadedNodes(node.getLeft());
        //再处理当前节点

        if (node.getLeft() == null) {
            node.setLeft(pre);
            node.setLeftType(1);
        }
        if (pre != null && pre.getRight() == null) {
            pre.setRight(node);
            pre.setRightType(1);
        }
        //没处理一个节点后,让当前这个节点是下一个节点的前驱节点
        pre =node;
        //在处理右子树
        threadedNodes(node.getRight());
    }


    //删除节点
    public void delNode(int no) {
        if (root != null) {
            if (root.getNo() == no) {
                root = null;
            } else {
                root.delNode(no);
            }
        } else {
            System.out.println("这是空树,无法进行删除");
        }
    }

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

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

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

    public HeroNode preOrderSearch(int no) {
        if (root != null) {
            HeroNode node = this.root.preOrderSearch(no);
            return node;
        }
        return null;
    }

    public HeroNode infixOrderSearch(int no) {
        if (root != null) {
            HeroNode node = this.root.infixOrderSearch(no);
            return node;
        }
        return null;
    }

    public HeroNode suffixOrderSearch(int no) {
        if (root != null) {
            HeroNode node = this.root.suffixOrderSearch(no);
            return node;
        }
        return null;
    }
}

//创建HeroNode节点
class HeroNode {
    private int leftType;//0表示指示的是左子树,1就是前驱节点
    private int rightType;

    public int getLeftType() {
        return leftType;
    }

    public void setLeftType(int leftType) {
        this.leftType = leftType;
    }

    public int getRightType() {
        return rightType;
    }

    public void setRightType(int rightType) {
        this.rightType = rightType;
    }

    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    private int no;
    private String 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;
    }

    private HeroNode left;
    private HeroNode right;

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }//当输出是对象的时候,这个时候toString作用就很大了

    //递归删除节点
    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 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 suffixOrder() {

        if (this.left != null) {
            this.left.suffixOrder();
        }
        if (this.right != null) {
            this.right.suffixOrder();
        }
        System.out.println(this);//先输出父节点
    }

    //前序遍历查找
    public HeroNode preOrderSearch(int no) {
        if (this.no == no) {
            return this;
        }
        HeroNode 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 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;
    }

    public HeroNode suffixOrderSearch(int no) {
        HeroNode resNode = null;

        if (this.left != null) {
            resNode = this.left.suffixOrderSearch(no);
        }
        if (resNode != null) {
            return resNode;
        }

        if (this.right != null) {
            resNode = this.right.suffixOrderSearch(no);
        }
        if (resNode != null) {
            return resNode;
        }

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

        return resNode;
    }
}

7、遍历线索化二叉树

7.1、思路分析

image-20210322170024682

7.2、代码实现

package com.qingchengxiaoye.tree1;

public class ThreadedBinaryTreeDemo {
    public static void main(String[] args) {
        HeroNode root = new HeroNode(1, "1");
        HeroNode node2 = new HeroNode(3, "3");
        HeroNode node3 = new HeroNode(6, "6");
        HeroNode node4 = new HeroNode(8, "8");
        HeroNode node5 = new HeroNode(10, "10");
        HeroNode node6 = new HeroNode(14, "14");

        //先手动创建
        root.setLeft(node2);
        root.setRight(node3);
        node2.setLeft(node4);
        node2.setRight(node5);
        node3.setLeft(node6);

        ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
        threadedBinaryTree.setRoot(root);

        threadedBinaryTree.threadedNodes();

        System.out.println(node5.getLeft());
        System.out.println(node5.getRight());

        System.out.println("使用线索化方式遍历二叉树");
        threadedBinaryTree.threadedList();
    }
}

//定义二叉树
class ThreadedBinaryTree {
    private HeroNode root;

    //为了实现线索化,需要创建指向当前节点前驱节点的指针
    //在进行递归的时候,pre总是保留前一个节点
    private HeroNode pre = null;

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

    public void threadedNodes() {
        this.threadedNodes(root);
    }

    //遍历线索化二叉树的方法
    public void threadedList() {
        HeroNode node = root;
        while (node != null) {
            while (node.getLeftType() == 0) {
                node = node.getLeft();
            }
            System.out.println(node);
            while (node.getRightType()==1){
                node=node.getRight();
                System.out.println(node);
            }
            node=node.getRight();
        }
    }


    //编写对二叉树进行中序线索化
    public void threadedNodes(HeroNode node) {//node就是当前需要线索化的节点
        if (node == null) {
            return;
        }
        //先线索化左子树
        threadedNodes(node.getLeft());
        //再处理当前节点

        if (node.getLeft() == null) {
            node.setLeft(pre);
            node.setLeftType(1);
        }
        if (pre != null && pre.getRight() == null) {
            pre.setRight(node);
            pre.setRightType(1);
        }
        //没处理一个节点后,让当前这个节点是下一个节点的前驱节点
        pre = node;
        //在处理右子树
        threadedNodes(node.getRight());
    }


    //删除节点
    public void delNode(int no) {
        if (root != null) {
            if (root.getNo() == no) {
                root = null;
            } else {
                root.delNode(no);
            }
        } else {
            System.out.println("这是空树,无法进行删除");
        }
    }

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

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

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

    public HeroNode preOrderSearch(int no) {
        if (root != null) {
            HeroNode node = this.root.preOrderSearch(no);
            return node;
        }
        return null;
    }

    public HeroNode infixOrderSearch(int no) {
        if (root != null) {
            HeroNode node = this.root.infixOrderSearch(no);
            return node;
        }
        return null;
    }

    public HeroNode suffixOrderSearch(int no) {
        if (root != null) {
            HeroNode node = this.root.suffixOrderSearch(no);
            return node;
        }
        return null;
    }
}

//创建HeroNode节点
class HeroNode {
    private int leftType;//0表示指示的是左子树,1就是前驱节点
    private int rightType;

    public int getLeftType() {
        return leftType;
    }

    public void setLeftType(int leftType) {
        this.leftType = leftType;
    }

    public int getRightType() {
        return rightType;
    }

    public void setRightType(int rightType) {
        this.rightType = rightType;
    }

    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    private int no;
    private String 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;
    }

    private HeroNode left;
    private HeroNode right;

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }//当输出是对象的时候,这个时候toString作用就很大了

    //递归删除节点
    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 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 suffixOrder() {

        if (this.left != null) {
            this.left.suffixOrder();
        }
        if (this.right != null) {
            this.right.suffixOrder();
        }
        System.out.println(this);//先输出父节点
    }

    //前序遍历查找
    public HeroNode preOrderSearch(int no) {
        if (this.no == no) {
            return this;
        }
        HeroNode 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 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;
    }

    public HeroNode suffixOrderSearch(int no) {
        HeroNode resNode = null;

        if (this.left != null) {
            resNode = this.left.suffixOrderSearch(no);
        }
        if (resNode != null) {
            return resNode;
        }

        if (this.right != null) {
            resNode = this.right.suffixOrderSearch(no);
        }
        if (resNode != null) {
            return resNode;
        }

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

        return resNode;
    }
}

8、树结构实际应用

8.1、堆排序

8.1.1、实现思路

image-20210323193330700

image-20210323193344009

image-20210323193706921

8.1.2、题目求解

从最后一个非叶子节点开始

image-20210323194846932

8.1.2.1、解题思路

image-20210323200348761

image-20210323203037015

8.1.2.2、代码实现

package com.qingchengxiaoye.tree1;

import java.util.Arrays;

public class HeapSort {
    public static void main(String[] args) {
        int[] arr={4,6,8,5,9};
        heapSort(arr);
    }

    //编写一个堆排序的方法
    public static void heapSort(int[] arr){
        int temp=0;
//        adjustHeap(arr,1,arr.length);
//        System.out.println(Arrays.toString(arr));
//
//        adjustHeap(arr,0,arr.length);
//        System.out.println(Arrays.toString(arr));
        //根据升序降序,调整为大小堆
        for (int i = arr.length/2-1; i >=0 ; i--) {
            adjustHeap(arr,i,arr.length);
        }

        for (int j = arr.length-1; j >0 ; j--) {
            temp=arr[j];
            arr[j]=arr[0];
            arr[0]=temp;
            adjustHeap(arr,0,j);
        }
        System.out.println(Arrays.toString(arr));

    }

    //将一个数组(二叉树),调整成为一个大顶堆

    /**
     *
     * @param arr  待调整的数组 @param i   非叶子节点在数组中的索引
     * @param length  表示对多少个元素进行调整    length是在逐渐减少
     */
    public static void adjustHeap(int[] arr,int i,int length){
        int temp=arr[i];
        for (int k = i*2+1; k < length; k=k*2+1) {
            if (k+1<length&&arr[k]<arr[k+1]){
                k++;
            }
            if (arr[k]>temp){
                arr[i]=arr[k];
                i=k;
            }else {
                break;
            }
        }
        arr[i]=temp;
    }
}

9、哈夫曼树

9.1、基本介绍

image-20210323213233125

image-20210324114128478

image-20210324114151208

9.2、题目求解

image-20210324115530725

image-20210324142903509

image-20210324142847923

9.3、代码实现

package com.qingchengxiaoye.huffmanTree;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class HuffmanTree {
    public static void main(String[] args) {
        int[] arr = {13, 7, 8, 3, 29, 6, 1};
        Node root  = create(arr);
        preOrder(root);
    }

    //便写一个前序遍历的方法
    public static void preOrder(Node root){
        if (root!=null){
            root.preOrder();
        }else {
            System.out.println("为空树,不能遍历!");
        }
    }
    //返回的就是赫夫曼树的根节点
    public static Node create(int[] arr) {
        /**
         * 为了方便操作
         * 1、遍历arr数组
         * 2、将arr每个元素都构成一个 Node
         * 3、将Node放入ArrayList中
         */
        List<Node> nodes = new ArrayList<>();
        for (int value : arr) {
            nodes.add(new Node(value));
        }
        while (nodes.size() > 1) {
            //排序,从小到大
            Collections.sort(nodes);

            //取出根节点最小的两颗二叉树
            Node left = nodes.get(0);
            Node right = nodes.get(1);

            //构建一颗新的二叉树
            Node parent = new Node(left.value + right.value);
            parent.left = left;
            parent.right = right;

            //从arraylist中删除处理过的二叉树
            nodes.remove(left);
            nodes.remove(right);

            nodes.add(parent);
        }
        return nodes.get(0);
    }
}

//创建节点类
//为了让Node对象持续排序Collections集合排序
//让Node实现Comparable接口
class Node implements Comparable<Node> {
    int value;//节点权值
    Node left;
    Node right;

    //写一个前序遍历
    public void preOrder(){
        System.out.println(this);
        if (this.left!=null){
            this.left.preOrder();
        }
        if (this.right!=null){
            this.right.preOrder();
        }
}

    public Node(int value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "Node{" + "value=" + value + '}';
    }
    @Override
    public int compareTo(Node o) {
        //表示从小到大进行排序
        return this.value - o.value;
    }
}

10、赫夫曼编码

10.1、基本介绍

image-20210324193559379

image-20210324194247443

image-20210324204716372

image-20210324204200725

image-20210324204432963

image-20210324205658370

10.1.1、实践操作

image-20210324205918998

10.1.2、思路分析

image-20210324210324888

image-20210325101225413

10.1.3、代码实现

package com.qingchengxiaoye.huffumancode;

import java.util.*;

public class Code {
    public static void main(String[] args) {
        String content = "i like like like java do you like a java";
        byte[] contentBytes = content.getBytes();
        System.out.println(contentBytes.length);//40

        List<Node> nodes = getNodes(contentBytes);
        System.out.println(nodes);

        System.out.println("赫夫曼树");
        Node huffmanTreeRoot = create(nodes);

        System.out.println("前序遍历");
        huffmanTreeRoot.preOrder();

        System.out.println("测试是否已经生成了对应的哈夫曼编码");
        getCodes(huffmanTreeRoot);
        System.out.println(huffmanCodes);

        System.out.println("测试压缩后的byte");
        byte[] zip = zip(contentBytes, huffmanCodes);
        System.out.println(Arrays.toString(zip));

    }

    /**
     * @param bytes        这是原始字符串对应的byte数组
     * @param huffmanCodes 生成的哈夫曼编码表
     * @return 返回哈夫曼处理后的
     */
    //将字符串对应的byte[]数组,通过生成的赫夫曼编码表,返回一个赫夫曼编码压缩后的byte[]
    private static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
        //1、利用哈夫曼编码将byte转成哈夫曼编码对应的字符串
        StringBuilder stringBuilder = new StringBuilder();
        for (byte aByte : bytes) {
            stringBuilder.append(huffmanCodes.get(aByte));
        }

        //将字符串转为byte数组
        //统计返回byte[] huffmanCodesBytes的长度
        int len;
        if (stringBuilder.length() % 8 == 0) {
            len = stringBuilder.length() / 8;
        } else {
            len = stringBuilder.length() / 8 + 1;
        }
        //创建存储压缩后的byte数组
        byte[] huffmanCodeBytes = new byte[len];
        int index = 0;//记录是第几个byte
        for (int i = 0; i < stringBuilder.length(); i += 8) {
            String strByte;
            if (i + 8 > stringBuilder.length()) {//不够八位
                strByte = stringBuilder.substring(i);
            } else {
                strByte = stringBuilder.substring(i, i + 8);
            }
            //将strByte转成byte,放入到huffmanCodeBytes
            huffmanCodeBytes[index++] = (byte) Integer.parseInt(strByte, 2);
        }
        return huffmanCodeBytes;

    }

    //生成赫夫曼树对应的编码表
    //将赫夫曼编码存放到Map<Byte,String>形式
    //32-》01
    static Map<Byte, String> huffmanCodes = new HashMap<Byte, String>();
    //再生成赫夫曼编码表示,需要拼接路径,定义一个StringBuild,存储叶子节点的路径
    static StringBuilder stringBuilder = new StringBuilder();


    //为了方便,重载getCode
    private static Map<Byte, String> getCodes(Node root) {
        if (root == null) {
            return null;
        }
        //处理左子树
        getCodes(root.left, "0", stringBuilder);
        getCodes(root.rigth, "1", stringBuilder);
        return huffmanCodes;
    }

    /**
     * 功能:将传入的node节点所有的叶子节点的赫夫曼编码得到,并放入到huffmanCodes中
     *
     * @param node          传入节点
     * @param code          路径 左节点是0  右节点是1
     * @param stringBuilder 用于拼接路径
     */
    private static void getCodes(Node node, String code, StringBuilder stringBuilder) {
        StringBuilder stringBuilder1 = new StringBuilder(stringBuilder);
        //将code加入到stringBuilder1
        stringBuilder1.append(code);
        if (node != null) {

            if (node.data == null) {//非叶子节点
                //向左递归
                getCodes(node.left, "0", stringBuilder1);
                //向右递归
                getCodes(node.rigth, "1", stringBuilder1);
            } else {//说明这是一个叶子节点
                //就表示找到了叶子节点的最后
                huffmanCodes.put(node.data, stringBuilder1.toString());
            }
        }
    }

    private static void preOrder(Node root) {
        if (root != null) {
            root.preOrder();
        } else {
            System.out.println("赫夫曼数为空");
        }

    }

    /**
     * @param bytes 接收字符数组
     * @return
     */
    private static List<Node> getNodes(byte[] bytes) {
        List<Node> nodes = new ArrayList<>();

        HashMap<Byte, Integer> counts = new HashMap<>();
        for (byte b : bytes) {
            Integer count = counts.get(b);
            if (count == null) {
                counts.put(b, 1);
            } else {
                counts.put(b, count + 1);
            }
        }
        //把每一个键值对转成一个Node对象,并加入到nodes集合
        //遍历map
        for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {
            nodes.add(new Node(entry.getKey(), entry.getValue()));
        }
        return nodes;
    }

    //通过list创建对应的赫夫曼树
    public static Node create(List<Node> nodes) {
        while (nodes.size() > 1) {
            //从小到大排序
            Collections.sort(nodes);
            Node leftNode = nodes.get(0);
            Node rightNode = nodes.get(1);
            //创建一颗新的二叉树,他的根节点没有data,只有权值
            Node parent = new Node(null, leftNode.weight + rightNode.weight);
            parent.left = leftNode;
            parent.rigth = rightNode;

            //将已经处理的
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            nodes.add(parent);
        }
        return nodes.get(0);
    }
}

//创建Node 带数据和权值
class Node implements Comparable<Node> {
    //byte 字节  ,一字节等于八位
    Byte data;//存放数据本身,比如‘a’ =》97  ''=>32
    int weight;//权值,表示字符出现的次数
    Node left;
    Node rigth;

    public Node(Byte data, int weight) {
        this.data = data;
        this.weight = weight;
    }

    @Override
    public int compareTo(Node o) {
        return this.weight - o.weight;
    }

    @Override
    public String toString() {
        return "Node{" +
                "data=" + data +
                ", weight=" + weight +
                '}';
    }

    //前序遍历
    public void preOrder() {
        System.out.println(this);
        if (this.left != null) {
            this.left.preOrder();
        }
        if (this.rigth != null) {
            this.rigth.preOrder();
        }
    }
}

10.1.4、封装操作

package com.qingchengxiaoye.huffumancode;

import java.util.*;

public class Code {
    public static void main(String[] args) {
        String content = "i like like like java do you like a java";
        byte[] contentBytes = content.getBytes();

        byte[] bytes = huffmanZip(contentBytes);
        System.out.println("压缩后的结果是");
        System.out.println(Arrays.toString(bytes));
        System.out.println("长度是:==》"+bytes.length);

        //分步过程
        /*
        List<Node> nodes = getNodes(contentBytes);
        System.out.println(nodes);

        System.out.println("赫夫曼树");
        Node huffmanTreeRoot = create(nodes);

        System.out.println("前序遍历");
        huffmanTreeRoot.preOrder();

        System.out.println("测试是否已经生成了对应的哈夫曼编码");
        getCodes(huffmanTreeRoot);
        System.out.println(huffmanCodes);

        System.out.println("测试压缩后的byte");
        byte[] zip = zip(contentBytes, huffmanCodes);
        System.out.println(Arrays.toString(zip));*/

    }
    //使用一个方法,将前面的方法封装起来,便于我们调用

    /**
     *
     * @param bytes  原始字符串对应的字节数组
     * @return  是经过哈夫曼处理后的字节数组(压缩后的数组)
     */
    private static byte[] huffmanZip(byte[] bytes){
        List<Node> nodes = getNodes(bytes);
        //创建赫夫曼树
        Node huffmanTreeRoot = create(nodes);
        //生成对应的赫夫曼编码表
        getCodes(huffmanTreeRoot);
        //根据生成的赫夫曼编码压缩,得到压缩后的赫夫曼编码字节数组
        byte[] zip = zip(bytes, huffmanCodes);
        return zip;
    }


    /**
     * @param bytes        这是原始字符串对应的byte数组
     * @param huffmanCodes 生成的哈夫曼编码表
     * @return 返回哈夫曼处理后的
     */
    //将字符串对应的byte[]数组,通过生成的赫夫曼编码表,返回一个赫夫曼编码压缩后的byte[]
    private static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
        //1、利用哈夫曼编码将byte转成哈夫曼编码对应的字符串
        StringBuilder stringBuilder = new StringBuilder();
        for (byte aByte : bytes) {
            stringBuilder.append(huffmanCodes.get(aByte));
        }

        //将字符串转为byte数组
        //统计返回byte[] huffmanCodesBytes的长度
        int len;
        if (stringBuilder.length() % 8 == 0) {
            len = stringBuilder.length() / 8;
        } else {
            len = stringBuilder.length() / 8 + 1;
        }
        //创建存储压缩后的byte数组
        byte[] huffmanCodeBytes = new byte[len];
        int index = 0;//记录是第几个byte
        for (int i = 0; i < stringBuilder.length(); i += 8) {
            String strByte;
            if (i + 8 > stringBuilder.length()) {//不够八位
                strByte = stringBuilder.substring(i);
            } else {
                strByte = stringBuilder.substring(i, i + 8);
            }
            //将strByte转成byte,放入到huffmanCodeBytes
            huffmanCodeBytes[index++] = (byte) Integer.parseInt(strByte, 2);
        }
        return huffmanCodeBytes;

    }

    //生成赫夫曼树对应的编码表
    //将赫夫曼编码存放到Map<Byte,String>形式
    //32-》01
    static Map<Byte, String> huffmanCodes = new HashMap<Byte, String>();
    //再生成赫夫曼编码表示,需要拼接路径,定义一个StringBuild,存储叶子节点的路径
    static StringBuilder stringBuilder = new StringBuilder();


    //为了方便,重载getCode
    private static Map<Byte, String> getCodes(Node root) {
        if (root == null) {
            return null;
        }
        //处理左子树
        getCodes(root.left, "0", stringBuilder);
        getCodes(root.rigth, "1", stringBuilder);
        return huffmanCodes;
    }

    /**
     * 功能:将传入的node节点所有的叶子节点的赫夫曼编码得到,并放入到huffmanCodes中
     *
     * @param node          传入节点
     * @param code          路径 左节点是0  右节点是1
     * @param stringBuilder 用于拼接路径
     */
    private static void getCodes(Node node, String code, StringBuilder stringBuilder) {
        StringBuilder stringBuilder1 = new StringBuilder(stringBuilder);
        //将code加入到stringBuilder1
        stringBuilder1.append(code);
        if (node != null) {

            if (node.data == null) {//非叶子节点
                //向左递归
                getCodes(node.left, "0", stringBuilder1);
                //向右递归
                getCodes(node.rigth, "1", stringBuilder1);
            } else {//说明这是一个叶子节点
                //就表示找到了叶子节点的最后
                huffmanCodes.put(node.data, stringBuilder1.toString());
            }
        }
    }

    private static void preOrder(Node root) {
        if (root != null) {
            root.preOrder();
        } else {
            System.out.println("赫夫曼数为空");
        }

    }

    /**
     * @param bytes 接收字符数组
     * @return
     */
    private static List<Node> getNodes(byte[] bytes) {
        List<Node> nodes = new ArrayList<>();

        HashMap<Byte, Integer> counts = new HashMap<>();
        for (byte b : bytes) {
            Integer count = counts.get(b);
            if (count == null) {
                counts.put(b, 1);
            } else {
                counts.put(b, count + 1);
            }
        }
        //把每一个键值对转成一个Node对象,并加入到nodes集合
        //遍历map
        for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {
            nodes.add(new Node(entry.getKey(), entry.getValue()));
        }
        return nodes;
    }

    //通过list创建对应的赫夫曼树
    public static Node create(List<Node> nodes) {
        while (nodes.size() > 1) {
            //从小到大排序
            Collections.sort(nodes);
            Node leftNode = nodes.get(0);
            Node rightNode = nodes.get(1);
            //创建一颗新的二叉树,他的根节点没有data,只有权值
            Node parent = new Node(null, leftNode.weight + rightNode.weight);
            parent.left = leftNode;
            parent.rigth = rightNode;

            //将已经处理的
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            nodes.add(parent);
        }
        return nodes.get(0);
    }
}

//创建Node 带数据和权值
class Node implements Comparable<Node> {
    //byte 字节  ,一字节等于八位
    Byte data;//存放数据本身,比如‘a’ =》97  ''=>32
    int weight;//权值,表示字符出现的次数
    Node left;
    Node rigth;

    public Node(Byte data, int weight) {
        this.data = data;
        this.weight = weight;
    }

    @Override
    public int compareTo(Node o) {
        return this.weight - o.weight;
    }

    @Override
    public String toString() {
        return "Node{" +
                "data=" + data +
                ", weight=" + weight +
                '}';
    }

    //前序遍历
    public void preOrder() {
        System.out.println(this);
        if (this.left != null) {
            this.left.preOrder();
        }
        if (this.rigth != null) {
            this.rigth.preOrder();
        }
    }
}

10.2、数据解压

10.2.1、实现思路

image-20210325105505051

image-20210325105654873

10.2.1、代码实现

package com.qingchengxiaoye.huffumancode;

import java.util.*;

public class Code {
    public static void main(String[] args) {
        String content = "i like like like java do you like a java";
        byte[] contentBytes = content.getBytes();

        byte[] huffmanCodeBytes = huffmanZip(contentBytes);
        System.out.println("压缩后的结果是");
        System.out.println(Arrays.toString(huffmanCodeBytes));
        System.out.println("长度是:==》" + huffmanCodeBytes.length);


        //解码
        byte[] sourceBytes = decode(huffmanCodes, huffmanCodeBytes);
        System.out.println(new String(sourceBytes));
    }
    //编写一个方法。完成对压缩数据的解码

    /**
     * @param huffmanCodes 哈夫曼编码表
     * @param huffmanBytes 赫夫曼编码得到的字节数组
     * @return 原来字符串对应的数组
     */
    private static byte[] decode(Map<Byte, String> huffmanCodes, byte[] huffmanBytes) {
        //1、先得到huffmanBytes对应的二进制字符串,形式10100101010.。。
        StringBuilder stringBuilder = new StringBuilder();
        //2、将byte数组转为二进制的字符串
        for (int i = 0; i < huffmanBytes.length; i++) {
            //判断是不是最后一个字节
            boolean flag = i == huffmanBytes.length - 1;
            stringBuilder.append(byteToBitString(!flag, huffmanBytes[i]));
        }

        //把字符串按照指定的赫夫曼编码进行解码
        //把赫夫曼编码进行调换,因为反向查询a->100 100->a
        Map<String, Byte> map = new HashMap<String, Byte>();
        for (Map.Entry<Byte, String> entry : huffmanCodes.entrySet()) {
            map.put(entry.getValue(), entry.getKey());
        }

        //创建一个集合,存放byte
        List<Byte> list = new ArrayList<>();
        //i可以理解为索引,扫描stringBuilder
        for (int i = 0; i < stringBuilder.length(); ) {
            int count = 1;//小的计数器
            boolean flag = true;
            Byte b = null;
            while (flag) {
                //递增取出key
                String key = stringBuilder.substring(i, i + count);//i不动,让count移动,指定匹配一个字符
                b = map.get(key);
                if (b == null) {
                    count++;
                } else {//匹配到了
                    flag = false;
                }
            }
            list.add(b);
            i += count;
        }
        //当for循环结束后,我们list中存放了所有的字符
        //将list中的数据放入到byte[]
        byte[] b = new byte[list.size()];
        for (int i = 0; i < b.length; i++) {
             b[i]=list.get(i);
        }
        return b;
    }

    /**
     * 将一个byte转成一个二进制的字符串
     *
     * @param b    传入的byte
     * @param flag flag表示是否需要补高位,如果是true就是需要  最后一个字节不需要不高为
     * @return 是该b对应的二进制字符串
     */
    private static String byteToBitString(boolean flag, byte b) {
        //使用变量保存b
        int temp = b;//将b转成int
        //如果是正数,需要补高位
        if (flag) {
            temp |= 256;//按位与 1 0000 0000 |0000 0001 =》1 0000 0001
        }
        String str = Integer.toBinaryString(temp);
        if (flag) {
            return str.substring(str.length() - 8);
        } else {
            return str;
        }
    }


    //使用一个方法,将前面的方法封装起来,便于我们调用

    /**
     * @param bytes 原始字符串对应的字节数组
     * @return 是经过哈夫曼处理后的字节数组(压缩后的数组)
     */
    private static byte[] huffmanZip(byte[] bytes) {
        List<Node> nodes = getNodes(bytes);
        //创建赫夫曼树
        Node huffmanTreeRoot = create(nodes);
        //生成对应的赫夫曼编码表
        getCodes(huffmanTreeRoot);
        //根据生成的赫夫曼编码压缩,得到压缩后的赫夫曼编码字节数组
        byte[] zip = zip(bytes, huffmanCodes);
        return zip;
    }


    /**
     * @param bytes        这是原始字符串对应的byte数组
     * @param huffmanCodes 生成的哈夫曼编码表
     * @return 返回哈夫曼处理后的
     */
    //将字符串对应的byte[]数组,通过生成的赫夫曼编码表,返回一个赫夫曼编码压缩后的byte[]
    private static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
        //1、利用哈夫曼编码将byte转成哈夫曼编码对应的字符串
        StringBuilder stringBuilder = new StringBuilder();
        for (byte aByte : bytes) {
            stringBuilder.append(huffmanCodes.get(aByte));
        }

        //将字符串转为byte数组
        //统计返回byte[] huffmanCodesBytes的长度
        int len;
        if (stringBuilder.length() % 8 == 0) {
            len = stringBuilder.length() / 8;
        } else {
            len = stringBuilder.length() / 8 + 1;
        }
        //创建存储压缩后的byte数组
        byte[] huffmanCodeBytes = new byte[len];
        int index = 0;//记录是第几个byte
        for (int i = 0; i < stringBuilder.length(); i += 8) {
            String strByte;
            if (i + 8 > stringBuilder.length()) {//不够八位
                strByte = stringBuilder.substring(i);
            } else {
                strByte = stringBuilder.substring(i, i + 8);
            }
            //将strByte转成byte,放入到huffmanCodeBytes
            huffmanCodeBytes[index++] = (byte) Integer.parseInt(strByte, 2);
        }
        return huffmanCodeBytes;

    }

    //生成赫夫曼树对应的编码表
    //将赫夫曼编码存放到Map<Byte,String>形式
    //32-》01
    static Map<Byte, String> huffmanCodes = new HashMap<Byte, String>();
    //再生成赫夫曼编码表示,需要拼接路径,定义一个StringBuild,存储叶子节点的路径
    static StringBuilder stringBuilder = new StringBuilder();


    //为了方便,重载getCode
    private static Map<Byte, String> getCodes(Node root) {
        if (root == null) {
            return null;
        }
        //处理左子树
        getCodes(root.left, "0", stringBuilder);
        getCodes(root.rigth, "1", stringBuilder);
        return huffmanCodes;
    }

    /**
     * 功能:将传入的node节点所有的叶子节点的赫夫曼编码得到,并放入到huffmanCodes中
     *
     * @param node          传入节点
     * @param code          路径 左节点是0  右节点是1
     * @param stringBuilder 用于拼接路径
     */
    private static void getCodes(Node node, String code, StringBuilder stringBuilder) {
        StringBuilder stringBuilder1 = new StringBuilder(stringBuilder);
        //将code加入到stringBuilder1
        stringBuilder1.append(code);
        if (node != null) {

            if (node.data == null) {//非叶子节点
                //向左递归
                getCodes(node.left, "0", stringBuilder1);
                //向右递归
                getCodes(node.rigth, "1", stringBuilder1);
            } else {//说明这是一个叶子节点
                //就表示找到了叶子节点的最后
                huffmanCodes.put(node.data, stringBuilder1.toString());
            }
        }
    }

    private static void preOrder(Node root) {
        if (root != null) {
            root.preOrder();
        } else {
            System.out.println("赫夫曼数为空");
        }

    }

    /**
     * @param bytes 接收字符数组
     * @return
     */
    private static List<Node> getNodes(byte[] bytes) {
        List<Node> nodes = new ArrayList<>();

        HashMap<Byte, Integer> counts = new HashMap<>();
        for (byte b : bytes) {
            Integer count = counts.get(b);
            if (count == null) {
                counts.put(b, 1);
            } else {
                counts.put(b, count + 1);
            }
        }
        //把每一个键值对转成一个Node对象,并加入到nodes集合
        //遍历map
        for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {
            nodes.add(new Node(entry.getKey(), entry.getValue()));
        }
        return nodes;
    }

    //通过list创建对应的赫夫曼树
    public static Node create(List<Node> nodes) {
        while (nodes.size() > 1) {
            //从小到大排序
            Collections.sort(nodes);
            Node leftNode = nodes.get(0);
            Node rightNode = nodes.get(1);
            //创建一颗新的二叉树,他的根节点没有data,只有权值
            Node parent = new Node(null, leftNode.weight + rightNode.weight);
            parent.left = leftNode;
            parent.rigth = rightNode;

            //将已经处理的
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            nodes.add(parent);
        }
        return nodes.get(0);
    }
}

//创建Node 带数据和权值
class Node implements Comparable<Node> {
    //byte 字节  ,一字节等于八位
    Byte data;//存放数据本身,比如‘a’ =》97  ''=>32
    int weight;//权值,表示字符出现的次数
    Node left;
    Node rigth;

    public Node(Byte data, int weight) {
        this.data = data;
        this.weight = weight;
    }

    @Override
    public int compareTo(Node o) {
        return this.weight - o.weight;
    }

    @Override
    public String toString() {
        return "Node{" +
                "data=" + data +
                ", weight=" + weight +
                '}';
    }

    //前序遍历
    public void preOrder() {
        System.out.println(this);
        if (this.left != null) {
            this.left.preOrder();
        }
        if (this.rigth != null) {
            this.rigth.preOrder();
        }
    }
}

10.3、文件压缩

image-20210325133925585

10.2.1、代码实现

package com.qingchengxiaoye.huffumancode;

import java.io.*;
import java.util.*;

public class Code {
    public static void main(String[] args) {
        //测试压缩文件的代码
        String srcFile="E:\\资料\\图解Java数据结构和算法-尚硅谷-韩顺平\\资料\\压缩测试文件\\src.bmp";
        String dstFile="E:\\资料\\图解Java数据结构和算法-尚硅谷-韩顺平\\资料\\dst.zip";
        zipFile(srcFile,dstFile);
        System.out.println("压缩文件成功");
        /*
        String content = "i like like like java do you like a java";
        byte[] contentBytes = content.getBytes();

        byte[] huffmanCodeBytes = huffmanZip(contentBytes);
        System.out.println("压缩后的结果是");
        System.out.println(Arrays.toString(huffmanCodeBytes));
        System.out.println("长度是:==》" + huffmanCodeBytes.length);


        //解码
        byte[] sourceBytes = decode(huffmanCodes, huffmanCodeBytes);
        System.out.println(new String(sourceBytes));

         */

    }

    //对文件进行压缩

    /**
     *
     * @param srcFile  压缩文件全路径
     * @param dstFile
     */
    public static void zipFile(String srcFile,String dstFile){
        //创建输出流
        OutputStream os=null;
        InputStream is=null;
        ObjectOutputStream oos=null;
        try {
            //创建文件输入流
            is = new FileInputStream(srcFile);
            //创建一个和源文件大小一样的byte[]
            byte[] b = new byte[is.available()];
            //读取文件
            is.read(b);
            //直接对源文件进行压缩
            byte[] huffmanBytes = huffmanZip(b);
            //创建文件输出流,存放压缩文件
            os = new FileOutputStream(dstFile);
            //创建一个与文件输出流关联的ObjectOutputStream
            oos = new ObjectOutputStream(os);
            //把赫夫曼编码后的字节数组写入文件
            oos.writeObject(huffmanBytes);
            //这里我们以对象流的方式写入赫夫曼编码,是为了我们以后恢复文件使用
            //把赫夫曼编码写入压缩文件
            oos.writeObject(huffmanCodes);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
                oos.close();
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }





    //编写一个方法。完成对压缩数据的解码
    /**
     * @param huffmanCodes 哈夫曼编码表
     * @param huffmanBytes 赫夫曼编码得到的字节数组
     * @return 原来字符串对应的数组
     */
    private static byte[] decode(Map<Byte, String> huffmanCodes, byte[] huffmanBytes) {
        //1、先得到huffmanBytes对应的二进制字符串,形式10100101010.。。
        StringBuilder stringBuilder = new StringBuilder();
        //2、将byte数组转为二进制的字符串
        for (int i = 0; i < huffmanBytes.length; i++) {
            //判断是不是最后一个字节
            boolean flag = i == huffmanBytes.length - 1;
            stringBuilder.append(byteToBitString(!flag, huffmanBytes[i]));
        }

        //把字符串按照指定的赫夫曼编码进行解码
        //把赫夫曼编码进行调换,因为反向查询a->100 100->a
        Map<String, Byte> map = new HashMap<String, Byte>();
        for (Map.Entry<Byte, String> entry : huffmanCodes.entrySet()) {
            map.put(entry.getValue(), entry.getKey());
        }

        //创建一个集合,存放byte
        List<Byte> list = new ArrayList<>();
        //i可以理解为索引,扫描stringBuilder
        for (int i = 0; i < stringBuilder.length(); ) {
            int count = 1;//小的计数器
            boolean flag = true;
            Byte b = null;
            while (flag) {
                //递增取出key
                String key = stringBuilder.substring(i, i + count);//i不动,让count移动,指定匹配一个字符
                b = map.get(key);
                if (b == null) {
                    count++;
                } else {//匹配到了
                    flag = false;
                }
            }
            list.add(b);
            i += count;
        }
        //当for循环结束后,我们list中存放了所有的字符
        //将list中的数据放入到byte[]
        byte[] b = new byte[list.size()];
        for (int i = 0; i < b.length; i++) {
             b[i]=list.get(i);
        }
        return b;
    }

    /**
     * 将一个byte转成一个二进制的字符串
     *
     * @param b    传入的byte
     * @param flag flag表示是否需要补高位,如果是true就是需要  最后一个字节不需要不高为
     * @return 是该b对应的二进制字符串
     */
    private static String byteToBitString(boolean flag, byte b) {
        //使用变量保存b
        int temp = b;//将b转成int
        //如果是正数,需要补高位
        if (flag) {
            temp |= 256;//按位与 1 0000 0000 |0000 0001 =》1 0000 0001
        }
        String str = Integer.toBinaryString(temp);
        if (flag) {
            return str.substring(str.length() - 8);
        } else {
            return str;
        }
    }


    //使用一个方法,将前面的方法封装起来,便于我们调用

    /**
     * @param bytes 原始字符串对应的字节数组
     * @return 是经过哈夫曼处理后的字节数组(压缩后的数组)
     */
    private static byte[] huffmanZip(byte[] bytes) {
        List<Node> nodes = getNodes(bytes);
        //创建赫夫曼树
        Node huffmanTreeRoot = create(nodes);
        //生成对应的赫夫曼编码表
        getCodes(huffmanTreeRoot);
        //根据生成的赫夫曼编码压缩,得到压缩后的赫夫曼编码字节数组
        byte[] zip = zip(bytes, huffmanCodes);
        return zip;
    }


    /**
     * @param bytes        这是原始字符串对应的byte数组
     * @param huffmanCodes 生成的哈夫曼编码表
     * @return 返回哈夫曼处理后的
     */
    //将字符串对应的byte[]数组,通过生成的赫夫曼编码表,返回一个赫夫曼编码压缩后的byte[]
    private static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
        //1、利用哈夫曼编码将byte转成哈夫曼编码对应的字符串
        StringBuilder stringBuilder = new StringBuilder();
        for (byte aByte : bytes) {
            stringBuilder.append(huffmanCodes.get(aByte));
        }

        //将字符串转为byte数组
        //统计返回byte[] huffmanCodesBytes的长度
        int len;
        if (stringBuilder.length() % 8 == 0) {
            len = stringBuilder.length() / 8;
        } else {
            len = stringBuilder.length() / 8 + 1;
        }
        //创建存储压缩后的byte数组
        byte[] huffmanCodeBytes = new byte[len];
        int index = 0;//记录是第几个byte
        for (int i = 0; i < stringBuilder.length(); i += 8) {
            String strByte;
            if (i + 8 > stringBuilder.length()) {//不够八位
                strByte = stringBuilder.substring(i);
            } else {
                strByte = stringBuilder.substring(i, i + 8);
            }
            //将strByte转成byte,放入到huffmanCodeBytes
            huffmanCodeBytes[index++] = (byte) Integer.parseInt(strByte, 2);
        }
        return huffmanCodeBytes;

    }

    //生成赫夫曼树对应的编码表
    //将赫夫曼编码存放到Map<Byte,String>形式
    //32-》01
    static Map<Byte, String> huffmanCodes = new HashMap<Byte, String>();
    //再生成赫夫曼编码表示,需要拼接路径,定义一个StringBuild,存储叶子节点的路径
    static StringBuilder stringBuilder = new StringBuilder();


    //为了方便,重载getCode
    private static Map<Byte, String> getCodes(Node root) {
        if (root == null) {
            return null;
        }
        //处理左子树
        getCodes(root.left, "0", stringBuilder);
        getCodes(root.rigth, "1", stringBuilder);
        return huffmanCodes;
    }

    /**
     * 功能:将传入的node节点所有的叶子节点的赫夫曼编码得到,并放入到huffmanCodes中
     *
     * @param node          传入节点
     * @param code          路径 左节点是0  右节点是1
     * @param stringBuilder 用于拼接路径
     */
    private static void getCodes(Node node, String code, StringBuilder stringBuilder) {
        StringBuilder stringBuilder1 = new StringBuilder(stringBuilder);
        //将code加入到stringBuilder1
        stringBuilder1.append(code);
        if (node != null) {

            if (node.data == null) {//非叶子节点
                //向左递归
                getCodes(node.left, "0", stringBuilder1);
                //向右递归
                getCodes(node.rigth, "1", stringBuilder1);
            } else {//说明这是一个叶子节点
                //就表示找到了叶子节点的最后
                huffmanCodes.put(node.data, stringBuilder1.toString());
            }
        }
    }

    private static void preOrder(Node root) {
        if (root != null) {
            root.preOrder();
        } else {
            System.out.println("赫夫曼数为空");
        }

    }

    /**
     * @param bytes 接收字符数组
     * @return
     */
    private static List<Node> getNodes(byte[] bytes) {
        List<Node> nodes = new ArrayList<>();

        HashMap<Byte, Integer> counts = new HashMap<>();
        for (byte b : bytes) {
            Integer count = counts.get(b);
            if (count == null) {
                counts.put(b, 1);
            } else {
                counts.put(b, count + 1);
            }
        }
        //把每一个键值对转成一个Node对象,并加入到nodes集合
        //遍历map
        for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {
            nodes.add(new Node(entry.getKey(), entry.getValue()));
        }
        return nodes;
    }

    //通过list创建对应的赫夫曼树
    public static Node create(List<Node> nodes) {
        while (nodes.size() > 1) {
            //从小到大排序
            Collections.sort(nodes);
            Node leftNode = nodes.get(0);
            Node rightNode = nodes.get(1);
            //创建一颗新的二叉树,他的根节点没有data,只有权值
            Node parent = new Node(null, leftNode.weight + rightNode.weight);
            parent.left = leftNode;
            parent.rigth = rightNode;

            //将已经处理的
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            nodes.add(parent);
        }
        return nodes.get(0);
    }
}

//创建Node 带数据和权值
class Node implements Comparable<Node> {
    //byte 字节  ,一字节等于八位
    Byte data;//存放数据本身,比如‘a’ =》97  ''=>32
    int weight;//权值,表示字符出现的次数
    Node left;
    Node rigth;

    public Node(Byte data, int weight) {
        this.data = data;
        this.weight = weight;
    }

    @Override
    public int compareTo(Node o) {
        return this.weight - o.weight;
    }

    @Override
    public String toString() {
        return "Node{" +
                "data=" + data +
                ", weight=" + weight +
                '}';
    }

    //前序遍历
    public void preOrder() {
        System.out.println(this);
        if (this.left != null) {
            this.left.preOrder();
        }
        if (this.rigth != null) {
            this.rigth.preOrder();
        }
    }
}

10.4、文件解压

image-20210325141022457

10.4.1、代码实现

package com.qingchengxiaoye.huffumancode;

import java.io.*;
import java.util.*;

public class Code {
    public static void main(String[] args) {
        //测试压缩文件的代码
//        String srcFile="E:\\资料\\图解Java数据结构和算法-尚硅谷-韩顺平\\资料\\压缩测试文件\\src.bmp";
//        String dstFile="E:\\资料\\图解Java数据结构和算法-尚硅谷-韩顺平\\资料\\dst.zip";
//        zipFile(srcFile,dstFile);
//        System.out.println("压缩文件成功");
        //测试解压
        String zipFile="E:\\资料\\图解Java数据结构和算法-尚硅谷-韩顺平\\资料\\dst.zip";
        String dstFile="E:\\资料\\图解Java数据结构和算法-尚硅谷-韩顺平\\资料\\src.bmp";
        unZipFile(zipFile,dstFile);
        System.out.println("解压成功");
        /*
        String content = "i like like like java do you like a java";
        byte[] contentBytes = content.getBytes();

        byte[] huffmanCodeBytes = huffmanZip(contentBytes);
        System.out.println("压缩后的结果是");
        System.out.println(Arrays.toString(huffmanCodeBytes));
        System.out.println("长度是:==》" + huffmanCodeBytes.length);


        //解码
        byte[] sourceBytes = decode(huffmanCodes, huffmanCodeBytes);
        System.out.println(new String(sourceBytes));

         */

    }
    //对文件解压
    public static void unZipFile(String zipFile,String dstFile){
        //定义文件输入流
        InputStream is=null;
        //定义一个对象输入流
        ObjectInputStream ois=null;
        //定义文件输出流
        OutputStream os=null;
        try {
            //创建文件输入流
            is=new FileInputStream(zipFile);
            //创建一个和is关联的对象输入流
            ois=new ObjectInputStream(is);
            //读取byte数组 huffmanBytes
            byte[] huffmanBytes = (byte[]) ois.readObject();
            //读取赫夫曼编码表
            Map<Byte,String> huffmanCodes= (Map<Byte, String>) ois.readObject();

            //解码
            byte[] bytes = decode(huffmanCodes, huffmanBytes);
            //将bytes写入到目标问价
            os = new FileOutputStream(dstFile);
            //写数据到文件中
            os.write(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
                ois.close();
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    //对文件进行压缩
    /**
     *
     * @param srcFile  压缩文件全路径
     * @param dstFile
     */
    public static void zipFile(String srcFile,String dstFile){
        //创建输出流
        OutputStream os=null;
        InputStream is=null;
        ObjectOutputStream oos=null;
        try {
            //创建文件输入流
            is = new FileInputStream(srcFile);
            //创建一个和源文件大小一样的byte[]
            byte[] b = new byte[is.available()];
            //读取文件
            is.read(b);
            //直接对源文件进行压缩
            byte[] huffmanBytes = huffmanZip(b);
            //创建文件输出流,存放压缩文件
            os = new FileOutputStream(dstFile);
            //创建一个与文件输出流关联的ObjectOutputStream
            oos = new ObjectOutputStream(os);
            //把赫夫曼编码后的字节数组写入文件
            oos.writeObject(huffmanBytes);
            //这里我们以对象流的方式写入赫夫曼编码,是为了我们以后恢复文件使用
            //把赫夫曼编码写入压缩文件
            oos.writeObject(huffmanCodes);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
                oos.close();
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }





    //编写一个方法。完成对压缩数据的解码
    /**
     * @param huffmanCodes 哈夫曼编码表
     * @param huffmanBytes 赫夫曼编码得到的字节数组
     * @return 原来字符串对应的数组
     */
    private static byte[] decode(Map<Byte, String> huffmanCodes, byte[] huffmanBytes) {
        //1、先得到huffmanBytes对应的二进制字符串,形式10100101010.。。
        StringBuilder stringBuilder = new StringBuilder();
        //2、将byte数组转为二进制的字符串
        for (int i = 0; i < huffmanBytes.length; i++) {
            //判断是不是最后一个字节
            boolean flag = i == huffmanBytes.length - 1;
            stringBuilder.append(byteToBitString(!flag, huffmanBytes[i]));
        }

        //把字符串按照指定的赫夫曼编码进行解码
        //把赫夫曼编码进行调换,因为反向查询a->100 100->a
        Map<String, Byte> map = new HashMap<String, Byte>();
        for (Map.Entry<Byte, String> entry : huffmanCodes.entrySet()) {
            map.put(entry.getValue(), entry.getKey());
        }

        //创建一个集合,存放byte
        List<Byte> list = new ArrayList<>();
        //i可以理解为索引,扫描stringBuilder
        for (int i = 0; i < stringBuilder.length(); ) {
            int count = 1;//小的计数器
            boolean flag = true;
            Byte b = null;
            while (flag) {
                //递增取出key
                String key = stringBuilder.substring(i, i + count);//i不动,让count移动,指定匹配一个字符
                b = map.get(key);
                if (b == null) {
                    count++;
                } else {//匹配到了
                    flag = false;
                }
            }
            list.add(b);
            i += count;
        }
        //当for循环结束后,我们list中存放了所有的字符
        //将list中的数据放入到byte[]
        byte[] b = new byte[list.size()];
        for (int i = 0; i < b.length; i++) {
             b[i]=list.get(i);
        }
        return b;
    }

    /**
     * 将一个byte转成一个二进制的字符串
     *
     * @param b    传入的byte
     * @param flag flag表示是否需要补高位,如果是true就是需要  最后一个字节不需要不高为
     * @return 是该b对应的二进制字符串
     */
    private static String byteToBitString(boolean flag, byte b) {
        //使用变量保存b
        int temp = b;//将b转成int
        //如果是正数,需要补高位
        if (flag) {
            temp |= 256;//按位与 1 0000 0000 |0000 0001 =》1 0000 0001
        }
        String str = Integer.toBinaryString(temp);
        if (flag) {
            return str.substring(str.length() - 8);
        } else {
            return str;
        }
    }


    //使用一个方法,将前面的方法封装起来,便于我们调用

    /**
     * @param bytes 原始字符串对应的字节数组
     * @return 是经过哈夫曼处理后的字节数组(压缩后的数组)
     */
    private static byte[] huffmanZip(byte[] bytes) {
        List<Node> nodes = getNodes(bytes);
        //创建赫夫曼树
        Node huffmanTreeRoot = create(nodes);
        //生成对应的赫夫曼编码表
        getCodes(huffmanTreeRoot);
        //根据生成的赫夫曼编码压缩,得到压缩后的赫夫曼编码字节数组
        byte[] zip = zip(bytes, huffmanCodes);
        return zip;
    }


    /**
     * @param bytes        这是原始字符串对应的byte数组
     * @param huffmanCodes 生成的哈夫曼编码表
     * @return 返回哈夫曼处理后的
     */
    //将字符串对应的byte[]数组,通过生成的赫夫曼编码表,返回一个赫夫曼编码压缩后的byte[]
    private static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
        //1、利用哈夫曼编码将byte转成哈夫曼编码对应的字符串
        StringBuilder stringBuilder = new StringBuilder();
        for (byte aByte : bytes) {
            stringBuilder.append(huffmanCodes.get(aByte));
        }

        //将字符串转为byte数组
        //统计返回byte[] huffmanCodesBytes的长度
        int len;
        if (stringBuilder.length() % 8 == 0) {
            len = stringBuilder.length() / 8;
        } else {
            len = stringBuilder.length() / 8 + 1;
        }
        //创建存储压缩后的byte数组
        byte[] huffmanCodeBytes = new byte[len];
        int index = 0;//记录是第几个byte
        for (int i = 0; i < stringBuilder.length(); i += 8) {
            String strByte;
            if (i + 8 > stringBuilder.length()) {//不够八位
                strByte = stringBuilder.substring(i);
            } else {
                strByte = stringBuilder.substring(i, i + 8);
            }
            //将strByte转成byte,放入到huffmanCodeBytes
            huffmanCodeBytes[index++] = (byte) Integer.parseInt(strByte, 2);
        }
        return huffmanCodeBytes;

    }

    //生成赫夫曼树对应的编码表
    //将赫夫曼编码存放到Map<Byte,String>形式
    //32-》01
    static Map<Byte, String> huffmanCodes = new HashMap<Byte, String>();
    //再生成赫夫曼编码表示,需要拼接路径,定义一个StringBuild,存储叶子节点的路径
    static StringBuilder stringBuilder = new StringBuilder();


    //为了方便,重载getCode
    private static Map<Byte, String> getCodes(Node root) {
        if (root == null) {
            return null;
        }
        //处理左子树
        getCodes(root.left, "0", stringBuilder);
        getCodes(root.rigth, "1", stringBuilder);
        return huffmanCodes;
    }

    /**
     * 功能:将传入的node节点所有的叶子节点的赫夫曼编码得到,并放入到huffmanCodes中
     *
     * @param node          传入节点
     * @param code          路径 左节点是0  右节点是1
     * @param stringBuilder 用于拼接路径
     */
    private static void getCodes(Node node, String code, StringBuilder stringBuilder) {
        StringBuilder stringBuilder1 = new StringBuilder(stringBuilder);
        //将code加入到stringBuilder1
        stringBuilder1.append(code);
        if (node != null) {

            if (node.data == null) {//非叶子节点
                //向左递归
                getCodes(node.left, "0", stringBuilder1);
                //向右递归
                getCodes(node.rigth, "1", stringBuilder1);
            } else {//说明这是一个叶子节点
                //就表示找到了叶子节点的最后
                huffmanCodes.put(node.data, stringBuilder1.toString());
            }
        }
    }

    private static void preOrder(Node root) {
        if (root != null) {
            root.preOrder();
        } else {
            System.out.println("赫夫曼数为空");
        }

    }

    /**
     * @param bytes 接收字符数组
     * @return
     */
    private static List<Node> getNodes(byte[] bytes) {
        List<Node> nodes = new ArrayList<>();

        HashMap<Byte, Integer> counts = new HashMap<>();
        for (byte b : bytes) {
            Integer count = counts.get(b);
            if (count == null) {
                counts.put(b, 1);
            } else {
                counts.put(b, count + 1);
            }
        }
        //把每一个键值对转成一个Node对象,并加入到nodes集合
        //遍历map
        for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {
            nodes.add(new Node(entry.getKey(), entry.getValue()));
        }
        return nodes;
    }

    //通过list创建对应的赫夫曼树
    public static Node create(List<Node> nodes) {
        while (nodes.size() > 1) {
            //从小到大排序
            Collections.sort(nodes);
            Node leftNode = nodes.get(0);
            Node rightNode = nodes.get(1);
            //创建一颗新的二叉树,他的根节点没有data,只有权值
            Node parent = new Node(null, leftNode.weight + rightNode.weight);
            parent.left = leftNode;
            parent.rigth = rightNode;

            //将已经处理的
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            nodes.add(parent);
        }
        return nodes.get(0);
    }
}

//创建Node 带数据和权值
class Node implements Comparable<Node> {
    //byte 字节  ,一字节等于八位
    Byte data;//存放数据本身,比如‘a’ =》97  ''=>32
    int weight;//权值,表示字符出现的次数
    Node left;
    Node rigth;

    public Node(Byte data, int weight) {
        this.data = data;
        this.weight = weight;
    }

    @Override
    public int compareTo(Node o) {
        return this.weight - o.weight;
    }

    @Override
    public String toString() {
        return "Node{" +
                "data=" + data +
                ", weight=" + weight +
                '}';
    }

    //前序遍历
    public void preOrder() {
        System.out.println(this);
        if (this.left != null) {
            this.left.preOrder();
        }
        if (this.rigth != null) {
            this.rigth.preOrder();
        }
    }
}

10.5、注意事项

image-20210325143343261

11、二叉排序树

11.1、需求分析

image-20210325192351279

image-20210325192516167

11.2、介绍

image-20210325192634395

11.3、代码实现

image-20210325192907270

package com.qingchengxiaoye.binarysorttree;


import com.qingchengxiaoye.tree.BinnaryTree;

public class BinarySortTreeDemo {
    public static void main(String[] args) {
        int[] arr={7,3,10,12,5,1,9};
        BinarySortTree binarySortTree = new BinarySortTree();
        for (int i : arr) {
            binarySortTree.add(new Node(i));
        }
        binarySortTree.infixOrder();
    }
}

//创建二叉排序树
class BinarySortTree{
    private Node root;
    //添加节点的方法
    public void add(Node node){
        if (root==null){
            root=node;
        }else {
            root.add(node);
        }
    }

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

class Node {
    int value;
    Node left;
    Node right;

    public Node(int value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                '}';
    }

    //添加节点的方法
    //递归的形式添加节点,
    public void add(Node node) {
        if (node == null) {
            return;
        }
        //判断传入的值和当前节点的关系
        if (node.value < this.value) {
            //如果当前左子树为空
            if (this.left == null) {
                this.left = node;
            } else {
                //递归向左子树添加
                this.left.add(node);
            }
        } else {
            //如果当前左子树为空
            if (this.right == null) {
                this.right = node;
            } else {
                //递归向左子树添加
                this.right.add(node);
            }
        }
    }

    //中序遍历
    public void infixOrder(){
        if (this.left!=null){
            this.left.infixOrder();
        }
        System.out.println(this);
        if (this.right!=null){
            this.right.infixOrder();
        }
    }
}

11.4、二叉树的删除

image-20210325200017942

image-20210325201406704

image-20210325201421185

image-20210325202158185

11.5代码实现

package com.qingchengxiaoye.binarysorttree;


public class BinarySortTreeDemo {
    public static void main(String[] args) {
        int[] arr = {7, 3, 10, 12, 5, 1, 9, 2};
        BinarySortTree binarySortTree = new BinarySortTree();
        for (int i : arr) {
            binarySortTree.add(new Node(i));
        }
        System.out.println("中序遍历二叉排序树·");
        binarySortTree.infixOrder();

        //测试一下删除叶子节点
        binarySortTree.delNode(10);
        System.out.println("删除叶子节点后");
        binarySortTree.infixOrder();
    }
}

//创建二叉排序树
class BinarySortTree {
    private Node root;

    /**
     * @param node 传入的节点,(当作二叉排序树的根节点)
     * @return 返回以node节点为根节点的二叉树的  最小节点的值
     * 还要删除这个值
     */
    public int delRightTreeMin(Node node) {
        Node target = node;
        //循环查找左节点,就会找到最小值
        while (target.left != null) {
            target = target.left;
        }
        //这是target指向最小的值
        delNode(target.value);
        return target.value;
    }

    //删除节点
    public void delNode(int value) {
        if (root == null) {
            return;
        } else {
            Node targetNode = search(value);
            //如果没有找到要删除的的节点
            if (targetNode == null) {
                return;
            }
            //如果发现当前这颗二叉排序树只有一个节点
            if (root.left == null && root.right == null) {
                root = null;
                return;
            }
            //去找到targetNode的父节点
            Node parent = searchParent(value);
            //如果要删除的节点是叶子节点
            if (targetNode.left == null && targetNode.right == null) {
                //判断targetNode是父节点的左子节点还是右子节点
                if (parent.left != null && parent.left.value == value) {//是左子节点
                    parent.left = null;
                } else if (parent.right != null && parent.right.value == value) {//是右子节点
                    parent.right = null;
                }
            } else if (targetNode.left != null && targetNode.right != null) {
                int minVal = delRightTreeMin(targetNode.right);
                targetNode.value = minVal;
            } else {//删除只有一棵子树的节点
                //如果要删除的节点有左子节点
                if (targetNode.left != null) {
                    if (parent != null) {
                        //如果targetNode是parent的左节点
                        if (parent.left.value == value) {
                            parent.left = targetNode.left;
                        } else {//如果targetNode是parent的右节点
                            parent.right = targetNode.left;
                        }
                    } else {
                        root = targetNode.left;
                    }
                } else {//如果要删除的节点有右子节点
                    if (parent != null) {
                        //如果targetNode是parent的左节点
                        if (parent.left.value == value) {
                            parent.left = targetNode.right;
                        } else {//如果targetNode是parent的右节点
                            parent.right = targetNode.right;
                        }
                    } else {
                        root = targetNode.right;
                    }
                }
            }
        }
    }

    //查找父节点
    public Node searchParent(int value) {
        if (root == null) {
            return null;
        } else {
            return root.searchParent(value);
        }
    }

    //查找要删除的节点
    public Node search(int value) {
        if (root == null) {
            return null;
        } else {
            return root.search(value);
        }
    }

    //添加节点的方法
    public void add(Node node) {
        if (root == null) {
            root = node;
        } else {
            root.add(node);
        }
    }

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

class Node {
    int value;
    Node left;
    Node right;

    public Node(int value) {
        this.value = value;
    }

    //查找要删除的节点
    public Node search(int value) {
        if (this.value == value) {
            return this;
        } else if (value < this.value) {
            if (this.left == null) {
                return null;
            }
            return this.left.search(value);
        } else {
            if (this.right == null) {
                return null;
            }
            return this.right.search(value);
        }

    }
    //查找要删除节点的父节点

    /**
     * @param value
     * @return 返回的是要删除父节点的值,如果没有就返回null
     */
    public Node searchParent(int value) {
        //如果当前节点就是要删除节点的父节点,就返回
        if ((this.left != null && this.left.value == value) ||
                (this.right != null && this.right.value == value)) {
            return this;
        } else {
            //如果当前要查找的值小于当前节点,并且当前节点的左子节点不为空
            if (value < this.value && this.left != null) {
                return this.left.searchParent(value);//向左子树递归查找
            } else if (value >= this.value && this.right != null) {
                return this.right.searchParent(value);//向右子树
            } else {
                return null;//没有找到父节点
            }
        }

    }

    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                '}';
    }

    //添加节点的方法
    //递归的形式添加节点,
    public void add(Node node) {
        if (node == null) {
            return;
        }
        //判断传入的值和当前节点的关系
        if (node.value < this.value) {
            //如果当前左子树为空
            if (this.left == null) {
                this.left = node;
            } else {
                //递归向左子树添加
                this.left.add(node);
            }
        } else {
            //如果当前左子树为空
            if (this.right == null) {
                this.right = node;
            } else {
                //递归向左子树添加
                this.right.add(node);
            }
        }
    }

    //中序遍历
    public void infixOrder() {
        if (this.left != null) {
            this.left.infixOrder();
        }
        System.out.println(this);
        if (this.right != null) {
            this.right.infixOrder();
        }
    }
}

12、平衡二叉树

12.1、介绍

image-20210326085428026

image-20210326085631485

12.2、案例分析

12.2.1、左旋转

image-20210326085709735

image-20210326090445935

12.2.2、右旋转

image-20210326154733004

image-20210326154756909

12.2.3、双旋转

image-20210326155921694

image-20210326160009203

image-20210326161022033

12.3、代码实现

package com.qingchengxiaoye.avl;

public class AVLTreeDemo {
    public static void main(String[] args) {
//        int[] arr = {4, 3, 6, 5, 7, 8};
//        int[] arr = {10, 12, 8, 9, 7, 6};
        int [] arr={10,11,7,6,8,9};
        //创建一个AVl树
        AVLTree avlTree = new AVLTree();
        for (int i : arr) {
            avlTree.add(new Node(i));
        }
        //遍历
        System.out.println("中序遍历");
        avlTree.infixOrder();

        System.out.println("在没有平衡处理前");
        System.out.println("树的高度=" + avlTree.getRoot().height());
        System.out.println("树的左子树高度=" + avlTree.getRoot().leftHeight());
        System.out.println("树的右子树高度=" + avlTree.getRoot().rightHeight());
        System.out.println("当前根节点="+avlTree.getRoot());
    }
}

//创建AVLTree
class AVLTree {
    private Node root;

    public Node getRoot() {
        return root;
    }

    /**
     * @param node 传入的节点,(当作二叉排序树的根节点)
     * @return 返回以node节点为根节点的二叉树的  最小节点的值
     * 还要删除这个值
     */
    public int delRightTreeMin(Node node) {
        Node target = node;
        //循环查找左节点,就会找到最小值
        while (target.left != null) {
            target = target.left;
        }
        //这是target指向最小的值
        delNode(target.value);
        return target.value;
    }

    //删除节点
    public void delNode(int value) {
        if (root == null) {
            return;
        } else {
            Node targetNode = search(value);
            //如果没有找到要删除的的节点
            if (targetNode == null) {
                return;
            }
            //如果发现当前这颗二叉排序树只有一个节点
            if (root.left == null && root.right == null) {
                root = null;
                return;
            }
            //去找到targetNode的父节点
            Node parent = searchParent(value);
            //如果要删除的节点是叶子节点
            if (targetNode.left == null && targetNode.right == null) {
                //判断targetNode是父节点的左子节点还是右子节点
                if (parent.left != null && parent.left.value == value) {//是左子节点
                    parent.left = null;
                } else if (parent.right != null && parent.right.value == value) {//是右子节点
                    parent.right = null;
                }
            } else if (targetNode.left != null && targetNode.right != null) {
                int minVal = delRightTreeMin(targetNode.right);
                targetNode.value = minVal;
            } else {//删除只有一棵子树的节点
                //如果要删除的节点有左子节点
                if (targetNode.left != null) {
                    if (parent != null) {
                        //如果targetNode是parent的左节点
                        if (parent.left.value == value) {
                            parent.left = targetNode.left;
                        } else {//如果targetNode是parent的右节点
                            parent.right = targetNode.left;
                        }
                    } else {
                        root = targetNode.left;
                    }
                } else {//如果要删除的节点有右子节点
                    if (parent != null) {
                        //如果targetNode是parent的左节点
                        if (parent.left.value == value) {
                            parent.left = targetNode.right;
                        } else {//如果targetNode是parent的右节点
                            parent.right = targetNode.right;
                        }
                    } else {
                        root = targetNode.right;
                    }
                }
            }
        }
    }

    //查找父节点
    public Node searchParent(int value) {
        if (root == null) {
            return null;
        } else {
            return root.searchParent(value);
        }
    }

    //查找要删除的节点
    public Node search(int value) {
        if (root == null) {
            return null;
        } else {
            return root.search(value);
        }
    }

    //添加节点的方法
    public void add(Node node) {
        if (root == null) {
            root = node;
        } else {
            root.add(node);
        }
    }

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

class Node {
    int value;
    Node left;
    Node right;

    public Node(int value) {
        this.value = value;
    }

    //右旋转
    private void rightRotate() {

        Node newNode = new Node(value);

        newNode.right = right;

        newNode.left = left.right;

        value = left.value;

        left = left.left;

        right = newNode;
    }

    //左旋转的方法
    private void leftRotate() {
        //创建新的节点,以以前根节点的值
        Node newNode = new Node(value);
        //把新的节点的左子树设置为当前节点的左子树
        newNode.left = left;
        //把新的节点的右子树设置为当前节点的右子树的左子树
        newNode.right = right.left;
        //把当前节点的值替换成右子节点的值
        value = right.value;
        //把当前节点的右子树设置成当前节点右子树的右子树
        right = right.right;
        //把当前节点的左子树设置成新的节点
        left = newNode;

    }

    //返回左子树的高度
    public int leftHeight() {
        if (left == null) {
            return 0;
        }
        return left.height();
    }

    //返回右子树的高度
    public int rightHeight() {
        if (right == null) {
            return 0;
        }
        return right.height();
    }

    //返回以该节点为根节点的高度
    public int height() {
        return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
    }

    //查找要删除的节点
    Node search(int value) {
        if (this.value == value) {
            return this;
        } else if (value < this.value) {
            if (this.left == null) {
                return null;
            }
            return this.left.search(value);
        } else {
            if (this.right == null) {
                return null;
            }
            return this.right.search(value);
        }

    }
    //查找要删除节点的父节点

    /**
     * @param value
     * @return 返回的是要删除父节点的值,如果没有就返回null
     */
    Node searchParent(int value) {
        //如果当前节点就是要删除节点的父节点,就返回
        if ((this.left != null && this.left.value == value) ||
                (this.right != null && this.right.value == value)) {
            return this;
        } else {
            //如果当前要查找的值小于当前节点,并且当前节点的左子节点不为空
            if (value < this.value && this.left != null) {
                return this.left.searchParent(value);//向左子树递归查找
            } else if (value >= this.value && this.right != null) {
                return this.right.searchParent(value);//向右子树
            } else {
                return null;//没有找到父节点
            }
        }

    }

    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                '}';
    }

    //添加节点的方法
    //递归的形式添加节点,
    public void add(Node node) {
        if (node == null) {
            return;
        }
        //判断传入的值和当前节点的关系
        if (node.value < this.value) {
            //如果当前左子树为空
            if (this.left == null) {
                this.left = node;
            } else {
                //递归向左子树添加
                this.left.add(node);
            }
        } else {
            //如果当前左子树为空
            if (this.right == null) {
                this.right = node;
            } else {
                //递归向左子树添加
                this.right.add(node);
            }
        }

        //当添加一个节点后,如果:(右子树高度-左子树的高度)>1 ,左旋转
        if (rightHeight() - leftHeight() > 1) {
            if (right!=null&&right.leftHeight()>right.rightHeight()){
                //先对当前节点的右子树进行左旋转
                left.leftRotate();
            }
            leftRotate();
            return;
        }

        //当添加一个节点后,如果:(左子树高度-右子树的高度)>1 ,右旋转
        if (leftHeight() - rightHeight() > 1) {
            //如果左子树的右子树高度大于他的左子树的高度
            if (left!=null&&left.rightHeight()>left.leftHeight()){
                //先对当前节点的左子树进行左旋转
                left.leftRotate();
            }
            //针对当前节点进行旋转
            rightRotate();
            return;
        }
    }

    //中序遍历
    public void infixOrder() {
        if (this.left != null) {
            this.left.infixOrder();
        }
        System.out.println(this);
        if (this.right != null) {
            this.right.infixOrder();
        }
    }
}

13、多路查找树

13.1、问题分析

image-20210326163535267

image-20210326163734605

image-20210326163938281

image-20210326164012386

image-20210326164336812

image-20210326164451102

image-20210326165050872

image-20210326165155782

image-20210326165422234

image-20210326165624489

    left = newNode;

}

//返回左子树的高度
public int leftHeight() {
    if (left == null) {
        return 0;
    }
    return left.height();
}

//返回右子树的高度
public int rightHeight() {
    if (right == null) {
        return 0;
    }
    return right.height();
}

//返回以该节点为根节点的高度
public int height() {
    return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
}

//查找要删除的节点
Node search(int value) {
    if (this.value == value) {
        return this;
    } else if (value < this.value) {
        if (this.left == null) {
            return null;
        }
        return this.left.search(value);
    } else {
        if (this.right == null) {
            return null;
        }
        return this.right.search(value);
    }

}
//查找要删除节点的父节点

/**
 * @param value
 * @return 返回的是要删除父节点的值,如果没有就返回null
 */
Node searchParent(int value) {
    //如果当前节点就是要删除节点的父节点,就返回
    if ((this.left != null && this.left.value == value) ||
            (this.right != null && this.right.value == value)) {
        return this;
    } else {
        //如果当前要查找的值小于当前节点,并且当前节点的左子节点不为空
        if (value < this.value && this.left != null) {
            return this.left.searchParent(value);//向左子树递归查找
        } else if (value >= this.value && this.right != null) {
            return this.right.searchParent(value);//向右子树
        } else {
            return null;//没有找到父节点
        }
    }

}

@Override
public String toString() {
    return "Node{" +
            "value=" + value +
            '}';
}

//添加节点的方法
//递归的形式添加节点,
public void add(Node node) {
    if (node == null) {
        return;
    }
    //判断传入的值和当前节点的关系
    if (node.value < this.value) {
        //如果当前左子树为空
        if (this.left == null) {
            this.left = node;
        } else {
            //递归向左子树添加
            this.left.add(node);
        }
    } else {
        //如果当前左子树为空
        if (this.right == null) {
            this.right = node;
        } else {
            //递归向左子树添加
            this.right.add(node);
        }
    }

    //当添加一个节点后,如果:(右子树高度-左子树的高度)>1 ,左旋转
    if (rightHeight() - leftHeight() > 1) {
        if (right!=null&&right.leftHeight()>right.rightHeight()){
            //先对当前节点的右子树进行左旋转
            left.leftRotate();
        }
        leftRotate();
        return;
    }

    //当添加一个节点后,如果:(左子树高度-右子树的高度)>1 ,右旋转
    if (leftHeight() - rightHeight() > 1) {
        //如果左子树的右子树高度大于他的左子树的高度
        if (left!=null&&left.rightHeight()>left.leftHeight()){
            //先对当前节点的左子树进行左旋转
            left.leftRotate();
        }
        //针对当前节点进行旋转
        rightRotate();
        return;
    }
}

//中序遍历
public void infixOrder() {
    if (this.left != null) {
        this.left.infixOrder();
    }
    System.out.println(this);
    if (this.right != null) {
        this.right.infixOrder();
    }
}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值