数据结构、算法与应用 12.6.3 霍夫曼编码-霍夫曼树

queue.h

#ifndef QUEUE_H
#define QUEUE_H

template <typename T>
class queue
{
public:
	virtual ~queue();
	virtual bool empty() const = 0;
							// 返回 true,当且仅当当前队列为空
	virtual int size() const = 0;
							// 返回队列中元素个数
	virtual T& front() = 0;
							// 返回头元素的引用
	virtual T& back() = 0;
							// 返回尾元素的引用
	virtual void pop() = 0;
							// 删除首元素
	virtual void push(const T& theElement) = 0;
							// 把元素 theElement 加入队尾
};

#endif // !QUEUE_H

arrayQueue.h

#ifndef ARRAYQUEUE_H
#define ARRAYQUEUE_H

#include "queue.h"
#include "myExceptions.h"
#include <sstream>

using namespace std;

template<class T>
class arrayQueue : public queue<T>
{
   public:
      arrayQueue(int initialCapacity = 10);
      ~arrayQueue() {delete [] queue;}
      bool empty() const {return theFront == theBack;}
      int size() const
          {return (theBack - theFront + arrayLength) % arrayLength;}
      T& front()
      {// 返回头元素的引用
          if (theFront == theBack)
              throw queueEmpty();
          return queue[(theFront + 1) % arrayLength];
      }
      T& back()
      {// 返回尾元素的引用
          if (theFront == theBack)
              throw queueEmpty();
          return queue[theBack];
       }
      void pop()
      {// 删除队列首元素
          if (theFront == theBack)
              throw queueEmpty;
          theFront = (theFront + 1) % arrayLength;
          queue[theFront].~T();   // 给 T 析构
      }
      void push(const T& theElement);  // 把元素 theElement 加入队尾
   private:
      int theFront;       // 逆时针方向,指向列首元素的下一个位置
      int theBack;        // 尾元素的位置
      int arrayLength;    // 队列容量
      T *queue;           // 元素数组
};

template<class T>
arrayQueue<T>::arrayQueue(int initialCapacity)
{// Constructor.
   if (initialCapacity < 1)
   {ostringstream s;
    s << "Initial capacity = " << initialCapacity << " Must be > 0";
    throw illegalParameterValue(s.str());
   }
   arrayLength = initialCapacity;
   queue = new T[arrayLength];
   theFront = 0;
   theBack = 0;
}

template<typename T>
void arrayQueue<T>::push(const T& theElement)
{// 把元素 theElement 加入队列
   
    if ((theBack + 1) % theFront == theFront)
    {// 加倍数组长度
        // 分配新的数组空间
        T* newQueue = new T[theFront * 2];

        // 把原数组复制到新数组
        int start = (theFront + 1) % arrayLength;
        if (start < 2)
            // 没有形成环
            copy(queue + start, queue + arrayLength, newQueue);
        else
        {// 队列形成环
            copy(queue + start, queue + theFront, newQueue);
            copy(queue, queue + theBack + 1, newQueue + arrayLength - start);
        }
        theFront = 2 * arrayLength - 1;
        theBack = arrayLength - 2;          // 队列长度 - 1
        arrayLength *= 2;
        delete [] queue;
        queue = newQueue;
   }

   // 把元素 theElement 插入队列的尾部
    theBack = (theBack + 1) % arrayLength;
    queue[theBack] = theElement;
}

#endif

changeLength1D.h

// 更改数组的长度

#ifndef CHANGELENGTH1D_H
#define CHANGELENGTH1D_H

#include "myExceptions.h"

using namespace std;

template<class T>
void changeLength1D(T*& a, int oldLength, int newLength)
{
   if (newLength < 0)
      throw illegalParameterValue("new length must be >= 0");

   T* temp = new T[newLength];              // 新阵列
   int number = min(oldLength, newLength);  // 要复制的编号
   copy(a, a + number, temp);
   delete [] a;                             // 释放旧内存
   a = temp;
}

#endif

myExceptions.h

// 各种错误类型的异常类

#ifndef MYEXCEPTIONS_H
#define MYEXCEPTIONS_H
#include <string>

using namespace std;

// 非法参数值
class illegalParameterValue 
{
   public:
      illegalParameterValue(string theMessage = "Illegal parameter value")
            {message = theMessage;}
      void outputMessage() {cout << message << endl;}
   private:
      string message;
};

// 非法输入数据
class illegalInputData 
{
   public:
      illegalInputData(string theMessage = "Illegal data input")
            {message = theMessage;}
      void outputMessage() {cout << message << endl;}
   private:
      string message;
};

// 非法索引
class illegalIndex 
{
   public:
      illegalIndex(string theMessage = "Illegal index")
            {message = theMessage;}
      void outputMessage() {cout << message << endl;}
   private:
      string message;
};

// 矩阵索引越界
class matrixIndexOutOfBounds 
{
   public:
      matrixIndexOutOfBounds
            (string theMessage = "Matrix index out of bounds")
            {message = theMessage;}
      void outputMessage() {cout << message << endl;}
   private:
      string message;
};

// 矩阵大小不匹配
class matrixSizeMismatch 
{
   public:
      matrixSizeMismatch(string theMessage = 
                   "The size of the two matrics doesn't match")
            {message = theMessage;}
      void outputMessage() {cout << message << endl;}
   private:
      string message;
};

// 堆栈是空的
class stackEmpty
{
   public:
      stackEmpty(string theMessage = 
                   "Invalid operation on empty stack")
            {message = theMessage;}
      void outputMessage() {cout << message << endl;}
   private:
      string message;
};

// 队列为空
class queueEmpty
{
   public:
      queueEmpty(string theMessage = 
                   "Invalid operation on empty queue")
            {message = theMessage;}
      void outputMessage() {cout << message << endl;}
   private:
      string message;
};

// 哈希表已满
class hashTableFull
{
   public:
      hashTableFull(string theMessage = 
                   "The hash table is full")
            {message = theMessage;}
      void outputMessage() {cout << message << endl;}
   private:
      string message;
};

// 边权重未定义
class undefinedEdgeWeight
{
   public:
      undefinedEdgeWeight(string theMessage = 
                   "No edge weights defined")
            {message = theMessage;}
      void outputMessage() {cout << message << endl;}
   private:
      string message;
};

// 方法未定义
class undefinedMethod
{
   public:
      undefinedMethod(string theMessage = 
                   "This method is undefined")
            {message = theMessage;}
      void outputMessage() {cout << message << endl;}
   private:
      string message;
};
#endif

minPriorityQueue.h

// 抽象类最小优先级队列
// 所有方法都是纯虚函数

#ifndef MINPRIORITYQUEUE_H
#define MINPRIORITYQUEUE_H

using namespace std;

template<class T>
class minPriorityQueue 
{
   public:
      virtual ~minPriorityQueue() {}
      virtual bool empty() const = 0;
                  // 返回真iff队列为空
      virtual int size() const = 0;
                  // 返回队列中的元素数
      virtual const T& top() = 0;
                  // 返回对min元素的引用
      virtual void pop() = 0;
                  // 删除栈顶
      virtual void push(const T& theElement) = 0;
                  // 将元素添加到队列中
};
#endif

binaryTreeNode.h

#ifndef BINARYTREENODE_H
#define BINARYTREENODE_H
#include <iostream>
using namespace std;

template<typename T>
struct binaryTreeNode
{
	T element;
	binaryTreeNode<T> *leftChild,          // 左子树
					  *rightChild;         // 右子树

	binaryTreeNode() {leftChild = rightChild = nullptr;}
	binaryTreeNode(const T& theElement) : element(theElement)
	{
		leftChild = rightChild = nullptr;
	}
	binaryTreeNode(const T& theElement, binaryTreeNode *theLeftChild, binaryTreeNode *theRightChild) : element(theElement)
	{
		leftChild = theLeftChild;
		rightChild = theRightChild;
	}
};

#endif // !BINARYTREENODE_H

binaryTree.h

#ifndef BRNARYTREE_H
#define BRNARYTREE_H
#include <iostream>
using namespace std;

template<typename T>
class binaryTree
{
public:
	virtual ~binaryTree(){}
	virtual bool empty() const = 0;
	virtual int size() const = 0;
	virtual void preOrder(void (*) (T *)) = 0;
	virtual void inOrder(void (*)  (T *)) = 0;
	virtual void postOrder(void (*) (T*)) = 0;
	virtual void levelOrder(void (*) (T *)) = 0;
};

#endif // !BRNARYTREE_H

linkedBinaryTree.h

#ifndef LINKEDBINARYTREE_H
#define LINKEDBINARYTREE_H
#include <iostream>
#include "binaryTree.h"
#include "binaryTreeNode.h"
#include "arrayQueue.h"

using namespace std;

template<typename E>
class linkedBinaryTree : public binaryTree<binaryTreeNode<E>>
{
public:
	linkedBinaryTree() {root = nullptr; treeSize = 0;}
	~linkedBinaryTree() {erase();}
	
	bool empty() const {return treeSize == 0;}
	int size() const {return treeSize;}

	// 用指定的根和左右子树来构造一棵二叉树
	void makeTree(const E& element, linkedBinaryTree<E>&, linkedBinaryTree<E>&);

	void preOrder(void(*theVisit)(binaryTreeNode<E>*))    // theVisit是个函数指针,指向一个没有参数没有返回值的函数
		{ visit = theVisit; preOrder(root); }
	void inOrder(void(*theVisit)(binaryTreeNode<E>*))
		{ visit = theVisit; inOrder(root); }
	void postOrder(void(*theVisit)(binaryTreeNode<E>*))
		{ visit = theVisit; postOrder(root); }
	void levelOrder(void(*)(binaryTreeNode<E>*));
	void erase()
	{
		postOrder(dispose);
		root = nullptr;
		treeSize = 0;
	}

	int height() const { return height(root); }
	int height(binaryTreeNode<E> *t) const;

private:
	binaryTreeNode<E> *root;                      // 指向根的指针
	int treeSize;                                 // 树的节点个数
	static void (*visit)(binaryTreeNode<E> *);     // 访问函数
	static void preOrder(binaryTreeNode<E> *t);
	static void inOrder(binaryTreeNode<E> *t);
	static void postOrder(binaryTreeNode<E> *t);
	static void dispose(binaryTreeNode<E> *t) {delete t;}

	static void output(binaryTreeNode<E> *t)
		{ cout << t->element<< ' '; }

};

template<class E>
void linkedBinaryTree<E>::makeTree(const E& element, linkedBinaryTree<E>& left, linkedBinaryTree<E>& right)
{// 将left、right和element组合在一起以创建新树
 // 左,右,这一定是不同的树。
   // 创建组合树
   root = new binaryTreeNode<E> (element, left.root, right.root);
   treeSize = left.treeSize + right.treeSize + 1;

   // 拒绝左右树的访问
   left.root = right.root = NULL;
   left.treeSize = right.treeSize = 0;
}

template<typename E>
void linkedBinaryTree<E>::preOrder(binaryTreeNode<E> *t)
{// 前序遍历
	if (t != nullptr)
	{
		linkedBinaryTree<E>::visit(t);
		preOrder(t->leftChild);
		preOrder(t->rightChild);
	}
}

template<typename E>
void linkedBinaryTree<E>::inOrder(binaryTreeNode<E> *t)
{// 中序遍历
	if (t != nullptr)
	{
		inOrder(t->leftChild);
		linkedBinaryTree<E>::visit(t);
		inOrder(t->rightChild);
	}
}

template<typename E>
void linkedBinaryTree<E>::postOrder(binaryTreeNode<E> *t)
{// 后序遍历
	if (t != nullptr)
	{
		postOrder(t->leftChild);
		postOrder(t->rightChild);
		linkedBinaryTree<E>::visit(t);
	}
}

template<typename E>
void levelOrder(binaryTreeNode<E> *t)
{// 层次遍历
	arrayQueue<binaryTreeNode<E>*> q;
	while (t != nullptr)
	{
		linkedBinaryTree<E>::visit(t);     // 访问 t

		// 将 t 的孩子插入队列
		if (t->leftChild != nullptr)
			q.push(t->leftChild);
		if (t->rightChild != nullptr)
			q.push(t->rightChild);

		// 提取下一个要访问的节点
		try{ t = q.front(); }
		catch (queueEmpty) { return; }
		q.pop();                          // 删除首元素
	}
}

template<typename E>
int linkedBinaryTree<E>::height(binaryTreeNode<E> *t) const
{// 返回根位 *t 的树的高度
	if (t == nullptr)
		return 0;                    // 空树
	int hl = height(t->leftChild);   // 左树高
	int hr = height(t->rightChild);  // 右树高
	if (hl > hr)
		return ++hl;
	else
		return ++hr;
}

#endif // !LINKEDBINARYTREE_H

huffmanNode.h

#ifndef HUFFMANNODE_H
#define HUFFMANNODE_H

template<class T>
struct huffmanNode
{
   linkedBinaryTree<int> *tree;
   T weight;                    // 权值域

   operator T () const {return weight;}
};

#endif

minHeap.h

#ifndef MINHEAP_H
#define MINHEAP_H

#include "changeLength1D.h"
#include "minPriorityQueue.h"
#include "myExceptions.h"
#include <sstream>
#include <algorithm>

using namespace std;

template<class T>
class minHeap : public minPriorityQueue<T>
{
   public:
      minHeap(int initialCapacity = 10);
      ~minHeap() {delete [] heap;}
      bool empty() const {return heapSize == 0;}
      int size() const
          {return heapSize;}
      const T& top()
         {// 返回最大元素
            if (heapSize == 0)
               throw queueEmpty();
            return heap[1];
         }
      void pop();
      void push(const T&);
      void initialize(T *, int);
      void deactivateArray()
         {heap = NULL; arrayLength = heapSize = 0;}
      void output(ostream& out) const;
   private:
      int heapSize;       // 队列中的元素数
      int arrayLength;    // 队列容量 + 1
      T *heap;            // 元素数组
};

template<class T>
minHeap<T>::minHeap(int initialCapacity)
{// 构造函数
   if (initialCapacity < 1)
   {ostringstream s;
    s << "Initial capacity = " << initialCapacity << " Must be > 0";
    throw illegalParameterValue(s.str());
   }
   arrayLength = initialCapacity + 1;
   heap = new T[arrayLength];
   heapSize = 0;
}

template<typename T>
void minHeap<T>::push(const T& theElement)
{// 把元素 theElement 加入堆

    // 必要时增加数组长度
    if (heapSize == arrayLength - 1)
    {// 长度 * 2
        changeLength1D(heap, arrayLength, 2 * arrayLength);
        arrayLength *= 2;
    }
    
    // 为元素 theElement 寻找插入位置
    // currentNode 从新叶子向上移动
    int currentNode = ++heapSize;
    while (currentNode != 1 && heap[currentNode / 2] > theElement)
    {
        // 不能把元素 theElement 插入在 heap[currentNode]
        heap[currentNode] = heap[currentNode / 2];           // 把元素向下移动
        currentNode /= 2;                                    // currentNoed 移向双亲
    }

    heap[currentNode] = theElement;
}

template<typename T>
void minHeap<T>::pop()
{// 删除最大元素
    // 如果堆为空,抛出异常
    if (heapSize == 0)     // 堆空
        throw queueEmpty();

    // 删除最大元素
    heap[1].~T();

    // 删除最后一个元素,然后重建堆
    T lastElement = heap[heapSize--];

    // 从根开始,为最后一个元素寻找位置
    int currentNode = 1,
        child = 2;                         // currentNode 的孩子
    while (child <= heapSize)
    {
        // heap[child] 应该是 currentNode 的更大的孩子
        if (child < heapSize && heap[child] > heap[child + 1])
            child++;

        // 可以把 lastElement 放在 heap[currentNode] 吗?
        if (lastElement <= heap[child])
            break;                          // 可以

        // 不可以
        heap[currentNode] = heap[child];      // 把孩子 child 向上移动
        currentNode = child;                  // 向下移动一层寻找位置
        child *= 2;
    }
    heap[currentNode] = lastElement;
}

template<typename T>
void minHeap<T>::initialize(T *theHeap, int theSize)
{// 在数组 theHeap[1:theSize] 中建小根堆
    delete [] heap;
    heap = theHeap;
    heapSize = theSize;

    // 堆化
    for (int root = heapSize / 2; root >= 1; root--)
    {
        T rootElement = heap[root];

        // 为元素 rootElement 寻找位置
        int child = 2 * root;     // 孩子 child 的双亲是元素 rootElement 的位置 
        while (child <= heapSize)
        {
            // heap[child] 应该是兄弟中较大者
            if (child < heapSize && heap[child] > heap[child + 1])
                child++;

            // 可以把元素 rootElement 放在 heap[child / 2] 吗
            if (rootElement <= heap[child])
                break;

            // 不可以
            heap[child / 2] = heap[child];          // 把孩子向上移
            child *= 2;                             // 移到下一层
        }
        heap[child / 2] = rootElement;
    }
}

template<class T>
void minHeap<T>::output(ostream& out) const
{// 将列表放入流中并输出
   copy(heap + 1, heap + heapSize + 1, ostream_iterator<T>(cout, "  "));
}

// 重载 <<
template <class T>
ostream& operator<<(ostream& out, const minHeap<T>& x)
   {x.output(out); return out;}

#endif // !MAXHEAP_H

huffmanTree.h

#ifndef HUFFMANTREE_H
#define HUFFMANTREE_H
#include "linkedBinaryTree.h"
#include "huffmanNode.h"
#include "minHeap.h"

template<typename T>
linkedBinaryTree<T>* huffmanTree(T weight[], int n)
{// 用权 weight[1:n] 生成霍夫曼树,n >= 1
	// 创建一组单节点树
	huffmanNode<T> *hNode = new huffmanNode<T> [n + 1];
	linkedBinaryTree<T> emptyTree;
	for (int i = 1; i <= n; i++)
	{
		hNode[i].weight = weight[i];
		hNode[i].tree = new linkedBinaryTree<int>;
		hNode[i].tree->makeTree(i, emptyTree, emptyTree);
	}

	// 使一组单节点树构成小根堆
	minHeap<huffmanNode<T>> heap(1);
	heap.initialize(hNode, n);

	// 不断从小根堆中提取两个树合并,直到剩下一棵树
	huffmanNode<T> w, x, y;
	linkedBinaryTree<int> *z;
	for (int i = 1; i < n; i++)
	{
		// 从小根堆中取出两个最轻的树
		x = heap.top();
		heap.pop();
		y = heap.top();
		heap.pop();

		// 合并成一棵树
		z = new linkedBinaryTree<int>;
		z->makeTree(0, *x.tree, *y.tree);
		w.weight = x.weight + y.weight;
		w.tree = z;
		heap.push(w);
		delete x.tree;
		delete y.tree;
	}
}

#endif // !HUFFMANTREE_H

huffmanTree 函数的复杂性:

  创建数组 hNode 的时间是 Ο(n)。第一个 for 循环和堆的初始化时间也是 O(n)。在第二个 for 循环中,共有 2(n-1) 次的 top 操作、2(n-1) 次的 pop 操作和 n-1 次的 push 操作,时间为 O(nlogn)。其余部分的时间为 Θ(n)。因此 huffmanTree 函数的总时间 O(nlogn)。


版权声明:本文为w188123135原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。