huffman算法的簡單實現(xiàn)

簡介

昨天剛拿到的需求,要求用二叉樹設計一個huffman算法,主要用于壓縮數(shù)據(jù),就簡單的用javascript實現(xiàn)了一個,在實現(xiàn)的過程中,畫樹進行的很順利,但在測試編解碼的時候出現(xiàn)了一些問題,也是自己對huffman算法的理解錯誤,好在及時改正。

算法實現(xiàn)

對huffman算法實現(xiàn)原理如果不了解的話最好自己去研究一下在看,否則可能有些地方看不懂,在這不做具體說明。
廢話到此為止了,直接上代碼,如果認為對自己有用的,請點個贊,謝謝

/**
 * 定義一個節(jié)點
 *
 * @param weight 權值
 * @param char 字符
 * */
function node(weight, char) {

    this.left = 0;//左孩子
    this.right = 0;//右孩子
    this.parent = 0;//父節(jié)點
    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;//外部節(jié)點數(shù)量
    let m = 2 * n - 1;//內部節(jié)點+外部節(jié)點總數(shù)量
    let HT = [];//存儲節(jié)點的對象

    // console.log('n = ',n,'m = ',m,'nodes = ',nodes);
    for (let i = 0; i < m; i++) {//給所有節(jié)點一個默認權值
        HT.push(new node(0, ''));
    }
    //把葉子節(jié)點的值賦值給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);
    //計算內部節(jié)點的值
    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; //計算當前節(jié)點權值

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

        selectStart += 2;
    }
    return HT;
}

/**
 * 返回權值排名為rank的節(jié)點在HT中的下標(權值從小到大排列)
 *
 * @param HT 所有節(jié)點對象
 * @param rang 處理的節(jié)點范圍
 * @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);
    //從未參與合成的節(jié)點中選出最小的值
    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 待編碼數(shù)據(jù)*/
function encode(HT, nodes) {

    let HC = [];
    let HCStr = '';
    let bit = '';
    for (let i = 0; i < nodes.length; i++) {//遍歷葉子節(jié)點
        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 從根節(jié)點開始遍歷
    for (const c of bit) {
        if (c === '1') {//證明有右節(jié)點
            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();//數(shù)據(jù)準備
    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) ? 

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容