X16数据结构部分08

构建哈夫曼树

package du;

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

public class m17 {
    /*
    哈夫曼树概述
        也叫赫夫曼树
        如果树的带权路径长度(wpl)最小
        那么就是最优二叉树
        也叫哈夫曼树
        权值较大的节点离根最近
        权就是该节点对应的值

    相关概念
        路径
            一个节点往下可以达到子节点或孙子节点的通路
        路径长度
            通路中的分支数目叫做路径长度
            如果根节点层数为1
            那么到达L层路径长度为L-1
            说通俗点就是线的个数
        带权路径长度
            路径长度与该节点权的乘积
        树的带权路径长度
            所有叶子节点带权路径长度之和
            wpl最小就是哈夫曼树
     */

    /*
    哈夫曼树构建步骤
        先上题
        给定数列{13,7,8,3,29,6,1}
        要求转换成哈夫曼树
        这个数列的每一个值最终都会成为叶子节点
    构建哈夫曼树的步骤
        * 从小到大排序,每个数据看作一个节点
            每个节点看作最简单的二叉树,也就是左右指针都为空的
        * 取出根节点权值最小的2个二叉树
        * 2个节点作为左子节点和右子节点,构建一棵新的二叉树,
            新二叉树权值就是两个子节点权值的和
        * 然后这棵新二叉树的权值将替换掉原来前两个位置的权值
            继续进行从小到大排序构建树的步骤
     */
    public static void main(String[] args) {
        int[] array = {13, 7, 8, 3, 29, 6, 1};
        Node root = createHuffmanTree(array);
        preOrderTraversal(root);
    }

    /**
     * 构建哈夫曼树的方法
     * @param array 构建树需要的数组
     * @return 哈夫曼树的根节点
     */
    private static Node createHuffmanTree(int[] array) {
        /*
        程序开始运行
        遍历数组
        将数组中每一个元素构建成Node
        再将Node放入到ArrayList中
        便于管理
        并进行排序
         */
        ArrayList<Node> arrayList = new ArrayList<>();
        for (int i : array) {
            arrayList.add(new Node(i));
        }
        while (arrayList.size() > 1) {
            /*
            程序进入while循环
            此时数组长度是2及以上
            可以构建新的二叉树
             */
            Collections.sort(arrayList);
            /*
            程序运行到此处
            可以进行代码测试
            判断是否已经排好序
            测试通过
            可以进行第2步
            取出权值最小的两个节点
            构建二叉树
             */
            Node node1 = arrayList.get(0);
            Node node2 = arrayList.get(1);
            Node node = new Node(node1.weighting + node2.weighting);
            node.leftNode = node1;
            node.rightNode = node2;
            /*
            程序运行到此处
            说明新二叉树已经构架好了
            需要删除已经处理过的二叉树
            再把新构建的树加入ArrayList集合
            此时新构建的节点放在了最后一位
            需要用while循环处理
             */
            arrayList.remove(node1);
            arrayList.remove(node2);
            arrayList.add(node);
            /*
            程序运行到此处
            可以进行代码测试
            测试第1处理是否成功
             */
        }
        /*
        程序运行到此处
        哈夫曼树构建完成
        返回根节点
         */
        return arrayList.get(0);
    }

    /**
     * 遍历哈夫曼树
     * @param root 树的根节点
     */
    public static void preOrderTraversal(Node root) {
        if (root != null) {
            root.preOrderTraversal();
        }else {
             throw new Error("Empty Huffman Tree");
        }
    }
}

/**
 * @author main stack frame
 * <p>
 * Node class
 * Node类需要支持排序
 * 实现Comparable接口
 */
class Node implements Comparable<Node> {
    /*
    节点权值
    左子节点
    右子节点
     */
    int weighting;
    Node leftNode;
    Node rightNode;

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

    /**
     * 前序遍历
     */
    public void preOrderTraversal() {
        System.out.println(this);
        if (this.leftNode != null) {
            this.leftNode.preOrderTraversal();
        }
        if (this.rightNode != null) {
            this.rightNode.preOrderTraversal();
        }
    }

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

    @Override
    public int compareTo(Node o) {
        /*
        从小到大进行排序
         */
        return this.weighting - o.weighting;
    }
}

二叉排序树

class Test21 {
    /*
    给你一个数列 {7,3,10,12,5,1,9}
    要求能够高效完成数据的查询和添加

    二叉排序树(BST)可以让检索插入删除效率较高
        对于二叉排序树的所有非叶子节点
        要求左子树节点值比当前值小
        右子节点比当前节点值大

        检索效率较高
        类似于二分法查找

        插入元素不会导致元素的移动
        所以插入速度也是非常快的
     */
    public static void main(String[] args) {
        int[] array = {7,3,10,12,5,1,9};
        BinarySortedTree binarySortedTree = new BinarySortedTree();
        for (int i = 0; i < array.length; i++) {
            binarySortedTree.insertNode(new Node1(array[i]));
        }
        binarySortedTree.middleOrderTraversal();
        /*
        此处运行中序遍历的方法
        正好是升序排列的数列
         */
    }
}

class BinarySortedTree {
    private Node1 root;

    /**
     * 插入元素的方法
     * @param node1 需要插入的元素
     */
    public void insertNode(Node1 node1){
        if (root == null) {
            root = node1;
        }else {
            root.insertNode(node1);
        }
    }

    /**
     * 中序遍历的方法
     */
    public void middleOrderTraversal(){
        if (root != null) {
            root.middleOrderTraversal();
        }else {
            throw new Error("Empty Binary Sorted Tree");
        }
    }
}

/*
节点类
 */
class Node1 {
    int value;
    Node1 leftNode;
    Node1 rightNode;

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

    /**
     * 插入节点的方法
     * @param node 需要插入的节点
     */
    public void insertNode(Node1 node) {
        if (node == null) {
            return;
        }
        /*
        程序运行到此处
        此时可以判断插入的节点和根节点的关系
        this.value表示根节点的值
         */
        if (node.value < this.value) {
            if (this.leftNode == null) {
                /*
                程序运行到此处
                说明当前插入的节点比根节点的值小
                并且根节点的左子树位空
                直接插入即可
                 */
                this.leftNode = node;
            }else {
                this.leftNode.insertNode(node);
            }
        }else {
            /*
            程序运行到此处
            说明当前节点比根节点大
            需要插入到右子树
             */
            if (this.rightNode == null) {
                this.rightNode = node;
            }else {
                this.rightNode.insertNode(node);
            }
        }
    }

    /**
     * 中序遍历的方法
     */
    public void middleOrderTraversal(){
        if (this.leftNode != null) {
            this.leftNode.middleOrderTraversal();
        }
        System.out.println(this);
        if (this.rightNode != null) {
            this.rightNode.middleOrderTraversal();
        }
    }

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

二叉树删除节点思路分析

/*
    二叉排序树删除节点的3种情况
        第1种情况:删除叶子节点
            先找到需要删除的节点targetNode
            再找到需要删除节点的父节点
            确定targetNode是parent的左子节点还是右子节点
            然后根据情况来删除就行了
            parent.leftNode/rightNode = null;
        第2种情况:删除只有1棵子树的节点
            前两步和第1种情况一样
            多加一个步骤是判断子树是左/右子节点
            再多加一个步骤是判断targetNode是parentNode
                的左/右字节点
            目前来看有4种情况
                parent.leftNode = targetNode.leftNode
                parent.leftNode = targetNode.rightNode
                parent.rightNode = targetNode.leftNode
                parent.rightNode = targetNode.rightNode
        第3种情况:删除有2棵子树的节点
            前两步和第1种情况一样
            找到targetNode右子树中最小的节点
            用一个临时变量保存temp = 12
            再删除12这个节点
            然后再
            targetNode.value = temp
     */

删除叶子节点代码实现

	// 下面2个方法插入到Node类里面
	/**
     * 查找需要删除的节点
     * @param value 需要删除节点的值
     * @return 需要删除的节点
     */
    public Node1 findNodeToBeDelete(int value){
        if (value == this.value) {
            return this;
        }else if (value < this.value) {
            /*
            程序运行到此处
            说明当前需要查找的值
            小于当前节点(第1次一定是根节点)
            需要向左子树遍历
            同时需要判断左子节点是否为空
             */
            if (this.leftNode == null) {
                return null;
            }
            this.leftNode.findNodeToBeDelete(value);
        }else {
            /*
            程序运行到此处
            说明当前需要查找的值
            大于当前节点(第1次一定是根节点)
            需要向右子树遍历
            同时需要判断右子节点是否为空
             */
            if (this.rightNode == null) {
                return null;
            }
            this.rightNode.findNodeToBeDelete(value);
        }
        return null;
    }

    /**
     * 查找需要删除节点的父节点
     * @param value 需要删除节点
     * @return 需要删除节点的父节点
     */
    public Node1 parentOfNodeToBeDelete(int value){
        if ((this.leftNode != null && this.leftNode.value == value) ||
                (this.rightNode != null && this.rightNode.value == value)) {
            /*
            程序运行到此处
            说明找到了需要删除节点的父节点
            直接返回即可
             */
            return this;
        }else {
            if (value < this.value && this.leftNode != null) {
                /*
                程序运行到此处
                查找的节点小于当前节点
                需要向左递归查找
                 */
                return this.leftNode.parentOfNodeToBeDelete(value);
            } else if (value >= this.value && this.rightNode != null) {
                /*
                程序运行到此处
                查找的节点大于等于当前节点
                需要向左递归查找
                加等号的原因是
                在添加方法的时候
                如果插入元素等于当前元素
                是放在右子树的
                 */
                return this.rightNode.parentOfNodeToBeDelete(value);
            }else {
                /*
                程序运行到此处
                说明当前递归程序左右子树有可能为空
                不满足需要递归的条件
                 */
                return null;
            }
        }
    }
	// 下面3个方法插入到BinarySortedTree类里面
	/**
     * 查找需要删除的节点
     * @param value
     * @return
     */
    public Node1 findNodeToBeDelete(int value){
        if (root == null) {
            return null;
        }else {
            return root.findNodeToBeDelete(value);
        }
    }

    /**
     * 查找需要删除节点的父节点
     * @param value
     * @return
     */
    public Node1 parentOfNodeToBeDelete(int value) {
        if (root == null) {
            return null;
        }else {
            return root.parentOfNodeToBeDelete(value);
        }
    }

    /**
     * 删除叶子节点的方法
     * @param value 需要删除节点的值
     */
    public void deleteNode(int value){
        if (root == null) {
            return;
        }else {
            Node1 targetNode = findNodeToBeDelete(value);
            /*
            程序开始运行
            首先查找要删除的节点和需要删除节点的父节点
            做两层安全控制
             */
            if (targetNode == null) {
                return;
            }
            if (root.leftNode == null && root.rightNode == null) {
                root = null;
                return;
            }

            Node1 parentOftNode = parentOfNodeToBeDelete(value);
            if (targetNode.leftNode == null && targetNode.rightNode == null) {
                /*
                程序能够进入if语句当中
                说明当前需要删除的节点是叶子节点
                此时需要判断targetNode是
                父元素的左子节点还是右子节点
                 */
                if (parentOftNode.leftNode != null
                        && parentOftNode.leftNode.value == value) {
                    parentOftNode.leftNode = null;
                } else if (parentOftNode.rightNode != null
                        && parentOftNode.rightNode.value == value) {
                    parentOftNode.rightNode = null;
                }
            }
        }
    }

删除节点综合代码实现

	/**
     * 删除节点的方法
     * @param value 需要删除节点的值
     */
    public void deleteNode(int value){
        if (root == null) {
            return;
        }else {
            Node1 targetNode = findNodeToBeDelete(value);
            /*
            程序开始运行
            首先查找要删除的节点和需要删除节点的父节点
            做两层安全控制
             */
            if (targetNode == null) {
                return;
            }
            if (root.leftNode == null && root.rightNode == null) {
                root = null;
                return;
            }

            Node1 parentOftNode = parentOfNodeToBeDelete(value);
            if (targetNode.leftNode == null && targetNode.rightNode == null) {
                /*
                程序能够进入if语句当中
                说明当前需要删除的节点是叶子节点
                此时需要判断targetNode是
                父元素的左子节点还是右子节点
                 */
                if (parentOftNode.leftNode != null
                        && parentOftNode.leftNode.value == value) {
                    parentOftNode.leftNode = null;
                } else if (parentOftNode.rightNode != null
                        && parentOftNode.rightNode.value == value) {
                    parentOftNode.rightNode = null;
                }
            } else if (targetNode.leftNode != null && targetNode.rightNode != null) {
                /*
                程序运行到此处
                说明该节点有2棵子树
                 */
                targetNode.value  = deleteMinimumValueOfRightSubtree(targetNode.rightNode);
            } else {
                /*
                程序运行到此处
                经过排除
                该节点只有1棵子树
                注意需要判空操作
                注意一共有4种情况
                 */
                if (targetNode.leftNode != null) {
                    /*
                    程序运行到此处
                    说明当前节点有1棵左子树
                     */
                    if (parentOftNode.leftNode.value == value) {
                        /*
                        程序运行到此处
                        说明当前节点是父节点的
                        左子节点
                         */
                        parentOftNode.leftNode = targetNode.leftNode;
                    }else {
                        /*
                        程序运行到此处
                        说明当前节点是父节点的
                        右子节点
                         */
                        parentOftNode.rightNode = targetNode.leftNode;
                    }
                }else {
                    /*
                    程序运行到此处
                    说明当前节点有1棵右子树
                     */
                    if (parentOftNode.leftNode.value == value) {
                        parentOftNode.leftNode = targetNode.rightNode;
                    }else {
                        parentOftNode.rightNode = targetNode.rightNode;
                    }
                }
            }
        }
    }

    /**
     * 辅助方法
     * 删除右子树的最小值
     * @param node 传入的根节点
     * @return 以node作为根节点二叉排序树最小值
     */
    public int deleteMinimumValueOfRightSubtree(Node1 node) {
        Node1 target = node;
        /*
        程序运行到此处
        此时会根据二叉排序树左小右大的原则
        一直向左子树方向查找
        最左下角位置的值为最小值

        这里也可以删除左子树最大的
         */
        while (target.leftNode != null) {
            target = target.leftNode;
        }
        /*
        程序运行到此处
        此时target指向最小节点
         */
        deleteNode(target.value);
        return target.value;
    }

B树,B+树和B*树

/*
    二叉树问题分析
        问题1:在构建二叉树时,如果数据量巨大,需要进行多次IO操作
        问题2:海量节点,会造成树的高度很大,降低操作速度

    B树介绍
        实际上是1种多叉树
        B树通过重新组织节点,降低树的高度,减少IO操作,提高效率
    2-3树
        所有叶子节点都在同一层,所有B树都满足这个条件
        有2个子节点的节点叫做2节点,2节点要么没有子树,要么有2个子节点
        有3个子节点的节点叫做3节点,3节点要么没有子树,要么有3个子节点
        3节点左边的值小于当前节点左边的数据项,中间的值介于2个数据项之间,右边的值大于2个数据项
        2-3树要比二叉树效率要高
        代码实现非常复杂
    2-3-4树
        和2-3树相似
        允许4节点的存在
        这里就不具体说明了

    B树
        B树的阶:节点最多子节点的个数,2-3树的阶为3,2-3-4树的阶为4
        B树的搜索:从根节点开始,进行n分查找,命中则结束,否则进入所属范围的儿子节点,直到所指向的儿子节点为空
    B+树
        所有真实数据全部在叶子节点,也叫做稠密索引
        叶子节点实际上是1串1串的链表,并且链表之间是有序的
        不可能在非叶子节点命中,非叶子节点相当于叶子节点的索引,也叫做稀疏索引
        更加适合文件索引系统
        类似于哈希表
        但相对于哈希表而言是有序的
        相对于B树砍掉的数据块更多
    B*树
        在B+树的基础上
        非叶子节点添加1个指针指向兄弟节点
     */

总目录

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

muskfans

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值