1. 程式人生 > >##資料結構##如何建立一棵哈夫曼樹並實現堆

##資料結構##如何建立一棵哈夫曼樹並實現堆

哈夫曼樹是一種二叉樹,但是它是一種加權路徑長度最短的二叉樹。

其可用堆來實現它的構建

其構建方法如下:

//heap.h

//仿函式
template<class T>
struct Less
{
	bool operator()(const T& left, const T& right)const 
	{
		return left < right;
	}
};
template<class T>
struct Great
{
	bool operator()(const T& left, const T& right)const
	{
		return left > right;
	}
};
//給模板引數
template<class T,class Compare = Great<T>>
//先實現大堆  後面仿函式再實現小堆
class Heap
{
public:
	//建構函式  無參+有參
	//初始化空堆
	Heap()
	{}
	//把陣列調成堆   
	Heap(T* a,size_t n)
	{
		//開空間   不把size增容  且對於空間不初始化
		_a.reserve(n);
		for (size_t i = 0; i < n; ++i)
		{
			//把數組裡的數字放在vector裡
			_a.push_back(a[i]);
		}
		//建堆  構建成一個大堆  從最後一個非葉結點的父親開始 
		for (int i = (_a.size() - 2) / 2; i >= 0; --i)
		{
			//向下調整演算法 調成大堆
			AdjustDown(i);
		}
	}
	//即插入資料  從最後一個插入之後進行比較

	void Push(const T& x)
	{
		_a.push_back(x);
		AdjustUp(_a.size()-1);
	}
	void Pop()
	{
		//把最後一個節點和第一個節點進行交換   然後再進行向下調整
		swap(_a[0], _a[_a.size() - 1]);
		_a.pop_back();
		AdjustDown(0);
	}
	//向下調整法 條件: 左右子樹必須是大堆
	void AdjustDown(int root)
	{
		Compare com;
		int parent = root;
		int child = (parent * 2) + 1;
		//停止的條件是孩子存在   即繼續的條件是孩子存在
		while (child<_a.size())
		{
			//找左孩子右孩子大的那個  假設右孩子大於左孩子  右孩子可能不存在 需要一個判斷
			//if (child+1<_a.size()&&_a[child+1] > _a[child])
			if (child + 1<_a.size() && com(_a[child + 1] , _a[child]))

			{
				child++;
			}
			//走到這  就指向大的那個孩子  如果孩子大於父親節點  說明不滿足大堆  即交換  交換完之後父親和孩子節點要交換
			//if (_a[child]> _a[parent])
			if (com(_a[child], _a[parent]))
			{
				swap(_a[parent], _a[child]);
				parent = child;
				child = parent * 2 + 1;
			}
			//如果父親節點大於大的那個孩子  說明父親節點已經是最大的  不需要交換 即退出迴圈
			else
			{
				break;
			}
		}
	}
	//向上調整法從最後一個節點開始  向上調整
	void AdjustUp(int child)
	{
		//定義一個型別的物件
		Compare com;
		int parent = (child - 1) / 2;
		while (child > 0)
		{
			//if (_a[child] >_a[parent])
			if (com(_a[child], _a[parent]))
			{
				swap(_a[parent], _a[child]);
				child = parent;
				parent = (child - 1) / 2;
			}
			else
			{
				break;
			}
		}
	}
	bool Empty()
	{
		return _a.empty();
	}
	size_t Size()
	{
		return _a.size();
	}
	T& Top()
	{
		//判斷size是否大於0 如果不大於0則沒有資料那麼就取不了堆頂
		assert(_a.size() > 0);
		return _a[0];
	}
	//判斷是否是堆 如果是大堆  父親節點大於孩子節點
	bool IsHeap()
	{
		//可以用遞迴實現
		//return  _IsHeap();
		//遍歷 判斷父親節點是否大於孩子節點
		for (size_t i = 0; i < (_a.size() - 2) / 2; ++i)
		{

			if (_a[i] < _a[i * 2 + 1] || ((i*2+2)<_a.size()&&_a[i] < _a[i * 2 + 2]))
				return false;
		}
		return true;
	}
	//建立小堆  
	void AdjustDown(int* heap, size_t k, size_t root)
	{
		assert(heap);
		int parent = root;
		int child = parent * 2 + 1;
		while (child < k)
		{
			if (heap[child + 1] < heap[child])
				++child;
			if (heap[parent]>heap[child])
			{
				swap(heap[parent], heap[child]);
				parent = child;
				child = parent * 2 + 1;
			}
			else
			{
				break;
			}
		}
	}
protected:
	vector<T> _a;
	
};

#include"heap.h"

//加權路徑最優二叉樹
template<class W>
struct HuffmanNode
{
	HuffmanNode<W>* _left;
	HuffmanNode<W>* _right;
	HuffmanNode<W>* _parent;

	W _w;//權值
	HuffmanNode(const W& w) :_w(w), _left(NULL), _right(NULL),_parent(NULL)
	{}
};
template<class W>
class HuffmanTree
{
	typedef HuffmanNode<W> Node;
public:
	//建構函式
	HuffmanTree() :root(NULL)
	{}
	HuffmanTree(W* a, size_t n,const W& invalid)
	{
		//有問題! 不能這麼構建  型別不能放權值  需要放節點
		//Heap<W> minHeap(a, n);		
		/*	W left;
			W right;*/

		//內部類  作用域受到類域的限制
		struct NodeCompare
		{
			bool operator()(const Node* left, const Node* right)
			{
				return left->_w < right->_w;//調的過載的<
			}
		};
		Heap<Node*,NodeCompare> minHeap;
	
		for (size_t i = 0; i < n; ++i)
		{
				if (a[i] != invalid)
				{
				minHeap.Push(new Node(a[i]));
			}
		}
		//如何選出節點最小的兩個節點??
		//每次選權值最小的兩個節點其之和構建父親
		while (minHeap.Size()>1){
			Node* left = minHeap.Top();
			minHeap.Pop();
			Node* right = minHeap.Top();
			minHeap.Pop();

			//count相加
			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* GetRoot()
	{
		return _root;
	}
protected:
	Node* _root;
};
//思考給的值為什麼都在葉子節點??
void testHuffmantree()
{
	int a[] = { 1, 2, 3, 4, 5, 6, 7, 8 ,-1};
	HuffmanTree<int> t1(a,sizeof(a)/sizeof(a[0]),-1);
}
如有不妥,歡迎指正。