哈夫曼樹

huffman樹,又稱最優二叉樹,是加權路徑長度最短的二叉樹
[貪心算法]是指在問題求解時,總是做出當前看起來最好的選擇,通俗的說貪心算法不是整體最優的選擇,而是局部最優解
使用貪心算法構建Huffman樹
這裏寫圖片描述
構建哈夫曼樹主要包括兩部分:
1:建小堆
2:將加權路徑長度最短,構建最短二叉樹
一:建小堆:

#pragma once
#include<vector>
#include<assert.h>
using namespace std;
template<class T>
class Less
{
public:
    bool operator()(const T&a, const T&b)
    {
        return a < b;
    }
};
template<class T>
class Greater
{
public:
    bool operator()(const T&a, const T&b)
    {
        return a>b;
    }
};
//小堆:任意一個節點是它子樹的最大節點
template<class T, class Compare = Less<T>>
class Heap
{
public:
    Heap()
    {}
    Heap(T*a, size_t n)
    {
        _a.reserve(n);//增容(可以直接拷貝數據)
        for (size_t i = 0; i < n; i++)
        {
            _a.push_back(a[i]);
        }
        //調整成堆
        for (int j = (_a.size() - 2) / 2; j >= 0; --j)
        {
            //向下調整
            _AjustDown(j);
        }
    }
    //插入
    void Push(const T&x)
    {
        _a.push_back(x);
        //調整成堆
        _AjustUp(_a.size() - 1);
    }
    //刪除
    void Pop()
    {
        assert(!_a.empty());

        swap(_a[0], _a[_a.size() - 1]);//根節點和最後一個葉子節點交換
        _a.pop_back();
        _AjustDown(0);

    }
    const T& Top()
    {
        return _a[0];
    }
    size_t Size()
    {
        return _a.size();
    }
    bool Empty()
    {
        return _a.empty();
    }

protected:
    //插入算法,主要影響root路徑,向上
    void _AjustUp(int child)
    {
        assert(!_a.empty());
        int parent = (child - 1) >> 1;
        Compare comFunc;//調用仿函數
        while (child>0)
        {
            //如果孩子節點的值大於父節點的值
            if (comFunc(_a[child], _a[parent]))
            {
                swap(_a[child], _a[parent]);
                child = parent;
                parent = (child - 1) >> 1;
            }
            else
            {
                break;
            }
        }
    }
protected:
    //向下調整成堆
    void _AjustDown(int root)
    {
        Compare comFuc;
        int parent = root;
        int child = root * 2 + 1;//左孩子

        while (child < _a.size())
        {
            //選較小的孩子
            if (child + 1 < _a.size() && comFuc(_a[child + 1], _a[child]))
            {
                ++child;
            }
            //比較父親與孩子的大小
            if (comFuc(_a[child], _a[parent]))
            {
                swap(_a[child], _a[parent]);
                parent = child;
                child = parent * 2 + 1;
            }
            else
            {
                break;
            }
        }
    }
protected:
    vector<T>_a;
};
void TestHeap()
{
    int a1[] = { 10, 16, 18, 12, 13, 15, 17, 14, 19 };

    size_t len = sizeof(a1) / sizeof(a1[0]);
    Heap<int, Less<int>>  h1(a1, len);
    h1.Top();
    /*h1.Push(25);
    h1.Pop();*/
    while (!h1.Empty())
    {
        cout << h1.Top() << " ";
        h1.Pop();
    }
    cout << endl;
}

二:構建哈夫曼樹:
構建哈夫曼樹,是所有葉子節點帶權路徑之和.
建樹的思路:
1:將所有節點看成獨立的樹,且左右子樹都爲空,
2:選擇權值最小的兩個節點,生成一個節點作爲它們的父節點,父節點的權值等於它們的權值之和
3:將父節點放回重複步驟2直到這個序列中只剩下最後一個節點,此時哈夫曼樹就建成了.

#pragma once
#include"Heap.h"
#include<assert.h>
using namespace std;
template<class T>
struct HuffmanTresNode
{
    T _w;//權值
    HuffmanTresNode<T> *_left;
    HuffmanTresNode<T> *_right;
    HuffmanTresNode<T> *_parent;

    HuffmanTresNode(const T&x = T())
        :_w(x)
        , _left(NULL)
        , _right(NULL)
        , _parent(NULL)
    {}
};
template<class T>
class HuffmanTree
{
    typedef HuffmanTresNode<T> Node;
public:
    //缺省的構造函數
    HuffmanTree()
        :_root(NULL)
    {}
    HuffmanTree(T*a, size_t n, const T& invalid = T())//創建huffmanTree
    {
        assert(a);
        //_root = CreatHuffmanTree(a, n, invalid);
        //比較權值
        struct Compare
        {
            bool operator()(const Node*l, const Node*r) const
            {
                return l->_w < r->_w;
            }
        };

        Heap<Node*, Compare> minHeap;//所有元素建小堆
        for (size_t i = 0; i < n; ++i)
        {
            if (a[i] != invalid)
            {
                minHeap.Push(new Node(a[i]));
            }
        }
        Node*left = NULL;
        Node*right = NULL;
        while (minHeap.Size()>1)
        {
            left = minHeap.Top();
            minHeap.Pop();

            right = minHeap.Top();
            minHeap.Pop();

            Node*parent = new Node(left->_w + right->_w);

            parent->_left = left;
            parent->_right = right;
            left->_parent = parent;
            right->_parent = parent;

            minHeap.Push(parent);
        }
        _root = minHeap.Top();
    }

    Node* GetHuffmanTree()
    {
        return _root;
    }

    ~HuffmanTree()
    {
        if (_root != NULL)
            _Destroy(_root);
    }

protected:

    //釋放空間9
    void _Destroy(Node*node)
    {
        if (node == NULL)
            return;
        _Destroy(node->_left);
        _Destroy(node->_right);
        delete node;
    }
protected:
    Node* _root;
};
void TestHuffmanTree()
{
    int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    HuffmanTree<int> t(a, 10);
}

當然哈夫曼樹的一個重要應用就是通過哈夫曼編碼來實現文件壓縮,關於文件壓縮後面我們再來探討.

發佈了113 篇原創文章 · 獲贊 62 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章