huffman算法的简单实现

javascript/jquery

浏览数:189

2019-8-21

简介

昨天刚拿到的需求,要求用二叉树设计一个huffman算法,主要用于压缩数据,就简单的用javascript实现了一个,在实现的过程中,画树进行的很顺利,但在测试编解码的时候出现了一些问题,也是自己对huffman算法的理解错误,好在及时改正。

算法实现

对huffman算法实现原理如果不了解的话最好自己去研究一下在看,否则可能有些地方看不懂,在这不做具体说明。
废话到此为止了,直接上代码,如果认为对自己有用的,请点个赞,谢谢

/**
 * 定义一个节点
 *
 * @param weight 权值
 * @param char 字符
 * */
function node(weight, char) {

    this.left = 0;//左孩子
    this.right = 0;//右孩子
    this.parent = 0;//父节点
    this.weight = weight || 0;//字符的权值
    this.char = char || '';
}

let nodes = [];
let selectStart = 0;

//
function prepareNodes() {

    const arr = [1, 2, 3, 4, 5];
    const arrChar = ['a', 'b', 'c', 'd', 'e'];
    arr.forEach(function (value, index) {
        nodes.push(new node(value, arrChar[index]));
    })
    return nodes;
}

function buildHTree(nodes) {

    selectStart = 0;
    // const nodes = prepareNodes();
    let left,//左孩子索引
        right;//右孩子

    let n = nodes.length;//外部节点数量
    let m = 2 * n - 1;//内部节点+外部节点总数量
    let HT = [];//存储节点的对象

    // console.log('n = ',n,'m = ',m,'nodes = ',nodes);
    for (let i = 0; i < m; i++) {//给所有节点一个默认权值
        HT.push(new node(0, ''));
    }
    //把叶子节点的值赋值给HT[0,n-1];
    for (let i = 0; i < n; i++) {
        HT[i].char = nodes[i].char;
        HT[i].weight = nodes[i].weight;
    }
    // console.log('HT = ',HT);
    //计算内部节点的值
    for (let i = n; i < m; i++) {
        left = selectMinWeightNode(HT, i, 0);//获取HT中权值最小的node下标
        right = selectMinWeightNode(HT, i, 1);
        // console.log('left = ',left,'right = ',right);
        HT[i].left = left || 0;
        HT[i].right = right || 0;
        HT[i].weight = HT[left].weight + HT[right].weight; //计算当前节点权值

        HT[left].parent = i;
        HT[right].parent = i;

        selectStart += 2;
    }
    return HT;
}

/**
 * 返回权值排名为rank的节点在HT中的下标(权值从小到大排列)
 *
 * @param HT 所有节点对象
 * @param rang 处理的节点范围
 * @rank left/right*/
function selectMinWeightNode(HT, rang, rank) {

    let copyNodes = [];

    for (let i = 0; i < rang; i++) {
        copyNodes.push(HT[i])
    }
    //sort  小->大
    copyNodes.sort((a, b) => a.weight - b.weight);
    //从未参与合成的节点中选出最小的值
    const targetNode = copyNodes[rank + selectStart];
    // console.log(targetNode);
    for (let i = 0; i < HT.length; i++) {
        if (targetNode === HT[i]) {
            return i;
        }
    }
    return null;
}


function huffmanCode(char, bit) {
    this.char = char;
    this.bit = bit;
}

/**
 * huffman编码
 *
 * @param HT huffman树
 * @param nodes 待编码数据*/
function encode(HT, nodes) {

    let HC = [];
    let HCStr = '';
    let bit = '';
    for (let i = 0; i < nodes.length; i++) {//遍历叶子节点
        for (let j = 0; j < HT.length; j++) {
            bit = '';
            let char = nodes[i];
            if (HT[j].char === char) {
                let nodeIndex = j;
                while (HT[nodeIndex].parent !== 0) {
                    let parentIndex = HT[nodeIndex].parent;
                    if (HT[parentIndex].left === nodeIndex) {
                        bit = '0' + bit;
                    } else {
                        bit = '1' + bit;
                    }
                    nodeIndex = parentIndex;
                }
                const code = new huffmanCode(HT[j].char, bit);
                HC.push(code);
                HCStr = HCStr+code.bit;
                break;
            }
        }
    }
    console.log(HC);
    return HCStr;
}

/**
 * huffman解码
 *
 * @param HT huffman树
 * @param ecode*/
function decode(HT, ecode) {

    let str = '';
    const bit = ecode;
    let nodeIndex = HT.length - 1;
    //遍历bit 从根节点开始遍历
    for (const c of bit) {
        if (c === '1') {//证明有右节点
            nodeIndex = HT[nodeIndex].right;
        } else {
            nodeIndex = HT[nodeIndex].left;
        }
        if (HT[nodeIndex].left === 0 && HT[nodeIndex].right === 0) {
            str += HT[nodeIndex].char;
            nodeIndex = HT.length - 1;
        }
    }
    return str;
}


function main() {

    const nodes = prepareNodes();//数据准备
    const HT = buildHTree(nodes);//建树
    const a = encode(HT,'abcdeaabcdddddbe');//编码
    // const a = decode(HT, '01001100101101001001100101010011');//解码
    console.log(a);
}

main();

测试结果

01001100101101001001100101010101001111
[ huffmanCode { char: 'a', bit: '010' },
  huffmanCode { char: 'b', bit: '011' },
  huffmanCode { char: 'c', bit: '00' },
  huffmanCode { char: 'd', bit: '10' },
  huffmanCode { char: 'e', bit: '11' },
  huffmanCode { char: 'a', bit: '010' },
  huffmanCode { char: 'a', bit: '010' },
  huffmanCode { char: 'b', bit: '011' },
  huffmanCode { char: 'c', bit: '00' },
  huffmanCode { char: 'd', bit: '10' },
  huffmanCode { char: 'd', bit: '10' },
  huffmanCode { char: 'd', bit: '10' },
  huffmanCode { char: 'd', bit: '10' },
  huffmanCode { char: 'd', bit: '10' },
  huffmanCode { char: 'b', bit: '011' },
  huffmanCode { char: 'e', bit: '11' } ]
➜  service-mqtt-backup git:(master) ✗ node huffman.js
 010 011 00 10 11 010 010 011 00 10 10 10 10 10 011 11
➜  service-mqtt-backup git:(master) ✗ 

作者:合欢猪