數據結構和算法系列16 哈夫曼樹


這一篇要總結的是樹中的最后一種,即哈夫曼樹,我想從以下幾點對其進行總結:

1,什么是哈夫曼樹?

2,如何構建哈夫曼樹?

3,哈夫曼編碼?

4,算法實現?

一,什么是哈夫曼樹

什么是哈夫曼樹呢?

哈夫曼樹是一種帶權路徑長度最短的二叉樹,也稱為最優二叉樹。下面用一幅圖來說明。

ds48

它們的帶權路徑長度分別為:

圖a: WPL=5*2+7*2+2*2+13*2=54

圖b: WPL=5*3+2*3+7*2+13*1=48

可見,圖b的帶權路徑長度較小,我們可以證明圖b就是哈夫曼樹(也稱為最優二叉樹)。

二,如何構建哈夫曼樹

一般可以按下面步驟構建:

1,將所有左,右子樹都為空的作為根節點。

2,在森林中選出兩棵根節點的權值最小的樹作為一棵新樹的左,右子樹,且置新樹的附加根節點的權值為其左,右子樹上根節點的權值之和。注意,左子樹的權值應小於右子樹的權值。

3,從森林中刪除這兩棵樹,同時把新樹加入到森林中。

4,重復2,3步驟,直到森林中只有一棵樹為止,此樹便是哈夫曼樹。

下面是構建哈夫曼樹的圖解過程:

ds52

三,哈夫曼編碼

利用哈夫曼樹求得的用於通信的二進制編碼稱為哈夫曼編碼。樹中從根到每個葉子節點都有一條路徑,對路徑上的各分支約定指向左子樹的分支表示”0”碼,指向右子樹的分支表示“1”碼,取每條路徑上的“0”或“1”的序列作為各個葉子節點對應的字符編碼,即是哈夫曼編碼。

就拿上圖例子來說:

A,B,C,D對應的哈夫曼編碼分別為:111,10,110,0

用圖說明如下:

ds50

記住,設計電文總長最短的二進制前綴編碼,就是以n個字符出現的頻率作為權構造一棵哈夫曼樹,由哈夫曼樹求得的編碼就是哈夫曼編碼。

四,算法實現

C#版:

namespace HuffTree.CSharp
{
    class Program
    {
        static void Main(string[] args)
        {
            //四個葉子節點
            int leafNum = 4;

            //赫夫曼樹的節點總數
            int totalNodes = 2 * leafNum - 1;

            //各葉子節點的權值
            int[] weight = new int[] { 5,7,2,13};

            //各葉子節點的值
            string[] alphabet = new string[] { "A","B","C","D"};

            //初始化赫夫曼樹
            HuffmanTree[] huffmanTree = new HuffmanTree[totalNodes].Select(p => new HuffmanTree() { }).ToArray();

            //構建赫夫曼樹
            HuffmanTreeBLL.Create(huffmanTree,leafNum,weight);

            //赫夫曼編碼
            string[] huffmanCode = HuffmanTreeBLL.Coding(huffmanTree,leafNum);

            //打印結果
            PrintResult(alphabet,huffmanTree,huffmanCode,leafNum);

            Console.ReadKey();
        }

        /// <summary>
        /// 打印結果
        /// </summary>
        /// <param name="alphabet"></param>
        /// <param name="huffmanTree"></param>
        /// <param name="huffmanCode"></param>
        /// <param name="leafNum"></param>
        private static void PrintResult(string[] alphabet,HuffmanTree[] huffmanTree,string[] huffmanCode,int leafNum)
        {
            if (alphabet.Count() < 1 || huffmanTree.Count() < 1 || huffmanCode.Count() < 1) return;

            for (int i = 0; i < leafNum; i++)
            {
                Console.WriteLine("字符:{0},權重值:{1},赫夫曼編碼:{2}",alphabet[i],huffmanTree[i].Weight,huffmanCode[i]);
            }
        }
    }
}

namespace DS.BLL
{
    /// <summary>
    /// 描述:赫夫曼樹操作類
    /// 作者:魯寧
    /// 時間:2013/9/17 18:14:33
    /// </summary>
    public class HuffmanTreeBLL
    {
        /// <summary>
        /// 構建赫夫曼樹
        /// 思路:一步一步向上搭建
        /// </summary>
        /// <param name="huffmanTree">待操作的赫夫曼樹</param>
        /// <param name="leafNum">葉節點數量</param>
        /// <param name="weight">節點權重值</param>
        /// <returns>構建好的赫夫曼樹</returns>
        public static HuffmanTree[] Create(HuffmanTree[] huffmanTree, int leafNum, int[] weight)
        {
            //獲取赫夫曼樹結點總數
            int totalNodes = 2 * leafNum - 1;

            InitLeafNode(huffmanTree,leafNum,weight);
            
            //構造赫夫曼樹(4個節點只需要3步就可以完成構建)
            for (int i = leafNum; i < totalNodes; i++)
            { 
                //獲取權重最小的兩個葉子節點的下標
                int minIndex1 = -1;
                int minIndex2 = -1;
                SelectNode(huffmanTree,i,ref minIndex1,ref minIndex2);

                huffmanTree[minIndex1].Parent = i;
                huffmanTree[minIndex2].Parent = i;

                huffmanTree[i].Left = minIndex1;
                huffmanTree[i].Right = minIndex2;
                huffmanTree[i].Weight = huffmanTree[minIndex1].Weight + huffmanTree[minIndex2].Weight;
            }
            return huffmanTree;
        }

        /// <summary>
        /// 赫夫曼編碼
        /// 思路:左子樹為0,右子樹為1,對應的編碼后的規則是:從根節點到子節點
        /// </summary>
        /// <param name="huffmanTree">待操作的赫夫曼樹</param>
        /// <param name="leafNum">葉子節點的數量</param>
        /// <returns>赫夫曼編碼</returns>
        public static string[] Coding(HuffmanTree[] huffmanTree, int leafNum)
        { 
            string[] huffmanCode= new string[leafNum];

            //當前節點下標
            int current = 0;
            //父節點下標
            int parent = 0;

            for (int i = 0; i < leafNum; i++)
            {
                string codeTemp = string.Empty;
                current = i;
                
                //第一次獲取最左節點
                parent = huffmanTree[current].Parent;

                while (parent != 0)
                {
                    if (huffmanTree[parent].Left == current) codeTemp += "0";
                    else codeTemp += "1";

                    current = parent;
                    parent = huffmanTree[parent].Parent;
                }
                huffmanCode[i] = new string(codeTemp.Reverse().ToArray());
            }
            return huffmanCode;
        }


        /// <summary>
        /// 初始化葉節點
        /// </summary>
        /// <param name="huffmanTree"></param>
        /// <param name="leafNum"></param>
        /// <param name="weight"></param>
        private static void InitLeafNode(HuffmanTree[] huffmanTree, int leafNum, int[] weight)
        {
            if (huffmanTree == null || leafNum<1 || weight.Count()<1) return;

            for (int i = 0; i < leafNum; i++)
            {
                huffmanTree[i].Weight = weight[i];
            }
        }

        /// <summary>
        /// 獲取葉子節點中權重最小的兩個節點
        /// </summary>
        /// <param name="huffmanTree">待操作的赫夫曼</param>
        /// <param name="searchNode">要查找的節點數</param>
        /// <param name="minIndex1"></param>
        /// <param name="minIndex2"></param>
        private static void SelectNode(HuffmanTree[] huffmanTree, int searchNode, ref int minIndex1, ref int minIndex2)
        {
            HuffmanTree minNode1 = null;
            HuffmanTree minNode2 = null;

            for (int i = 0; i < searchNode; i++)
            {
                //只查找獨根樹葉子節點
                if (huffmanTree[i].Parent == 0)
                {
                    //如果為null,則表示當前節葉子節點最小
                    if (minNode1 == null)
                    {
                        minIndex1 = i;
                        minNode1= huffmanTree[i];
                        continue;
                    }

                    if (minNode2 == null)
                    {
                        minIndex2 = i;
                        minNode2= huffmanTree[i];

                        //交換位置,確保minIndex1為最小
                        if (minNode1.Weight >= minNode2.Weight)
                        { 
                            //節點交換
                            var temp = minNode1;
                            minNode1 = minNode2;
                            minNode2 = temp;

                            //交換下標
                            var tempIndex = minIndex1;
                            minIndex1 = minIndex2;
                            minIndex2 = tempIndex;

                            continue;
                        }
                    }

                    if (minNode1 != null && minNode2 != null)
                    {
                        if (huffmanTree[i].Weight < minNode1.Weight) //注意,不能是“<=”
                        {
                            //將min1臨時轉存給min2
                            minNode2 = minNode1;
                            minNode1 = huffmanTree[i];

                            //記錄在數組中的下標
                            minIndex2 = minIndex1;
                            minIndex1 = i;
                        }
                        else
                        {
                            if (huffmanTree[i].Weight < minNode2.Weight)
                            { 
                                minNode2= huffmanTree[i];
                                minIndex2 = i;
                            }
                        }
                    }
                }
            }
        }
    }

    /// <summary>
    /// 赫夫曼樹存儲結構
    /// </summary>
    public class HuffmanTree
    {
        public int Weight { get; set; } //權值

        public int Parent { get; set; } //父節點

        public int Left { get; set; } //左孩子節點

        public int Right { get; set; } //右孩子節點
    }
}

程序輸出結果為:

ds51


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM