数据结构——哈夫曼树

数据结构——哈夫曼树,第1张

概述转自:http://www.cnblogs.com/skywang12345/p/3706833.html 哈夫曼树的介绍 Huffman Tree,中文名是哈夫曼树或霍夫曼树,它是最优二叉树。 定义:给定n个权值作为n个叶子结点,构造一棵二叉树,若树的带权路径长度达到最小,则这棵树被称为哈夫曼树。 这个定义里面涉及到了几个陌生的概念,下面就是一颗哈夫曼树,我们来看图解答。 (01) 路径和路径长

转自:http://www.cnblogs.com/skywang12345/p/3706833.HTML

哈夫曼树的介绍

Huffman Tree,中文名是哈夫曼树或霍夫曼树,它是最优二叉树。

定义:给定n个权值作为n个叶子结点,构造一棵二叉树,若树的带权路径长度达到最小,则这棵树被称为哈夫曼树。 这个定义里面涉及到了几个陌生的概念,下面就是一颗哈夫曼树,我们来看图解答。

(01) 路径和路径长度

定义:在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。
例子:100和80的路径长度是1,50和30的路径长度是2,20和10的路径长度是3。

(02) 结点的权及带权路径长度

定义:若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。
例子:节点20的路径长度是3,它的带权路径长度= 路径长度 * 权 = 3 * 20 = 60。

(03) 树的带权路径长度

定义:树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。
例子:示例中,树的WPL= 1*100 + 2*50 + 3*20 + 3*10 = 100 + 100 + 60 + 30 = 290。


比较下面两棵树

上面的两棵树都是以{10,20,50,100}为叶子节点的树。

左边的树WPL=2*10 + 2*20 + 2*50 + 2*100 = 360
右边的树WPL=290

左边的树WPL > 右边的树的WPL。你也可以计算除上面两种示例之外的情况,但实际上右边的树就是{10,100}对应的哈夫曼树。至此,应该堆哈夫曼树的概念有了一定的了解了,下面看看如何去构造一棵哈夫曼树。

 

哈夫曼树的图文解析

假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,哈夫曼树的构造规则为:

1. 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);
2. 在森林中选出根结点的权值最小的两棵树进行合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;
3. 从森林中删除选取的两棵树,并将新树加入森林;
4. 重复(02)、(03)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。


以{5,6,7,8,15}为例,来构造一棵哈夫曼树。

第1步:创建森林,森林包括5棵树,这5棵树的权值分别是5,15。
第2步:在森林中,选择根节点权值最小的两棵树(5和6)来进行合并,将它们作为一颗新树的左右孩子(谁左谁右无关紧要,这里,我们选择较小的作为左孩子),并且新树的权值是左右孩子的权值之和。即,新树的权值是11。 然后,将"树5"和"树6"从森林中删除,并将新的树(树11)添加到森林中。
第3步:在森林中,选择根节点权值最小的两棵树(7和8)来进行合并。得到的新树的权值是15。 然后,将"树7"和"树8"从森林中删除,并将新的树(树15)添加到森林中。
第4步:在森林中,选择根节点权值最小的两棵树(11和15)来进行合并。得到的新树的权值是26。 然后,将"树11"和"树15"从森林中删除,并将新的树(树26)添加到森林中。
第5步:在森林中,选择根节点权值最小的两棵树(15和26)来进行合并。得到的新树的权值是41。 然后,将"树15"和"树26"从森林中删除,并将新的树(树41)添加到森林中。
此时,森林中只有一棵树(树41)。这棵树就是我们需要的哈夫曼树!

 

哈夫曼树的基本 *** 作

哈夫曼树的重点是如何构造哈夫曼树。本文构造哈夫曼时,用到了以前介绍过的"(二叉堆)最小堆"。下面对哈夫曼树进行讲解。

1. 基本定义

 

    public class HuffmanNode : IComparable,ICloneable    {        public int key;              // 权值        public HuffmanNode left;     // 左孩子        public HuffmanNode right;    // 右孩子        public HuffmanNode parent;   // 父结点        public HuffmanNode(int key,HuffmanNode left,HuffmanNode right,HuffmanNode parent)        {            this.key = key;            this.left = left;            this.right = right;            this.parent = parent;        }        public object Clone()        {            object obj = null;            obj = this;            return obj;        }        public int Compareto(object obj)        {            return this.key - ((HuffmanNode)obj).key;        }    }

 

 

 

HuffmanNode是哈夫曼树的节点类。

public class Huffman {    private HuffmanNode mRoot;  // 根结点    ...}

Huffman是哈夫曼树对应的类,它包含了哈夫曼树的根节点和哈夫曼树的相关 *** 作。

2. 构造哈夫曼树

 

    public class Huffman    {        private HuffmanNode mRoot;  // 根结点        /*          * 创建Huffman树         *         * @param 权值数组         */        public Huffman(int[] a)        {            HuffmanNode parent = null;            MinHeap heap;            // 建立数组a对应的最小堆            heap = new MinHeap(a);            for (int i = 0; i < a.Length - 1; i++)            {                HuffmanNode left = heap.dumpFromMinimum();  // 最小节点是左孩子                HuffmanNode right = heap.dumpFromMinimum(); // 其次才是右孩子                // 新建parent节点,左右孩子分别是left/right;                // parent的大小是左右孩子之和                parent = new HuffmanNode(left.key + right.key,left,right,null);                left.parent = parent;                right.parent = parent;                // 将parent节点数据拷贝到"最小堆"中                heap.insert(parent);            }            mRoot = parent;            // 销毁最小堆            heap.destroy();        }        /*         * 前序遍历"Huffman树"         */        private voID preOrder(HuffmanNode tree)        {            if (tree != null)            {                Console.Write(tree.key + " ");                preOrder(tree.left);                preOrder(tree.right);            }        }        public voID preOrder()        {            preOrder(mRoot);        }        /*         * 中序遍历"Huffman树"         */        private voID inorder(HuffmanNode tree)        {            if (tree != null)            {                inorder(tree.left);                Console.Write(tree.key + " ");                inorder(tree.right);            }        }        public voID inorder()        {            inorder(mRoot);        }        /*         * 后序遍历"Huffman树"         */        private voID postorder(HuffmanNode tree)        {            if (tree != null)            {                postorder(tree.left);                postorder(tree.right);                Console.Write(tree.key + " ");            }        }        public voID postorder()        {            postorder(mRoot);        }        /*         * 销毁Huffman树         */        private voID destroy(HuffmanNode tree)        {            if (tree == null)                return;            if (tree.left != null)                destroy(tree.left);            if (tree.right != null)                destroy(tree.right);            tree = null;        }        public voID destroy()        {            destroy(mRoot);            mRoot = null;        }        /*         * 打印"Huffman树"         *         * key        -- 节点的键值          * direction  --  0,表示该节点是根节点;         *               -1,表示该节点是它的父结点的左孩子;         *                1,表示该节点是它的父结点的右孩子。         */        private voID print(HuffmanNode tree,int key,int direction)        {            if (tree != null)            {                if (direction == 0) // tree是根节点                    Console.Writeline("{0} is root\n",tree.key);                else                // tree是分支节点                    Console.Writeline("{0} is {1}‘s {2} child\n",tree.key,key,direction == 1 ? "right" : "left");                print(tree.left,-1);                print(tree.right,1);            }        }        public voID print()        {            if (mRoot != null)                print(mRoot,mRoot.key,0);        }    }

 

首先创建最小堆,然后进入for循环。

每次循环时:

(01) 首先,将最小堆中的最小节点拷贝一份并赋值给left,然后重塑最小堆(将最小节点和后面的节点交换位置,接着将"交换位置后的最小节点"之前的全部元素重新构造成最小堆);
(02) 接着,再将最小堆中的最小节点拷贝一份并将其赋值right,然后再次重塑最小堆;
(03) 然后,新建节点parent,并将它作为left和right的父节点;
(04) 接着,将parent的数据复制给最小堆中的指定节点。

MinHeap

 

    public class MinHeap    {        private List<HuffmanNode> mHeap;        // 存放堆的数组        /*          * 创建最小堆         *         * 参数说明:         *     a -- 数据所在的数组         */        public MinHeap(int[] a)        {            mHeap = new List<HuffmanNode>();            //mHeap = new ArrayList<HuffmanNode>();            // 初始化数组            for (int i = 0; i < a.Length; i++)            {                HuffmanNode node = new HuffmanNode(a[i],null,null);                mHeap.Add(node);            }            // 从(size/2-1) --> 0逐次遍历。遍历之后,得到的数组实际上是一个最小堆。            for (int i = a.Length / 2 - 1; i >= 0; i--)                filterdown(i,a.Length - 1);        }        /*          * 最小堆的向下调整算法         *         * 注:数组实现的堆中,第N个节点的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。         *         * 参数说明:         *     start -- 被下调节点的起始位置(一般为0,表示从第1个开始)         *     end   -- 截至范围(一般为数组中最后一个元素的索引)         */        public voID filterdown(int start,int end)        {            int c = start;      // 当前(current)节点的位置            int l = 2 * c + 1;  // 左(left)孩子的位置            HuffmanNode tmp = mHeap[c]; // 当前(current)节点            while (l <= end)            {                // "l"是左孩子,"l+1"是右孩子                if (l < end && (mHeap[1].Compareto(mHeap[l + 1]) > 0))                    l++;        // 左右两孩子中选择较小者,即mHeap[l+1]                int cmp = tmp.Compareto(mHeap[l]);                if (cmp <= 0)                    break;      //调整结束                else                {                    mHeap[c] = mHeap[l];                    c = l;                    l = 2 * l + 1;                }            }            mHeap[c] = tmp;        }        /*         * 最小堆的向上调整算法(从start开始向上直到0,调整堆)         *         * 注:数组实现的堆中,第N个节点的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。         *         * 参数说明:         *     start -- 被上调节点的起始位置(一般为数组中最后一个元素的索引)         */        public voID filterup(int start)        {            int c = start;          // 当前节点(current)的位置            int p = (c - 1) / 2;        // 父(parent)结点的位置             HuffmanNode tmp = mHeap[c]; // 当前(current)节点            while (c > 0)            {                int cmp = mHeap[p].Compareto(tmp);                if (cmp <= 0)                    break;                else                {                    mHeap[c] = mHeap[p];                    c = p;                    p = (p - 1) / 2;                }            }            mHeap[c] = tmp;        }        /*          * 将node插入到二叉堆中         */        public voID insert(HuffmanNode node)        {            int size = mHeap.Count();            mHeap.Add(node);    // 将"数组"插在表尾            filterup(size);     // 向上调整堆        }        /*         * 交换两个HuffmanNode节点的全部数据         */        private voID swapNode(int i,int j)        {            HuffmanNode tmp = mHeap[i];            mHeap[i] = mHeap[j];            mHeap[j] = tmp;        }        /*          * 新建一个节点,并将最小堆中最小节点的数据复制给该节点。         * 然后除最小节点之外的数据重新构造成最小堆。         *         * 返回值:         *     失败返回null。         */        public HuffmanNode dumpFromMinimum()        {            int size = mHeap.Count();            // 如果"堆"已空,则返回            if (size == 0)                return null;            // 将"最小节点"克隆一份,将克隆得到的对象赋值给node            HuffmanNode node = (HuffmanNode)mHeap[0].Clone();            // 交换"最小节点"和"最后一个节点"            mHeap[0] = mHeap[size - 1];            // 删除最后的元素            mHeap.Remove(mHeap[size - 1]);            if (mHeap.Count() > 1)                filterdown(0,mHeap.Count() - 1);            return node;        }        // 销毁最小堆        public voID destroy()        {            mHeap.Clear();            mHeap = null;        }    }

 

 

 

在二叉堆中已经介绍过堆,这里就不再对堆的代码进行说明了。若有疑问,直接参考后文的源码。其它的相关代码,也Please RTFSC(Read The Fucking Source Code)!

测试程序
    public class HuffmanTest    {        private int[] a = { 5,6,8,7,15 };        public voID main()        {            int i;            Huffman tree;            Console.Writeline("== 添加数组: ");            for (i = 0; i < a.Length; i++)                Console.Writeline(a[i] + " ");            // 创建数组a对应的Huffman树            tree = new Huffman(a);            Console.Writeline("\n== 前序遍历: ");            tree.preOrder();            Console.Writeline("\n== 中序遍历: ");            tree.inorder();            Console.Writeline("\n== 后序遍历: ");            tree.postorder();            Console.Writeline("== 树的详细信息: ");            tree.print();            // 销毁二叉树            tree.destroy();        }    }

结果

== 添加数组:
5
6
8
7
15

== 前序遍历:
41 15 7 8 26 11 5 6 15
== 中序遍历:
7 15 8 41 5 11 6 26 15
== 后序遍历:
7 8 15 5 6 11 15 26 41 == 树的详细信息:
41 is root

15 is 41‘s left child

7 is 15‘s left child

8 is 15‘s right child

26 is 41‘s right child

11 is 26‘s left child

5 is 11‘s left child

6 is 11‘s right child

15 is 26‘s right child

总结

以上是内存溢出为你收集整理的数据结构——哈夫曼树全部内容,希望文章能够帮你解决数据结构——哈夫曼树所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1082846.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-27
下一篇 2022-05-27

发表评论

登录后才能评论

评论列表(0条)

保存