chapter8 树

数组、链表存储分析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二叉树

在这里插入图片描述
在这里插入图片描述

满二叉树:

完全二叉树:
在这里插入图片描述

二叉树的遍历

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

前序遍历:12354

中序遍历:21534

后序遍历:25431

package tree;

/**
 * @author : sky
 * @version : 1.0
 */
public class BinaryTreeTest {
    public static void main(String[] args) {
        //先需要创建一棵二叉树
        BinaryTree binaryTree=new BinaryTree();
        //创建需要的节点
        Node root=new Node(1,"宋江");
        Node node2=new Node(2,"吴用");
        Node node3=new Node(3,"卢俊义");
        Node node4=new Node(4,"林冲");
        Node node5=new Node(5,"关胜");
        //说明:按理说,二叉树应该递归创建,但是目前先手动创建
        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);
        binaryTree.setRoot(root);

        //测试
        System.out.println("前序遍历");//1234  12354
        binaryTree.preOrder();
        System.out.println("------------------------");
        System.out.println("中序遍历");//2134  21534
        binaryTree.infixOrder();
        System.out.println("------------------------");
        System.out.println("后序遍历");//2431  25431
        binaryTree.postOrder();
    }
}

//节点类
class Node{
    private int no;
    private String name;
    private Node left;//默认为null
    private Node right;//默认为null

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

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Node getLeft() {
        return left;
    }

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

    public Node getRight() {
        return right;
    }

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

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

    //前序遍历
    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 postOrder(){
        //递归向左子树后序遍历
        if(this.left!=null){
            this.left.postOrder();
        }
        //递归向右子树后序遍历
        if(this.right!=null){
            this.right.postOrder();
        }
        System.out.println(this);//输出当前节点,即父节点
    }
}

//定义二叉树
class BinaryTree{
    private Node root;//定义一个根节点

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

    //前序遍历,二叉树调用前序遍历,真正的方案实现是在节点类实现的
    public void preOrder(){
        if(this.root!=null){
            this.root.preOrder();
        }else{
            System.out.println("二叉树为空,无法遍历");
        }
    }

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

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

二叉树的查找

在这里插入图片描述
在这里插入图片描述

package tree;

/**
 * @author : sky
 * @version : 1.0
 */
public class BinaryTreeTest {
    public static void main(String[] args) {
        //先需要创建一棵二叉树
        BinaryTree binaryTree=new BinaryTree();
        //创建需要的节点
        Node root=new Node(1,"宋江");
        Node node2=new Node(2,"吴用");
        Node node3=new Node(3,"卢俊义");
        Node node4=new Node(4,"林冲");
        Node node5=new Node(5,"关胜");
        //说明:按理说,二叉树应该递归创建,但是目前先手动创建
        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);
        binaryTree.setRoot(root);

        //测试
        System.out.println("前序遍历");//1234  12354
        binaryTree.preOrder();
        System.out.println("------------------------");
        System.out.println("中序遍历");//2134  21534
        binaryTree.infixOrder();
        System.out.println("------------------------");
        System.out.println("后序遍历");//2431  25431
        binaryTree.postOrder();
        System.out.println("------------------------");
        System.out.println();
        System.out.println("前序查找");
        Node resNode = binaryTree.preOrderSearch1(5);//no==5,比较了4次
        if(resNode!=null){
            System.out.println("找到了,查找结果:"+resNode);
        }else {
            System.out.println("没有找到!");
        }
        System.out.println("------------------------");
        System.out.println("中序查找");
        resNode = binaryTree.infixOrderSearch1(5);//no==5,比较了3次
        if(resNode!=null){
            System.out.println("找到了,查找结果:"+resNode);
        }else {
            System.out.println("没有找到!");
        }
        System.out.println("------------------------");
        System.out.println("后序查找");
        resNode = binaryTree.postOrderSearch1(5);//no==5,比较了2次
        if(resNode!=null){
            System.out.println("找到了,查找结果:"+resNode);
        }else {
            System.out.println("没有找到!");
        }

    }
}

//节点类
class Node{
    private int no;
    private String name;
    private Node left;//默认为null
    private Node right;//默认为null

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

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Node getLeft() {
        return left;
    }

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

    public Node getRight() {
        return right;
    }

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

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

    //前序遍历
    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 postOrder(){
        //递归向左子树后序遍历
        if(this.left!=null){
            this.left.postOrder();
        }
        //递归向右子树后序遍历
        if(this.right!=null){
            this.right.postOrder();
        }
        System.out.println(this);//输出当前节点,即父节点
    }


    /**
     * 前序遍历查找
     * @param no 需要查找的节点
     * @return 如果找到返回该节点,如果没有找到,返回null
     */
    public Node preOrderSearch(int no){
        System.out.println("进入前序查找");
        //比较当前节点是不是
        if(this.no==no){
            return this;
        }
        //1.判断左子节点是否为空,如果不为空,则递归前序查找
        //2.如果左递归前序查找,找到节点则返回
        Node resNode=null;
        if(this.left!=null){
           resNode =this.left.preOrderSearch(no);
        }
        if(resNode!=null){//如果这个结果节点不为空,说明左子树找到了
            return resNode;
        }
        //3.左子树没有找到,则要判断当前节点的右子节点是否为空,如果不为空,则继续右递归查找
        if(this.right!=null){
            resNode=this.right.preOrderSearch(no);
        }
        return resNode;//这个时候不管找没找到都必须返回
    }
    //中序遍历查找
    public Node infixOrderSearch(int no){

        Node resNode=null;
        //判断当前节点的左子节点是否为空,如果不为空,递归中序查找
        if(this.left!=null){
            resNode=this.left.infixOrderSearch(no);
        }
        if(resNode!=null){
            return resNode;
        }
        System.out.println("进入中序查找");
        //左子树没有找到,和当前节点进行比较
        if(this.no==no){
            return this;
        }
        //否则向右进行中序查找
        if(this.right!=null){
            resNode=this.right.infixOrderSearch(no);
        }
        return resNode;
    }

    //后序遍历查找
    public Node postOrderSearch(int no){
        //先判断当前节点的左子节点是否为空,如果不为空则递归后续查找
        Node resNode=null;
        if(this.left!=null){
            resNode=this.left.postOrderSearch(no);
        }
        if(resNode!=null){//说明在左子树找到
            return resNode;
        }
        //如果左子树没有找到,则向右递归查找
        if(this.right!=null){
            resNode=this.right.postOrderSearch(no);
        }
        if(resNode!=null){
            return resNode;
        }
        System.out.println("进入后序查找");
        //如果左右子树都没有找到
        if(this.no==no){
            return this;
        }
        return resNode;
    }
}

//定义二叉树
class BinaryTree{
    private Node root;//定义一个根节点

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

    //前序遍历,二叉树调用前序遍历,真正的方案实现是在节点类实现的
    public void preOrder(){
        if(this.root!=null){
            this.root.preOrder();
        }else{
            System.out.println("二叉树为空,无法遍历");
        }
    }

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

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

    //前序查找
    public Node preOrderSearch1(int no){
        if(root!=null){
            return root.preOrderSearch(no);
        }
        return null;
    }

    //中序查找
    public Node infixOrderSearch1(int no){
        if(root!=null){
            return root.infixOrderSearch(no);
        }
        return null;
    }

    //后序查找
    public Node postOrderSearch1(int no){
        if(root!=null){
            return root.postOrderSearch(no);
        }
        return null;
    }
}

二叉树删除节点

在这里插入图片描述

单向表要删除节点的话只能找到他的前一个节点
在这里插入图片描述

package tree;

/**
 * @author : sky
 * @version : 1.0
 */
public class BinaryTreeTest {
    public static void main(String[] args) {
        //先需要创建一棵二叉树
        BinaryTree binaryTree=new BinaryTree();
        //创建需要的节点
        Node root=new Node(1,"宋江");
        Node node2=new Node(2,"吴用");
        Node node3=new Node(3,"卢俊义");
        Node node4=new Node(4,"林冲");
        Node node5=new Node(5,"关胜");
        //说明:按理说,二叉树应该递归创建,但是目前先手动创建
        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);
        binaryTree.setRoot(root);

        //测试
        System.out.println("前序遍历");//1234  12354
        binaryTree.preOrder();
        System.out.println("------------------------");
        //删除节点
        binaryTree.deleteNode(3);
        System.out.println("删除后");
        binaryTree.preOrder();//1234

    }
}

//节点类
class Node{
    private int no;
    private String name;
    private Node left;//默认为null
    private Node right;//默认为null

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

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Node getLeft() {
        return left;
    }

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

    public Node getRight() {
        return right;
    }

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

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


    //递归删除节点,如果叶子节点,直接删除,如果不是,则删除子树
    public void deleteNode(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.deleteNode(no);
        }
        //没有删除掉的话,右子树递归删除
        if(this.right!=null){
            this.right.deleteNode(no);
        }
    }
}

//定义二叉树
class BinaryTree{
    private Node root;//定义一个根节点

    public void setRoot(Node root) {
        this.root = root;
    }
	//删除节点
    public void deleteNode(int no){
        //首先判断根节点是否为空
        if(root!=null){
            //这里得判断root节点是否就是需要删除的节点
            if(root.getNo()==no){
                root=null;
            }else{
                //递归删除
                root.deleteNode(no);
            }
        }else{
            System.out.println("空树,不能删除");
        }
    }
}

在这里插入图片描述

顺序存储二叉树

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package tree;

/**
 * @author : sky
 * @version : 1.0
 */
public class ArrBinaryTreeDemo {
    public static void main(String[] args) {
        int[] arr={1,2,3,4,5,6,7};
        //创建一个ArrBinaryTree
        ArrBinaryTree arrBinaryTree = new ArrBinaryTree(arr);
        arrBinaryTree.preOrder();//1245367

        arrBinaryTree.infixOrder();//4251637
        
        arrBinaryTree.postOrder();//4526731
    }
}

//编写一个类,实现顺序存储二叉树
class ArrBinaryTree{
    private int[] arr;//存储数据节点的数组

    public ArrBinaryTree(int[] arr) {
        this.arr = arr;
    }

    //重载preOrder
    public void preOrder(){
        this.preOrder(0);
    }

    //编写一个方法,完成顺序存储二叉树的前序遍历
    /**
     *
     * @param index 表示数组的下标
     */
    public void preOrder(int index){
        //如果数组为空或者数组arr.length==0
        if(arr==null||arr.length==0){
            System.out.println("数组为空\t");
        }
        System.out.print(arr[index]+"\t");//输出当前的元素
        //向左递归遍历,并保证数组不越界
        if(index*2+1<arr.length){
            preOrder(index*2+1);
        }
        //向右递归遍历并保证数组不越界
        if(index*2+2<arr.length){
            preOrder(index*2+2);
        }
    }

    //中序遍历
    public void infixOrder(int index){
        if(arr==null||arr.length==0){
            System.out.println("数组为空");
        }
        //向左递归遍历,并保证数组不越界
        if(index*2+1<arr.length){
            infixOrder(index*2+1);
        }
        System.out.print(arr[index]+"\t");//输出当前的元素
        //向右递归遍历并保证数组不越界
        if(index*2+2<arr.length){
            infixOrder(index*2+2);
        }
    }
    public void infixOrder(){
        this.infixOrder(0);
    }

    //后序遍历
    public void postOrder(int index){
        if(arr==null||arr.length==0){
            System.out.println("数组为空");
        }
        //向左递归遍历,并保证数组不越界
        if(index*2+1<arr.length){
            postOrder(index*2+1);
        }
        //向右递归遍历并保证数组不越界
        if(index*2+2<arr.length){
            postOrder(index*2+2);
        }
        System.out.print(arr[index]+"\t");//输出当前的元素
    }
    public void postOrder(){
        this.postOrder(0);
    }
}

在这里插入图片描述

线索化二叉树

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

中序线索化:

package tree;

/**
 * @author : sky
 * @version : 1.0
 */
public class ThreadBinaryTreeDemo {
    public static void main(String[] args) {
        //测试中序线索化
        threadNode root = new threadNode(1, "tom");
        threadNode node2 = new threadNode(3, "jack");
        threadNode node3 = new threadNode(6, "smith");
        threadNode node4 = new threadNode(8, "mary");
        threadNode node5 = new threadNode(10, "kim");
        threadNode node6 = new threadNode(14, "timmy");
        //二叉树后面递归创建,现在手动创建
        root.setLeft(node2);
        root.setRight(node3);
        node2.setLeft(node4);
        node2.setRight(node5);
        node3.setLeft(node6);
        ThreadBinaryTree threadBinaryTree = new ThreadBinaryTree();
        threadBinaryTree.setRoot(root);
        threadBinaryTree.infixThreadedNode();

        //测试:以10号节点测试
        threadNode left = node5.getLeft();
        System.out.println("10号节点的前驱节点");
        System.out.println(left);//Node{no=3, name='jack'}
        System.out.println("10号节点的后继节点");
        System.out.println(node5.getRight());//Node{no=1, name='tom'}

    }
}

class threadNode{
    private int no;
    private String name;
    private threadNode left;//默认为null
    private threadNode right;//默认为null

    //说明:规定:
    //1.如果leftType==0 表示指向的是左子树,如果leftType==1 表示指向的是前驱节点
    //2.如果rightType==0 表示指向的是右子树,如果rightType==1 表示指向的是后继节点
    private int leftType;
    private int rightType;

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

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public threadNode getLeft() {
        return left;
    }

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

    public threadNode getRight() {
        return right;
    }

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

    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;
    }

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

//线索化二叉树
class ThreadBinaryTree{
    private threadNode root;//定义一个根节点
    //在递归线索化时,pre总是指向前一个节点
    private threadNode pre=null;

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

    //中序线索化方法
    /**
     * @param node 这个node就是当前需要线索化的节点
     */
    public void infixThreadedNode(threadNode node){
        //如果node==null,不能线索化
        if(node==null){
            return;
        }

        //1.先线索化左子树
        infixThreadedNode(node.getLeft());
        //2.线索化当前节点
        //先处理当前节点的前驱节点
        //以8节点来理解
        //8节点的.left=null,8节点的.leftType=1
        if(node.getLeft()==null){
            //让当前节点的左指针指向前驱节点
            node.setLeft(pre);
            //修改当前节点左指针的类型,指向前驱节点
            node.setLeftType(1);
        }
        //理解:本节点的后继结点处理,需要等到本节点为pre时才处理
        //处理后继结点
        if(pre!=null&&pre.getRight()==null){
            //让前驱节点的右指针指向当前节点
            pre.setRight(node);
            //修改前驱节点的右指针类型
            pre.setRightType(1);
        }
        //!!!每处理一个节点之后,让当前节点是下一个节点的前驱节点
        pre=node;
        //3.再线索化右子树
        infixThreadedNode(node.getRight());
    }

    //重载threadedNode方法
    public void infixThreadedNode(){
       this.infixThreadedNode(root);
    }
}

在这里插入图片描述
在这里插入图片描述

后序遍历线索化二叉树需要定义父节点

package tree;



/**
 * @author : sky
 * @version : 1.0
 */
public class ThreadBinaryTreeDemo {
    public static void main(String[] args) {
        //测试中序线索化
        threadNode root = new threadNode(1, "tom");
        threadNode node2 = new threadNode(3, "jack");
        threadNode node3 = new threadNode(6, "smith");
        threadNode node4 = new threadNode(8, "mary");
        threadNode node5 = new threadNode(10, "kim");
        threadNode node6 = new threadNode(14, "timmy");
        //二叉树后面递归创建,现在手动创建
        root.setLeft(node2);
        root.setRight(node3);
        node2.setLeft(node4);
        node2.setRight(node5);
        node3.setLeft(node6);
        ThreadBinaryTree threadBinaryTree = new ThreadBinaryTree();
        threadBinaryTree.setRoot(root);
        //threadBinaryTree.infixThreadedNode();
        //threadBinaryTree.preThreadedNode();
        threadBinaryTree.postThreadedNode();

        //测试:以10号节点测试
        threadNode left = node5.getLeft();
        System.out.println("10号节点的前驱节点");
        System.out.println(left);//Node{no=3, name='jack'}
        System.out.println("10号节点的后继节点");
        System.out.println(node5.getRight());//Node{no=1, name='tom'}
        System.out.println("----------------------");
        System.out.println("线索化遍历:");
        //threadBinaryTree.infixThreadedList();//8 3 10 1 14 6
        //threadBinaryTree.preThreadedList();
        //threadBinaryTree.postThreadedList(root);
    }
}

class threadNode{
    private int no;
    private String name;
    private threadNode left;//默认为null
    private threadNode right;//默认为null

    //说明:规定:
    //1.如果leftType==0 表示指向的是左子树,如果leftType==1 表示指向的是前驱节点
    //2.如果rightType==0 表示指向的是右子树,如果rightType==1 表示指向的是后继节点
    private int leftType;
    private int rightType;

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

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public threadNode getLeft() {
        return left;
    }

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

    public threadNode getRight() {
        return right;
    }

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

    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;
    }

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

//线索化二叉树
class ThreadBinaryTree{
    private threadNode root;//定义一个根节点
    //在递归线索化时,pre总是指向前一个节点
    private threadNode pre=null;

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

    //中序线索化方法
    /**
     * @param node 这个node就是当前需要线索化的节点
     */
    public void infixThreadedNode(threadNode node){
        //如果node==null,不能线索化
        if(node==null){
            return;
        }

        //1.先线索化左子树
        infixThreadedNode(node.getLeft());
        //2.线索化当前节点
        //先处理当前节点的前驱节点
        //以8节点来理解
        //8节点的.left=null,8节点的.leftType=1
        if(node.getLeft()==null){
            //让当前节点的左指针指向前驱节点
            node.setLeft(pre);
            //修改当前节点左指针的类型,指向前驱节点
            node.setLeftType(1);
        }
        //理解:本节点的后继结点处理,需要等到本节点为pre时才处理
        //处理后继结点
        if(pre!=null&&pre.getRight()==null){
            //让前驱节点的右指针指向当前节点
            pre.setRight(node);
            //修改前驱节点的右指针类型
            pre.setRightType(1);
        }
        //!!!每处理一个节点之后,让当前节点是下一个节点的前驱节点
        pre=node;
        //3.再线索化右子树
        infixThreadedNode(node.getRight());
    }

    //重载threadedNode方法
    public void infixThreadedNode(){
       this.infixThreadedNode(root);
    }


    //遍历中序线索化树
    public void infixThreadedList(){
        //定义一个变量,存储当前遍历的节点
        threadNode node=root;
        while(node!=null){
            //循环找到leftType==1的节点,第一个找到的就是8节点
            //后面随着遍历变化而变化,当left=1时,说明该节点是按照线索化处理后的有效节点
            while(node.getLeftType()==0){
                node=node.getLeft();
            }
            //打印当前节点
            System.out.println(node);
            //如果当前节点的右指针指向的是后继节点,就一直输出
            while(node.getRightType()==1){
                //获取到当前节点的后继节点
                node=node.getRight();
                System.out.println(node);
            }
            //如果不等于1就替换遍历的节点
            node=node.getRight();
        }
    }

    public void preThreadedNode(threadNode node){
        //如果node==null,不能线索化
        if(node==null){
            return;
        }
        //1.线索化当前节点
        if(node.getLeft()==null){
            node.setLeft(pre);
            node.setLeftType(1);
        }
        //处理后继结点
        if(pre!=null&&pre.getRight()==null){
            pre.setRight(node);
            pre.setRightType(1);
        }
        //!!!每处理一个节点之后,让当前节点是下一个节点的前驱节点
        pre=node;

        //只有当节点没有线索化才递归,不然会陷入死循环
        //2.先线索化左子树
        if(node.getLeftType()==0){
            preThreadedNode(node.getLeft());
        }

        //3.再线索化右子树
        if(node.getRightType()==0){
            preThreadedNode(node.getRight());
        }
    }
    //重载threadedNode方法
    public void preThreadedNode(){
        this.preThreadedNode(root);
    }

    //遍历前序线索化树
    public void preThreadedList(){
        //定义一个变量,存储当前遍历的节点
        threadNode node=root;
        while(node!=null){
            //打印当前节点
            System.out.println(node);

            if(node.getLeftType()==0){
                node=node.getLeft();
            }else {
                node=node.getRight();
            }
        }
    }

    public void postThreadedNode(threadNode node){
        //如果node==null,不能线索化
        if(node==null){
            return;
        }

        //1.先线索化左子树
        postThreadedNode(node.getLeft());
        //2.再线索化右子树
        postThreadedNode(node.getRight());
        //3.线索化当前节点
        if(node.getLeft()==null){
            node.setLeft(pre);
            node.setLeftType(1);
        }
        //处理后继结点
        if(pre!=null&&pre.getRight()==null){
            pre.setRight(node);
            pre.setRightType(1);
        }
        //!!!每处理一个节点之后,让当前节点是下一个节点的前驱节点
        pre=node;
    }
    //重载threadedNode方法
    public void postThreadedNode(){
        this.postThreadedNode(root);
    }
    
}

树结构的应用

堆排序

见chapter5

package paixu;

import java.util.Arrays;

/**
 * @author : sky
 * @version : 1.0
 */
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;
        System.out.println("堆排序");
        //分步完成
        /*adjustHeap(arr,1,arr.length);
        System.out.println(Arrays.toString(arr));//[4, 9, 8, 5, 6]
        adjustHeap(arr,0,arr.length);
        System.out.println(Arrays.toString(arr));//[9, 6, 8, 5, 4]*/
        
        //最终代码
        //将无序序列构建成一个大顶堆, 第一个非叶子节点的值为arr.length/2-1
        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));
    }

    //将一个数组 (二叉树),调整成为一个大顶堆
    /**
     * 功能:完成将以i对应的非叶子节点的树,调整成大顶堆,即图解中的步骤一(2)
     * 即将{4,6,8,5,9}=> i=1 => 第一次调整为{4,9,8,5,6};
     * i=0  => 再次调整为 {9,6,8,5,4};
     * @param arr 待调整的数组
     * @param i 非叶子节点在数组中的索引
     * @param length 表示对多少个元素进行调整,length在逐渐减少
     */
    public static void adjustHeap(int[] arr,int i,int length){
        int temp=arr[i];//先取出当前元素的值,保存在临时变量
        
        //开始调整
        //k是i节点的左子节点
        for (int k = i*2+1; k <length ; k=k*2+1) {
            if(k+1<length && arr[k]<arr[k+1]){//说明左子节点的值小于右子节点的值
                k++;//k指向右子节点
            }
            if(arr[k]>temp){//如果子节点大于父节点
                arr[i]=arr[k];//把较大的值付给当前的节点
                i=k;//!!!然后让i指向k,继续循环比较
            }else{
                break;//因为是从最后一个非叶子节点走的,所以他之下的都是小的
            }
        }
        //当for循环结束后,我们已经将以i为父节点的树的最大值,放在了最顶上,即i的位置,此时是局部调整
        arr[i]=temp;//将temp的值放到调整后的位置
    }
}

哈夫曼树

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

构建哈夫曼树

在这里插入图片描述
在这里插入图片描述

一般左子节点指向值较大的,右子节点指向值较小的
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

WPL=29x1+13x3+7x3+8x3+6x4+1x5+3x5=157

手动创建的时候注意,大的放右边,小的放左边

package huffmantree;

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

/**
 * @author : sky
 * @version : 1.0
 */
public class HuffmanTreeDemo {
    public static void main(String[] args) {
        int[] arr={13,7,8,3,29,6,1};
        Node root = createHuffmanTree(arr);
        preOrder(root);//67 29 38 15 7 8 23 10 4 1 3 6 13
    }

    //创建哈夫曼树的方法
    /**
     * @param arr 需要创建成哈夫曼树的数组
     * @return 返回哈夫曼树的根节点
     */
    public static Node createHuffmanTree(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 leftNode=nodes.get(0);
            Node rightNode=nodes.get(1);

            Node parent=new Node(leftNode.value + rightNode.value);
            parent.left=leftNode;
            parent.right=rightNode;

            nodes.remove(leftNode);
            nodes.remove(rightNode);

            nodes.add(parent);
        }
        //返回哈夫曼树的根节点root节点
        return nodes.get(0);

        //排序:从小到大
        /*Collections.sort(nodes);
        System.out.println(nodes);*/

        /*//取出权值最小的两棵二叉树
        //1.取出权值最小的两个节点(二叉树)
        Node leftNode=nodes.get(0);
        Node rightNode=nodes.get(1);
        //2.构建一棵新的二叉树
        Node parent=new Node(leftNode.value + rightNode.value);
        parent.left=leftNode;
        parent.right=rightNode;

        //3.从ArrayList中删除处理过的二叉树
        nodes.remove(leftNode);
        nodes.remove(rightNode);
        //4.将parent加入到nodes中
        nodes.add(parent);
        Collections.sort(nodes);
        System.out.println("第一次处理后:"+nodes);*/
    }

    //哈夫曼树前序遍历
    public static void preOrder(Node root){
        if(root!=null){
            root.preOrder();
        }else{
            System.out.println("空树");
        }
    }
}

//创建节点类
//为了让Node对象对象支持排序 使用Collections集合排序,让Node实现Comparable接口
class Node implements Comparable<Node>{
    int value;//节点权值
    Node left;//指向左子节点
    Node right;//指向右子节点

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

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

    @Override
    public int compareTo(Node o) {//注意:使用sort方法必须实现
        //表示从小到大排序
        return this.value-o.value;
        //表示从大到小排序 return -(this.value-o.value);
    }

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

哈夫曼编码

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

哈夫曼编码是无损处理的,即无损压缩,解压不会出错
在这里插入图片描述
在这里插入图片描述

数据压缩(编码)

在这里插入图片描述

程序中间反码补码说明
在这里插入图片描述

package huffmancode;

import java.util.*;

/**
 * @author : sky
 * @version : 1.0
 */
public class HuffmanCode {
    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 = createHuffmanTree(nodes);
        preOrder(huffmanTreeRoot);

        //是否生成了对应的哈夫曼编码
        Map<Byte, String> huffmanCodes = getCodes(huffmanTreeRoot);
        System.out.println("生成的哈夫曼编码表:"+ huffmanCodes);
        //{32=01, 97=100, 100=11000, 117=11001, 101=1110,
        // 118=11011, 105=101, 121=11010, 106=0010, 107=1111, 108=000, 111=0011}

        byte[] zip = zip(contentBytes, huffmanCodes);
        System.out.println(Arrays.toString(zip));//17*/

        //发送哈夫曼编码之后的数组

        byte[] huffmanCodeBytes = huffmanZip(contentBytes);
        System.out.println(Arrays.toString(huffmanCodeBytes));
        System.out.println(huffmanCodeBytes.length);
    }

    /**
     * //使用一个方法将前面的方法封装起来,方便调用
     * @param bytes 原始的字符串对应的字节数组
     * @return 经过哈夫曼编码处理后的字节数组,即压缩后的字节数组
     */
    private static byte[] huffmanZip(byte[] bytes){
        //第一步 :把bytes都变成Node对象,并放入集合中
        List<Node> nodes = getNodes(bytes);
        //第二步:创建哈夫曼树
        Node huffmanTreeRoot = createHuffmanTree(nodes);
        //第三步:生成对应的哈夫曼编码(根据哈夫曼树来创建)
        Map<Byte, String> huffmanCodes = getCodes(huffmanTreeRoot);
        //第四步:返回哈夫曼编码的字节数组
        byte[] huffmanCodeBytes = zip(bytes, huffmanCodes);
        return huffmanCodeBytes;
    }

    /**
     *
     * @param bytes 接收一个字节数组
     * @return 返回一个List 形式 node【data=97,weight=5】
     */
    private static List<Node> getNodes(byte[] bytes){
        //1.先创建一个ArrayList
        List<Node> nodes=new ArrayList<>();
        //2.遍历bytes,统计并存储每一个byte出现的次数---->map[key,value]
        Map<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);
            }
        }
        //3.把每个键值对转成一个node对象,并加入到nodes集合中
        for(Map.Entry<Byte,Integer> entry:counts.entrySet()){
            nodes.add(new Node(entry.getKey(),entry.getValue()));
        }
        return nodes;
    }

    //通过List,创建对应的哈夫曼树
    private static Node createHuffmanTree(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.right=rightNode;

            //将处理过的移除
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            //加入
            nodes.add(parent);
        }
        return nodes.get(0);
    }
    //生成的哈夫曼树对应的哈夫曼编码
    //思路:1.将哈夫曼编码表放在Map<Byte,String> 形式 32-01,97-100
    static Map<Byte,String> huffmanCodes=new HashMap<>();
    //2.在生产哈夫曼编码表时,需要去拼接路径,定义一个StringBuilder存储某个叶子节点的路径
    static StringBuilder stringBuilder=new StringBuilder();

    /**
     * 功能:将传入的node节点的所有叶子节点的哈夫曼编码得到,并存放到huffmanCodes集合中
     * @param node 传入的节点
     * @param code 路径:左 0 右 1
     * @param stringBuilder 用来拼接路径
     */
    private static void getCodes(Node node,String code,StringBuilder stringBuilder){
        StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);
        //将code加入到stringBuilder2
        stringBuilder2.append(code);
        if(node!=null){//如果node==null不处理
            //判断当前node是叶子节点还是非叶子节点
            if(node.data==null){//非叶子节点
                //递归处理
                //向左处理
                getCodes(node.left,"0",stringBuilder2);
                //向右递归
                getCodes(node.right,"1",stringBuilder2);
            }else{//说明是叶子节点
                //表示找到了某个叶子节点最后
                huffmanCodes.put(node.data,stringBuilder2.toString());
            }
        }
    }

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


    /**
     * 编写一个方法,将字符串对应的byte[]数组,通过生成的哈夫曼编码表,返回一个哈夫曼编码处理后的byte[]数组
     * @param bytes 原始的字符串对应的byte[]
     * @param huffmanCodes 生成的哈夫曼编码map
     * @return 返回哈夫曼编码处理后的byte[]
     * String content="i like like like java do you like a java";
     * 传入的是:byte[] contentBytes = content.getBytes();
     * 返回的是字符串"1010100010........"对应的byte[] 8位对应一个byte,放入到huffmanCodesBytes中
     * huffmanCodesBytes[0] =10101000(补码)==》真实byte里的数是[10101000-> 10101000-1 -> 10100111(反码) ->11011000]
     * 即huffmanCodesBytes[0]=-88
     */
    private static byte[] zip(byte[] bytes,Map<Byte,String> huffmanCodes){
        //1.利用 huffmanCodes 将 bytes 转成 哈夫曼编码对应的字符串
        StringBuilder stringBuilder = new StringBuilder();
        //遍历bytes数组
        for(byte b:bytes){
            stringBuilder.append(huffmanCodes.get(b));
        }
        //System.out.println("lll"+stringBuilder);
        //1010100010111111110010001011111111001000101111111100100101001101110001110000011011101000111100101000101111111100110001001010011011100
        //2.将"1010100...."转成byte[]
        //统计返回的byte[] huffmanCodeBytes 长度
        //一句话int len=(stringBuilder.length()+7)/8;
        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){//因为是每8位对应一个byte,所以步长为8
            String strByte;
            if(i+8>stringBuilder.length()){//不够8位
                strByte=stringBuilder.substring(i);//直接取到最后
            }else{
                strByte=stringBuilder.substring(i,i+8);
            }
            //将strByte转成一个byte,放入huffmanCodeBytes数组中
            huffmanCodeBytes[index]=(byte)Integer.parseInt(strByte,2);
            index++;
        }
        return huffmanCodeBytes;
    }

    //前序遍历
    public static void preOrder(Node root){
        if(root!=null){
            root.preOrder();
        }
    }
}

//创建Node,带数据和权值
class Node implements Comparable<Node>{
    Byte data;//存放数据本身(字符),比如'a' =>97  ' '=>32
    int weight;//权值,字符出现的次数
    Node left;
    Node right;

    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.right!=null){
            this.right.preOrder();
        }
    }
}

数据解压(解码)

在这里插入图片描述

package huffmancode;

import java.util.*;

/**
 * @author : sky
 * @version : 1.0
 */
public class HuffmanCode {
    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 = createHuffmanTree(nodes);
        preOrder(huffmanTreeRoot);

        //是否生成了对应的哈夫曼编码
        Map<Byte, String> huffmanCodes = getCodes(huffmanTreeRoot);
        System.out.println("生成的哈夫曼编码表:"+ huffmanCodes);
        //{32=01, 97=100, 100=11000, 117=11001, 101=1110,
        // 118=11011, 105=101, 121=11010, 106=0010, 107=1111, 108=000, 111=0011}

        byte[] zip = zip(contentBytes, huffmanCodes);
        System.out.println(Arrays.toString(zip));//17*/

        //发送哈夫曼编码之后的数组

        byte[] huffmanCodeBytes = huffmanZip(contentBytes);
        System.out.println(Arrays.toString(huffmanCodeBytes));
        System.out.println(huffmanCodeBytes.length);

        //byteToBitString((byte)-1);//11111111111111111111111111111111
        byte[] decode = decode(huffmanCodes, huffmanCodeBytes);
        System.out.println(new String(decode));

    }

    //完成数据的解压

    /**
     *
     * @param huffmanCodes 哈夫曼编码表
     * @param huffmanBytes 哈夫曼编码得到的字节数组
     * @return 返回的就是原来的字符串对应的数组
     */
    private static byte[] decode(Map<Byte,String> huffmanCodes,byte[] huffmanBytes){
        //1.先得到huffmanBytes对应的二进制字符串
        StringBuilder stringBuilder = new StringBuilder();
        //将byte数组转成二进制字符串
        for(int i=0;i<huffmanBytes.length;i++){
            //判断是不是最后一个字节
            boolean flag=(i==huffmanBytes.length-1);
           stringBuilder.append(byteToBitString(!flag,huffmanBytes[i]));
        }
        //把字符串按照指定的哈夫曼编码进行解码
        //把哈夫曼编码表进行调换,因为是反向查询
        Map<String,Byte> map=new HashMap<>();
        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;//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;
    }


    /**
     * //思路:1.将huffmanCodeBytes[-88, -65, -56, -65, -56, -65, -55, 77, -57, 6, -24, -14, -117, -4, -60, -90, 28]
     * // 转成哈夫曼编码对应的二进制字符串
     * //2.二进制字符串对照哈夫曼编码重新转成人能读懂的字符串
     * 将byte转成一个二进制的字符串
     * @param flag 标识是否需要补高位,true 需要补;false 不需要补高位,如果是最后一个字节无需补高位
     * @param b 传入的byte
     * @return 返回的应该是b对应的二进制的字符串(注意是按补码返回的)
     */
    private static String byteToBitString(boolean flag,byte b){
        //使用变量保存b
        int temp=b;//将b转成int
        //如果是正数,我们还需要补高位
        if(flag){
            temp |=256;//按位或256  256=1 0000 0000  | 0000 0001 --》1 0000 0001
        }

        String str = Integer.toBinaryString(temp);//这里返回的是temp对应的二进制的补码

        if(flag){
            return str.substring(str.length()-8);
        }
        return str;
    }

    /**
     * //使用一个方法将前面的方法封装起来,方便调用
     * @param bytes 原始的字符串对应的字节数组
     * @return 经过哈夫曼编码处理后的字节数组,即压缩后的字节数组
     */
    private static byte[] huffmanZip(byte[] bytes){
        //第一步 :把bytes都变成Node对象,并放入集合中
        List<Node> nodes = getNodes(bytes);
        //第二步:创建哈夫曼树
        Node huffmanTreeRoot = createHuffmanTree(nodes);
        //第三步:生成对应的哈夫曼编码(根据哈夫曼树来创建)
        Map<Byte, String> huffmanCodes = getCodes(huffmanTreeRoot);
        //第四步:返回哈夫曼编码的字节数组
        byte[] huffmanCodeBytes = zip(bytes, huffmanCodes);
        return huffmanCodeBytes;
    }

    /**
     *
     * @param bytes 接收一个字节数组
     * @return 返回一个List 形式 node【data=97,weight=5】
     */
    private static List<Node> getNodes(byte[] bytes){
        //1.先创建一个ArrayList
        List<Node> nodes=new ArrayList<>();
        //2.遍历bytes,统计并存储每一个byte出现的次数---->map[key,value]
        Map<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);
            }
        }
        //3.把每个键值对转成一个node对象,并加入到nodes集合中
        for(Map.Entry<Byte,Integer> entry:counts.entrySet()){
            nodes.add(new Node(entry.getKey(),entry.getValue()));
        }
        return nodes;
    }

    //通过List,创建对应的哈夫曼树
    private static Node createHuffmanTree(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.right=rightNode;

            //将处理过的移除
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            //加入
            nodes.add(parent);
        }
        return nodes.get(0);
    }
    //生成的哈夫曼树对应的哈夫曼编码
    //思路:1.将哈夫曼编码表放在Map<Byte,String> 形式 32-01,97-100
    static Map<Byte,String> huffmanCodes=new HashMap<>();
    //2.在生产哈夫曼编码表时,需要去拼接路径,定义一个StringBuilder存储某个叶子节点的路径
    static StringBuilder stringBuilder=new StringBuilder();

    /**
     * 功能:将传入的node节点的所有叶子节点的哈夫曼编码得到,并存放到huffmanCodes集合中
     * @param node 传入的节点
     * @param code 路径:左 0 右 1
     * @param stringBuilder 用来拼接路径
     */
    private static void getCodes(Node node,String code,StringBuilder stringBuilder){
        StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);
        //将code加入到stringBuilder2
        stringBuilder2.append(code);
        if(node!=null){//如果node==null不处理
            //判断当前node是叶子节点还是非叶子节点
            if(node.data==null){//非叶子节点
                //递归处理
                //向左处理
                getCodes(node.left,"0",stringBuilder2);
                //向右递归
                getCodes(node.right,"1",stringBuilder2);
            }else{//说明是叶子节点
                //表示找到了某个叶子节点最后
                huffmanCodes.put(node.data,stringBuilder2.toString());
            }
        }
    }

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


    /**
     * 编写一个方法,将字符串对应的byte[]数组,通过生成的哈夫曼编码表,返回一个哈夫曼编码处理后的byte[]数组
     * @param bytes 原始的字符串对应的byte[]
     * @param huffmanCodes 生成的哈夫曼编码map
     * @return 返回哈夫曼编码处理后的byte[]
     * String content="i like like like java do you like a java";
     * 传入的是:byte[] contentBytes = content.getBytes();
     * 返回的是字符串"1010100010........"对应的byte[] 8位对应一个byte,放入到huffmanCodesBytes中
     * huffmanCodesBytes[0] =10101000(补码)==》真实byte里的数是[10101000-> 10101000-1 -> 10100111(反码) ->11011000]
     * 即huffmanCodesBytes[0]=-88
     */
    private static byte[] zip(byte[] bytes,Map<Byte,String> huffmanCodes){
        //1.利用 huffmanCodes 将 bytes 转成 哈夫曼编码对应的字符串
        StringBuilder stringBuilder = new StringBuilder();
        //遍历bytes数组
        for(byte b:bytes){
            stringBuilder.append(huffmanCodes.get(b));
        }
        //System.out.println("原来的编码"+stringBuilder);
        //1010100010111111110010001011111111001000101111111100100101001101110001110000011011101000111100101000101111111100110001001010011011100
        //2.将"1010100...."转成byte[]
        //统计返回的byte[] huffmanCodeBytes 长度
        //一句话int len=(stringBuilder.length()+7)/8;
        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){//因为是每8位对应一个byte,所以步长为8
            String strByte;
            if(i+8>stringBuilder.length()){//不够8位
                strByte=stringBuilder.substring(i);//直接取到最后
            }else{
                strByte=stringBuilder.substring(i,i+8);
            }
            //将strByte转成一个byte,放入huffmanCodeBytes数组中
            huffmanCodeBytes[index]=(byte)Integer.parseInt(strByte,2);
            index++;
        }
        return huffmanCodeBytes;
    }

    //前序遍历
    public static void preOrder(Node root){
        if(root!=null){
            root.preOrder();
        }
    }
}

//创建Node,带数据和权值
class Node implements Comparable<Node>{
    Byte data;//存放数据本身(字符),比如'a' =>97  ' '=>32
    int weight;//权值,字符出现的次数
    Node left;
    Node right;

    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.right!=null){
            this.right.preOrder();
        }
    }
}

文件压缩

在这里插入图片描述

压缩文件用的哈夫曼编码表是啥?
在这里插入图片描述
在这里插入图片描述

代码解压文件的时候可能有问题

package huffmancode;

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

/**
 * @author : sky
 * @version : 1.0
 */
public class HuffmanCode {
    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 = createHuffmanTree(nodes);
        preOrder(huffmanTreeRoot);

        //是否生成了对应的哈夫曼编码
        Map<Byte, String> huffmanCodes = getCodes(huffmanTreeRoot);
        System.out.println("生成的哈夫曼编码表:"+ huffmanCodes);
        //{32=01, 97=100, 100=11000, 117=11001, 101=1110,
        // 118=11011, 105=101, 121=11010, 106=0010, 107=1111, 108=000, 111=0011}

        byte[] zip = zip(contentBytes, huffmanCodes);
        System.out.println(Arrays.toString(zip));//17*/

        //发送哈夫曼编码之后的数组

        byte[] huffmanCodeBytes = huffmanZip(contentBytes);
        System.out.println(Arrays.toString(huffmanCodeBytes));
        System.out.println(huffmanCodeBytes.length);

        //byteToBitString((byte)-1);//11111111111111111111111111111111
        byte[] decode = decode(huffmanCodes, huffmanCodeBytes);
        System.out.println(new String(decode));

        //压缩文件的测试
        //String srcFile="D://tu.jpg";
        //String dstFile="D://dst.zip";
        //zipFile(srcFile,dstFile);
        //System.out.println("压缩文件成功");
        String zipFile="D://dst.zip";
        String dstFile="D://src.jpg";
        unzipFile(zipFile,dstFile);
        System.out.println("解压成功");

    }


    /**
     * 压缩文件的解压
     * @param zipFile 准备解压的文件
     * @param dstFile 将文件解压到哪里
     */
    public static void unzipFile(String zipFile,String dstFile){
        //创建文件输入流
        FileInputStream fis=null;
        ObjectInputStream ois=null;
        FileOutputStream fos=null;
        try {
            fis=new FileInputStream(zipFile);
            //普通方式读不到?
            ois=new ObjectInputStream(fis);
            //读取byte数组
            byte[] huffmanBytes =(byte[])ois.readObject();
            //读取哈夫曼编码表
            Map<Byte,String> huffmanCodes =(Map<Byte,String>)ois.readObject();
            //解码
            byte[] bytes = decode(huffmanCodes, huffmanBytes);
            //将bytes数组写入到目标文件
            fos=new FileOutputStream(dstFile);
            //写数据到文件中
            fos.write(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                fos.close();
                ois.close();
                fis.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 编写方法,将一个文件进行压缩
     * @param srcFile 传入的希望压缩的文件的全路径
     * @param dstFile 压缩后将压缩文件放到那个目录
     */
    public static void zipFile(String srcFile,String dstFile){
        //创建文件输入流
        FileInputStream fis=null;
        FileOutputStream fos=null;
        ObjectOutputStream oos=null;
        try {
            //创建文件输入流
            fis= new FileInputStream(srcFile);
            //创建一个和源文件大小一样的byte数组
            byte[] b=new byte[fis.available()];//available()返回源文件的大小
            //读取文件
            fis.read(b);
            //使用哈夫曼编码,直接对源文件压缩
            byte[] huffmanBytes = huffmanZip(b);
            //创建文件的输出流,存放压缩文件
            fos=new FileOutputStream(dstFile);
            //创建一个和文件输出流关联的ObjectOutputStream
            oos=new ObjectOutputStream(fos);

            oos.writeObject(huffmanBytes);//先把哈夫曼编码后的数组huffmanBytes写入压缩文件
            //这里我们以对象流的方式写入哈夫曼编码,是为了以后我们恢复源文件时使用
            //注意一定要把哈夫曼编码写入压缩文件
            oos.writeObject(huffmanCodes);

        }catch (Exception e){
            System.out.println(e.getMessage());
        }finally {
            try {
                oos.close();
                fos.close();
                fis.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    //完成数据的解压
    /**
     *
     * @param huffmanCodes 哈夫曼编码表
     * @param huffmanBytes 哈夫曼编码得到的字节数组
     * @return 返回的就是原来的字符串对应的数组
     */
    private static byte[] decode(Map<Byte,String> huffmanCodes,byte[] huffmanBytes){
        //1.先得到huffmanBytes对应的二进制字符串
        StringBuilder stringBuilder = new StringBuilder();
        //将byte数组转成二进制字符串
        for(int i=0;i<huffmanBytes.length;i++){
            //判断是不是最后一个字节
            boolean flag=(i==huffmanBytes.length-1);
           stringBuilder.append(byteToBitString(!flag,huffmanBytes[i]));
        }
        //把字符串按照指定的哈夫曼编码进行解码
        //把哈夫曼编码表进行调换,因为是反向查询
        Map<String,Byte> map=new HashMap<>();
        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;//i直接移动到count的位置
            if(i+count>stringBuilder.length()){
            	String key=stringBuilder.substring(i);
            	break;
            }
        }
        //当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;
    }


    /**
     * //思路:1.将huffmanCodeBytes[-88, -65, -56, -65, -56, -65, -55, 77, -57, 6, -24, -14, -117, -4, -60, -90, 28]
     * // 转成哈夫曼编码对应的二进制字符串
     * //2.二进制字符串对照哈夫曼编码重新转成人能读懂的字符串
     * 将byte转成一个二进制的字符串
     * @param flag 标识是否需要补高位,true 需要补;false 不需要补高位,如果是最后一个字节无需补高位
     * @param b 传入的byte
     * @return 返回的应该是b对应的二进制的字符串(注意是按补码返回的)
     */
    private static String byteToBitString(boolean flag,byte b){
        //使用变量保存b
        int temp=b;//将b转成int
        //如果是正数,我们还需要补高位
        if(flag){
            temp |=256;//按位或256  256=1 0000 0000  | 0000 0001 --》1 0000 0001
        }

        String str = Integer.toBinaryString(temp);//这里返回的是temp对应的二进制的补码

        if(flag){
            return str.substring(str.length()-8);
        }
        return str;
    }

    /**
     * //使用一个方法将前面的方法封装起来,方便调用
     * @param bytes 原始的字符串对应的字节数组
     * @return 经过哈夫曼编码处理后的字节数组,即压缩后的字节数组
     */
    private static byte[] huffmanZip(byte[] bytes){
        //第一步 :把bytes都变成Node对象,并放入集合中
        List<Node> nodes = getNodes(bytes);
        //第二步:创建哈夫曼树
        Node huffmanTreeRoot = createHuffmanTree(nodes);
        //第三步:生成对应的哈夫曼编码(根据哈夫曼树来创建)
        Map<Byte, String> huffmanCodes = getCodes(huffmanTreeRoot);
        //第四步:返回哈夫曼编码的字节数组
        byte[] huffmanCodeBytes = zip(bytes, huffmanCodes);
        return huffmanCodeBytes;
    }

    /**
     *
     * @param bytes 接收一个字节数组
     * @return 返回一个List 形式 node【data=97,weight=5】
     */
    private static List<Node> getNodes(byte[] bytes){
        //1.先创建一个ArrayList
        List<Node> nodes=new ArrayList<>();
        //2.遍历bytes,统计并存储每一个byte出现的次数---->map[key,value]
        Map<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);
            }
        }
        //3.把每个键值对转成一个node对象,并加入到nodes集合中
        for(Map.Entry<Byte,Integer> entry:counts.entrySet()){
            nodes.add(new Node(entry.getKey(),entry.getValue()));
        }
        return nodes;
    }

    //通过List,创建对应的哈夫曼树
    private static Node createHuffmanTree(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.right=rightNode;

            //将处理过的移除
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            //加入
            nodes.add(parent);
        }
        return nodes.get(0);
    }
    //生成的哈夫曼树对应的哈夫曼编码
    //思路:1.将哈夫曼编码表放在Map<Byte,String> 形式 32-01,97-100
    static Map<Byte,String> huffmanCodes=new HashMap<>();
    //2.在生产哈夫曼编码表时,需要去拼接路径,定义一个StringBuilder存储某个叶子节点的路径
    static StringBuilder stringBuilder=new StringBuilder();

    /**
     * 功能:将传入的node节点的所有叶子节点的哈夫曼编码得到,并存放到huffmanCodes集合中
     * @param node 传入的节点
     * @param code 路径:左 0 右 1
     * @param stringBuilder 用来拼接路径
     */
    private static void getCodes(Node node,String code,StringBuilder stringBuilder){
        StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);
        //将code加入到stringBuilder2
        stringBuilder2.append(code);
        if(node!=null){//如果node==null不处理
            //判断当前node是叶子节点还是非叶子节点
            if(node.data==null){//非叶子节点
                //递归处理
                //向左处理
                getCodes(node.left,"0",stringBuilder2);
                //向右递归
                getCodes(node.right,"1",stringBuilder2);
            }else{//说明是叶子节点
                //表示找到了某个叶子节点最后
                huffmanCodes.put(node.data,stringBuilder2.toString());
            }
        }
    }

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


    /**
     * 编写一个方法,将字符串对应的byte[]数组,通过生成的哈夫曼编码表,返回一个哈夫曼编码处理后的byte[]数组
     * @param bytes 原始的字符串对应的byte[]
     * @param huffmanCodes 生成的哈夫曼编码map
     * @return 返回哈夫曼编码处理后的byte[]
     * String content="i like like like java do you like a java";
     * 传入的是:byte[] contentBytes = content.getBytes();
     * 返回的是字符串"1010100010........"对应的byte[] 8位对应一个byte,放入到huffmanCodesBytes中
     * huffmanCodesBytes[0] =10101000(补码)==》真实byte里的数是[10101000-> 10101000-1 -> 10100111(反码) ->11011000]
     * 即huffmanCodesBytes[0]=-88
     */
    private static byte[] zip(byte[] bytes,Map<Byte,String> huffmanCodes){
        //1.利用 huffmanCodes 将 bytes 转成 哈夫曼编码对应的字符串
        StringBuilder stringBuilder = new StringBuilder();
        //遍历bytes数组
        for(byte b:bytes){
            stringBuilder.append(huffmanCodes.get(b));
        }
        //System.out.println("原来的编码"+stringBuilder);
        //1010100010111111110010001011111111001000101111111100100101001101110001110000011011101000111100101000101111111100110001001010011011100
        //2.将"1010100...."转成byte[]
        //统计返回的byte[] huffmanCodeBytes 长度
        //一句话int len=(stringBuilder.length()+7)/8;
        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){//因为是每8位对应一个byte,所以步长为8
            String strByte;
            if(i+8>stringBuilder.length()){//不够8位
                strByte=stringBuilder.substring(i);//直接取到最后
            }else{
                strByte=stringBuilder.substring(i,i+8);
            }
            //将strByte转成一个byte,放入huffmanCodeBytes数组中
            huffmanCodeBytes[index]=(byte)Integer.parseInt(strByte,2);
            index++;
        }
        return huffmanCodeBytes;
    }

    //前序遍历
    public static void preOrder(Node root){
        if(root!=null){
            root.preOrder();
        }
    }
}

//创建Node,带数据和权值
class Node implements Comparable<Node>{
    Byte data;//存放数据本身(字符),比如'a' =>97  ' '=>32
    int weight;//权值,字符出现的次数
    Node left;
    Node right;

    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.right!=null){
            this.right.preOrder();
        }
    }
}

二叉排序树

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package binarysorttree;

/**
 * @author : sky
 * @version : 1.0
 */
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 = 0; i < arr.length; i++) {
            binarySortTree.add(new Node(arr[i]));
        }
        //中序遍历二叉排序树
        binarySortTree.infixOrder();//1 3 5 7 9 10 12
    }
}

//创建节点
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();
        }
    }

}

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

    //添加节点的方法
    public void add(Node node){
       if(root==null){
           root=node;//如果root为空,则直接让root指向node
       } else{
           root.add(node);
       }
    }

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

二叉排序树的删除节点

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

或者(3)从targetNode的左子树找到最大的节点

package binarysorttree;

/**
 * @author : sky
 * @version : 1.0
 */
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 = 0; i < arr.length; i++) {
            binarySortTree.add(new Node(arr[i]));
        }
        binarySortTree.add(new Node(0));
        //中序遍历二叉排序树
        binarySortTree.infixOrder();//1 3 5 7 9 10 12

        //测试删除叶子节点
        //binarySortTree.deleteNode(2);
        System.out.println("-----------------");
        //binarySortTree.infixOrder();
        //测试删除一棵子树的节点
        //binarySortTree.deleteNode(1);
        //测试删除有两棵子树的节点
        binarySortTree.deleteNode(7);
        binarySortTree.infixOrder();
    }
}

//创建节点
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();
        }
    }


    /**
     * 查找要删除的节点
     * @param value 希望删除的节点的值
     * @return 如果找到返回该节点,否则返回null
     */
    public Node search(int value){
        if(value==this.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;//没有找到父节点
            }
        }
    }
}

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

    //添加节点的方法
    public void add(Node node){
       if(root==null){
           root=node;//如果root为空,则直接让root指向node
       } else{
           root.add(node);
       }
    }

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

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

    //找最大值
    public int delLeftTreeMax(Node node){
        Node target=node;
        //循环查找右子节点,找到最大值
        while(target.right!=null){
            target=target.right;
        }
        deleteNode(target.value);
        return target.value;
    }

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

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

    //删除节点
    public void deleteNode(int value){
        if(root==null){
            return;
        }else{
            //1.先找到要删除的节点targetNode
            Node targetNode = search(value);
            //如果没有找到要删除的节点
            if(targetNode==null){
                return;
            }
            //如果targetNode没有父节点,或当前二叉排序树只有一个节点
            if(root.left==null && root.right==null){
                root=null;
                return;
            }
            //2.找到targetNode的父节点
            Node parent = searchParent(value);

            //3.如果要删除的节点是叶子节点
            if(targetNode.left==null&&targetNode.right==null){
                //判断targetNode是parent的左子节点还是右子节点
                if(parent.left!=null && parent.left.value==value){
                    parent.left=null;
                }else if(parent.right!=null && parent.right.value==value){
                    parent.right=null;
                }
                //4.删除有两棵子树的节点
            }else if(targetNode.left!=null && targetNode.right!=null){
                //有两种方式:删除右子树最小值的节点或删除左子树最大值的节点
                /*int minVal = delRightTreeMin(targetNode.right);
                targetNode.value=minVal;*/
                int maxVal=delLeftTreeMax(targetNode.left);
                targetNode.value=maxVal;
            }else{//5.删除只有一棵子树的节点
                //如果要删除的节点有左子节点
                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;
                    }
                }
            }
        }
    }
}

平衡二叉树(AVL树)

在这里插入图片描述
在这里插入图片描述

左旋转

当右子树的高度比左子树的高度高时,进行左旋转

左旋转的目的:降低右子树的高度
在这里插入图片描述
在这里插入图片描述

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

右旋转

在这里插入图片描述
在这里插入图片描述

左子树的高度大于右子树的高度

右旋转:目的是为了降低左子树的高度,从而达到一个平衡

//右旋转的方法
private void rightRotate(){
    //以当前节点的值创建一个新节点
    Node newNode = new Node(value);
    //把新节点的右子树指向当前节点的右子树
    newNode.right=right;
    //新节点的左子节点指向当前节点的左子节点的右子节点
    newNode.left=left.right;
    //把当前节点的值改为当前节点左子节点的值
    value=left.value;
    //把当前节点的左子节点指向当前节点的左子节点的左子节点
    left=left.left;
    //把当前节点的右子节点指向新的节点
    right=newNode;
}

双旋转

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

全部代码:

package avltree;


/**
 * @author : sky
 * @version : 1.0
 */
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 = 0; i <arr.length ; i++) {
            avlTree.add(new Node(arr[i]));
        }
        avlTree.infixOrder();
        System.out.println("-------------------");
        System.out.println("平衡后树的高度:"+avlTree.getRoot().height());
        System.out.println("平衡后树的左子树的高度:"+avlTree.getRoot().left.height());
        System.out.println("平衡后树的右子树的高度:"+avlTree.getRoot().right.height());
        System.out.println(avlTree.getRoot());
        System.out.println(avlTree.getRoot().left);
        System.out.println(avlTree.getRoot().right);
    }
}

class AVLTree{
    private Node root;

    public Node getRoot() {
        return root;
    }

    //添加节点的方法
    public void add(Node node){
        if(root==null){
            root=node;//如果root为空,则直接让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);
            }
        }

        //当添加完一个节点后,如果 右子树的高度-左子树的高度 >1,左旋转
        if(rightHeight()-leftHeight()>1){
            //如果当前节点的右子节点的左子树的高度大于右子树的高度
            if(right!=null && right.leftHeight()>right.rightHeight()){
                //先对右子节点进行右旋转
                right.rightRotate();
                //在对当前节点进行左旋转
                leftRotate();
            }else{//否则直接左旋转
                leftRotate();
            }
            return;//必须要,加一个处理一个
        }
        //当添加完一个节点后,如果 左子树的高度-右子树的高度 >1,右旋转
        if(leftHeight()-rightHeight()>1){
            //先要判断左子节点的右子树的高度大于左子树的高度
            if(left!=null && left.rightHeight()>left.leftHeight()){
                //先对当前节点的左节点进行左旋转
                left.leftRotate();
                //再对当前节点进行右旋转
                rightRotate();
            }else{//直接进行右旋转即可
                rightRotate();
            }
        }
    }

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

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

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

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

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

    //右旋转的方法
    private void rightRotate(){
        //以当前节点的值创建一个新节点
        Node newNode = new Node(value);
        //把新节点的右子树指向当前节点的右子树
        newNode.right=right;
        //新节点的左子节点指向当前节点的左子节点的右子节点
        newNode.left=left.right;
        //把当前节点的值改为当前节点左子节点的值
        value=left.value;
        //把当前节点的左子节点指向当前节点的左子节点的左子节点
        left=left.left;
        //把当前节点的右子节点指向新的节点
        right=newNode;
    }
}

多路查找树

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2-3树(B树)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

B树、B+树、B*树

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值