算法04 线索二叉树 不是重点 霍夫曼树和霍夫曼编码

堆排序

https://www.kuangstudy.com/bbs/1402601383655378946

线索二叉树 不是重点

本质

二叉树的遍历本质上是将一个复杂的非线性结构转换为线性结构,使每个结点都有了唯一前驱和后继(第一个结点无前驱,最后一个结点无后继)。对于二叉树的一个结点,查找其左右子女是方便的,其前驱后继只有在遍历中得到。为了容易找到前驱和后继,有两种方法。一是在结点结构中增加向前和向后的指针,这种方法增加了存储开销,不可取;二是利用二叉树的空链指针。

存储结构

线索二叉树中的线索能记录每个结点前驱和后继信息。为了区别线索指针和孩子指针,在每个结点中设置两个标志ltag和rtag。

当tag和rtag为0时,leftChild和rightChild分别是指向左孩子和右孩子的指针;否则,leftChild是指向结点前驱的线索(pre),rightChild是指向结点的后继线索(suc)。由于标志只占用一个二进位,每个结点所需要的存储空间节省很多。 [3]

现将二叉树的结点结构重新定义如下:

lchildltagdatartagrchild

其中:ltag=0 时lchild指向左儿子;ltag=1 时lchild指向前驱;rtag=0 时rchild指向右儿子;rtag=1 时rchild指向后继。

讲的太乱 实现错误 ,以后再补;

霍夫曼树 重点

给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

哈夫曼树又称为最优树.

1、路径和路径长度

在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。

2、结点的权及带权路径长度

若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。

3、树的带权路径长度

树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。

wpl最小成为霍夫曼树

霍夫曼树的实现

思路图

节点类

package suanfa03;

public class node implements Comparable<node> {
    int data;
    node left;
    node right;

    public node(int data) {
        this.data = data;
    }

    @Override
    public int compareTo(node o) {
        return -(this.data-o.data);
    }

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

实现类

package suanfa03;

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

import static org.junit.jupiter.api.Assertions.*;

class nodeTest {
    public static void main(String[] args) {
        int[] arr={8,11,23,29,14,7,3,5};
        node node = huFMan(arr);
        System.out.println(node);

    }
    public static node huFMan(int[] arr){
        //将数组分割为一个个二叉树,存入集合
        ArrayList<node> nodes = new ArrayList<>();
        for (int node:arr){
            nodes.add(new node(node));
        }

        // 循环进行 ==》  排序 / 取出最小的二叉树/移除/ 放入原来的集合
        while (nodes.size()>1){
            Collections.sort(nodes);
            //取出最后两个节点
            node left= nodes.get(nodes.size()-1);
            node right= nodes.get(nodes.size()-2);
            node parent= new node(left.data+right.data);
            nodes.remove(left);
            nodes.remove(right);
            nodes.add(parent);

        }
//        System.out.println(nodes);
        return nodes.get(0);
    }

}

霍夫曼编码

长度396

长度122 压缩70%

霍夫曼编码实现

节点类

package suanf04;

public class zipNode implements Comparable<zipNode> {
    //可能为空所以使用封装类
    Byte data;
    //权重
    int weight;
    //左右 节点
    zipNode left ;
    zipNode right;

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

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

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

霍夫曼编码

package suanf04;

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

public class zipHuffMan {
    public static void main(String[] args) {
/*        String msg= "can you can a can as a can canner can a can.";
//        //我们编码的不是具体字符串而是具体是数组;
        byte[] bytes = msg.getBytes();
//        //进行huffman 编码
        byte[] bytes1 = huffMan(bytes);
//        System.out.println(bytes.length);
//        System.out.println(bytes1.length);
        byte[] reByte= reZip(bytes1,huffCode);
        System.out.println(new String(reByte));*/
        String input= "F:\\A-所有学习的代码测试\\interview\\first01\\25.docx";
        String ouput="8.zip";
        try {
            zipFile(input,ouput);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            unZip("F:\\A-所有学习的代码测试\\interview\\8.zip","81.docx");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void unZip(String input,String output) throws IOException, ClassNotFoundException {
        //创建一个输入流,用对象流获取
        FileInputStream fos = new FileInputStream(input);
        ObjectInputStream oos = new ObjectInputStream(fos);
        // 读取byte数组
        byte[] bytes = (byte[]) oos.readObject();
        //获取霍夫曼编码表,关闭流
        Map<Byte, String> byteStringMap = (Map<Byte, String>) oos.readObject();
        oos.close();
        fos.close();
        //解码 /创建输出流 /写出数据
        byte[] zip = zip(bytes, byteStringMap);
        FileOutputStream fileOutputStream = new FileOutputStream(output);
        fileOutputStream.write(zip);

    }

    public static void zipFile(String input,String output) throws IOException {
        //创建一个输入流
        FileInputStream fileInputStream = new FileInputStream(input);
        //创建一个输入流指向 存入一个大小相同的byte数组
        byte[] bytes = new byte[fileInputStream.available()];
        //读取文件内容
        fileInputStream.read(bytes);
        fileInputStream.close();
        //使用霍夫曼进行编码
        byte[] huffZip = huffMan(bytes);
        //输出流
        FileOutputStream fileOutputStream = new FileOutputStream(output);
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(huffZip);
        objectOutputStream.writeObject(huffCode);
        objectOutputStream.close();
        fileInputStream.close();
    }


    static Map<Byte,String> huffCode=new HashMap<>();
    private static byte[] huffMan(byte[] bytes) {
        //统计每一个字符出现的次数,放入一个集合,
        List<zipNode> nodes = getNodes(bytes);
        //船舰一个霍夫曼树
        zipNode tree = getHuffMantree(nodes);
//        System.out.println(tree);
        //床架你一个霍夫曼编码表
        huffCode=getCodes(tree);
//        System.out.println(huffCode);
        //编码
        byte[] b =zip(bytes,huffCode);
//        System.out.println(b);
        //使用霍夫曼编码进行解压缩

        return b;
    }

    private static byte[] reZip(byte[] b, Map<Byte, String> huffCode) {
        StringBuilder sb= new StringBuilder();

        for (int i = 0; i <b.length ; i++) {
            byte b1 = b[i];
            //判断是否是最后一个
            boolean b2 = (i == b.length - 1);
            //是最后一个取反
            sb.append(byteToEight(!b2,b1));
        }
            //传入的 byte类型的数字 转化为int 就是三十2 位 需要调整
//            String s =Integer.toBinaryString(b1);
//        System.out.println(sb);

        //使用霍夫曼编码进行解码 ,
        // 1.键值对调换
//        System.out.println(huffCode);
        Map<String,Byte> re =new HashMap<>();
        huffCode.forEach((a,c)->{
            re.put(c,a);
        });
//        System.out.println(re);
        //创建一个集合存储不定长的byte
        List<Byte> list= new ArrayList<>();
        //处理字符串
        for (int i = 0; i <sb.length() ;) {
            int count= 1 ;
            boolean flag=true;
            Byte aByte=null;
            while (flag){
                String key =sb.substring(i,i+count);
                aByte = re.get(key);
                if (aByte==null){
                    count++;
                }else flag=false;
            }
//            System.out.println(aByte);
            list.add(aByte);
            i+=count;
        }
//        System.out.println(list);
        //list 集合转为数组
        byte[] bz = new byte[list.size()];
        for (int i = 0; i < bz.length; i++) {
            bz[i]=list.get(i);
        }
        return bz;
    }

    private static String byteToEight(boolean flag,byte b1) {
        int temp = b1;
        //-1 的值是-1的正确表示应该是1111 1111  因为C语言里,对整型数是采用Two’s complement表示法,
        // 而前面我的理解则是Sign-Magnitude表示法(浮点数采用该法)。
        // 在Two’s complement表示法里,1000 0001表示的是-127。
        if (flag){
            temp|=256;
        }
        String s = Integer.toBinaryString(temp);
        if (flag){
//        System.out.println(s.length());
//        System.out.println(s);
            //      return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
            return s.substring(s.length()-8);
        }else {
            return s;
        }
    }
    /**
     * 进行霍夫曼变化
     * @param bytes
     * @param huffCode
     * @return
     */
    private static byte[] zip(byte[] bytes, Map<Byte, String> huffCode) {
        StringBuilder sb =new StringBuilder();
        for (byte aByte : bytes) {
            sb.append(huffCode.get(aByte));
        }
//        System.out.println(sb);
        int len;
        if (sb.length()%8==0){
            len=sb.length()/8;
        }else {
            len=sb.length()/8+1;
        }
        //压缩后的byte
        byte[] a= new byte[len];
        int index=0;
        for (int i = 0; i <sb.length() ; i+=8) {
            String str;
            if (i+8>sb.length()){
                str=sb.substring(i);
            }else {
                str=sb.substring(i,i+8);
            }
//            System.out.println(str);
            //Integer的10111010 二进制 转化十进制
            byte b = (byte) Integer.parseInt(str, 2);
//            System.out.println(str+"+"+b);
            a[index]=b;
            index++;
        }
        return a;

    }

    static StringBuilder sb=new StringBuilder();
    static Map<Byte,String> bs=new HashMap<>();
    private static Map<Byte, String> getCodes(zipNode tree) {
        if (tree==null){return  null;}
        getCode(tree.left,"0",sb);
        getCode(tree.right,"1",sb);
        return bs;
    }

    private static void getCode(zipNode node, String code, StringBuilder sb) {
        StringBuilder sb2=new StringBuilder(sb);
        sb2.append(code);
        if (node.data==null){
            getCode(node.left,"0",sb2);
            getCode(node.right,"1",sb2);
        }else {
            bs.put(node.data,sb2.toString());
        }

    }

    private static zipNode getHuffMantree(List<zipNode> nodes) {
        while (nodes.size()>1){
            //排序,权值大在前
            Collections.sort(nodes);
            zipNode left = nodes.get(nodes.size() - 1);
            zipNode right = nodes.get(nodes.size() - 2);
            zipNode zipNode = new zipNode(null, left.weight + right.weight);
            //取出二叉树变成新的树的孩子
            zipNode.left=left;
            zipNode.right=right;
            //删除原先的二叉树
            nodes.remove(left);
            nodes.remove(right);
            //把新树加入 原本的集合中
            nodes.add(zipNode);
        }

        return nodes.get(0);
    }

    //把byte数组转为弄得集合
    private static List<zipNode> getNodes(byte[] bytes) {
        List<zipNode> nodes =new ArrayList<>();
        //存储每一个出现多少次
        HashMap<Byte, Integer> count = new HashMap<>();
        for (byte aByte : bytes) {
            //每次先从 hashmap数组中获取 对应的byte
            Integer integer = count.get(aByte);
            //不存在赋予一个
            if (integer==null){
                count.put(aByte,1);
            }else {
                //存在数量加一
                count.put(aByte,integer+1);
            }
        }
//        System.out.println(count);
        //把每一个键值对 变成 node
        count.forEach((a,b)->{
            nodes.add(new zipNode(a,b));
        });


//        nodes.forEach(a->{
//            System.out.println(a.data);
//        });
        return nodes;
    }
}

这里成功了这没有成功 ,成功压缩了,到会导致乱码

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值