代码改变世界

哈弗曼编码算法(简单链表法)

2011-12-26 23:24  java线程例子  阅读(212)  评论(0编辑  收藏  举报

算法代码如下:

public class HuffmanNode
    {
        public char Char { get; set; }
        public int Frequency { get; set; }
        //用于构造哈弗曼树
        internal HuffmanNode LeftChild { get; set; }
        internal HuffmanNode RightChild { get; set; }
        //用于构造链表
        internal HuffmanNode NextSibling { get; set; }
        public string HuffmanCode {get;set;}
        public HuffmanNode(char Char,int Freq)
        {
            this.Char = Char;
            this.Frequency = Freq;
        }
    }
    public class HuffmanAlgorithms
    {
        /// <summary>
        /// 进行哈弗曼编码,编码过程中不改变Nodes节点,仅改变一些域。
        /// </summary>
        /// <param name="Nodes"></param>
        public static void HuffmanCode(HuffmanNode[] Nodes)
        {

            if (Nodes == null || Nodes.Length == 0)
            {
                return;
            }
            #region 这个处理可以不要
            if (Nodes.Length == 1)
            {
                Nodes[0].HuffmanCode = "0";
                return;
            }
            if (Nodes.Length == 2)
            {
                Nodes[0].HuffmanCode = "0";
                Nodes[1].HuffmanCode = "1";
                return;
            }
            #endregion
            HuffmanNode theHead = null;

            //用插入排序算法,建立升序节点链表O(n^2)
            for (int i = 0; i < Nodes.Length;i++ )
            {
                InsertNode(ref theHead, Nodes[i]);
            }
            //建立Huffman树,采用贪婪法,每次取链表中最小的两个(前两个).
            HuffmanNode theTmp1 = null;
            HuffmanNode theTmp2 = null;
            theTmp1 = theHead;
            if (theHead != null)
            {
                theTmp2 = theHead.NextSibling;
            }
            //链表里有两个则操作,则继续建树,否则树建立完成,退出循环.O(n*lgn)
            while (theTmp1 != null && theTmp2 != null)
            {
                //建立一个内节点包含当前链表中两个最小元素,这个节点的频率是这两个元素的频率之和.
                HuffmanNode theParent = new HuffmanNode('\0', theTmp1.Frequency + theTmp2.Frequency);
                theParent.LeftChild = theTmp1;
                theParent.RightChild = theTmp2;
                //除掉当前两个最小元素,并插入新建节点.
                theHead = theTmp2.NextSibling;
                //将新建立的内节点插入当前链表.
                InsertNode(ref theHead, theParent);
                
                theTmp1 = theHead;
                if (theHead != null)
                {
                    theTmp2 = theHead.NextSibling;
                }
            }
            //采用树的先序遍历进行编码.结果存放在节点的编码域中.o(n)
            HuffmanCode(theHead, "");
            //清理临时域,保证内存释放.o(n)
            foreach (var node in Nodes)
            {
                node.LeftChild = null;
                node.NextSibling = null;
                node.RightChild = null;
            }
        }
        /// <summary>
        /// 利用树的先序遍历进行编码,实际上有效的都是叶子节点,叶子节点都在Nodes数组中。
        /// </summary>
        /// <param name="ANode"></param>
        /// <param name="StrCode"></param>
        private static void HuffmanCode(HuffmanNode ANode, string StrCode)
        {
            if (ANode != null)
            {
                ANode.HuffmanCode = StrCode;
                if (ANode.LeftChild != null)
                {
                    HuffmanCode(ANode.LeftChild, StrCode + "0");
                }
                if (ANode.RightChild != null)
                {
                    HuffmanCode(ANode.RightChild, StrCode + "1");
                }
            }
        }
        /// <summary>
        /// 将节点Node插入到链表AHead中去,并升序排列.
        /// </summary>
        /// <param name="AHead"></param>
        /// <param name="Node"></param>
        private static void InsertNode(ref HuffmanNode AHead,HuffmanNode Node)
        {
            HuffmanNode theTmp1 = AHead;
            HuffmanNode theTmp2 = null;
            while (theTmp1 != null && theTmp1.Frequency < Node.Frequency)
            {
                theTmp2 = theTmp1;
                theTmp1 = theTmp1.NextSibling;
            }
            if (theTmp2 == null)
            {
                AHead = Node;
            }
            else
            {
                theTmp2.NextSibling = Node;
             }
            Node.NextSibling = theTmp1;
        }
    }


PS:这个算法利用最小堆效率要高些,在O(nlgn),这里的算法还是O(n^2),符合插入排序算法的时间复杂度.