886数据结构-笔记三(2)-二叉树-续笔记三(1)

堆与优先队列

定义

对于一个关键码序列{K0,K1,···,Kn-1},如果满足Ki>=K2i+1,Ki>=K2i+2 (i=0, 1, ···, n/2 - 1),则称其为堆, 而且这是最大值堆;
对应的,如果满足Ki<=K2i+1,Ki<=K2i+2 (i=0, 1, ···, n/2 - 1),则为最小值堆

性质

最大值堆:
任意一个结点的值都大于或等于其任意一个子结点存储的值;
由于根结点包含大于或等于其子结点的值,而其子结点又依次大于或等于各自子结点的值 --> 根结点存储着该树所有结点中的最大值。
最小值堆:
任意一个结点的值都小于或等于其任意一个子结点存储的值;
由于根结点包含小于或等于其子结点的值,而其子结点又依次小于或等于各自子结点的值 --> 根结点存储着该树所有结点中的最小值。
886数据结构-笔记三(2)-二叉树-续笔记三(1)_第1张图片

实现(最大值堆)

template<class Elem,class Comp> class maxheap {
     
	private:
		Elem* Heap;				//指向堆的指针
		int size;				//堆中结点数最大为size
		int n;					//堆中当前结点数
		void shiftdowm(int);	//实现最大值堆,使各结点移到适当的位置
	public:
		maxheap(Elem* h, int num, int max) {
     
			Heap = h;
			n = num;
			size = max;
			buildHeap();
		}
		int heapsize() const {
     
			return n;
		}
		bool isLeaf(int pos) const {
     
			return (pos >= n/2) && (pos < n);
		}
		int leftchild(int pos) const {
     
			return 2*pos + 1;
		}
		int rightchild(int pos) const {
     
			return 2*pos + 2;
		}
		int parent(int pos) const {
     
			return (pos-1)/2;
		}
		bool insert(const Elem&);
		bool removemax(const Elem&);
		bool remove(const Elem&);
		void buildHeap() {
     
			for(int i=n/2-1; i>=0; i--)
				shiftdown(i);
		}
}

堆的形成:
· 不必将值一个个地插入堆中,通过交换形成堆;
· 假设根的左、右子树都已是堆,并且根的元素名为R,此时,有两种可能

  1. R的值大于或等于其两个子女,那么此时堆已完成;
  2. R的值小于其某一个或全部两个子女的值,此时R应与两个子女中值较大的一个交换,结果得到一个堆,除非R仍然小于其新子女的一个或全部的两个。
    这种情况下,我们只需简单地继续这种将R"拉下来"的过程,直至到达某一个层使它大于它的子女,或者它成了叶结点。

shiftdown

从堆的第一个非叶子结点(heap[n/2-1]),自底向上逐步把以各分支结点为根的子树调整成堆。
当调整到某一个位置heap[pos]时,由于其子树都已经是堆,
· 如果它正好满足堆定义,不用调整;
· 否则取其子树结点中较大的值与heap[pos]交换;
· 交换后此子树根可能不满足堆定义,但其子树还是堆,这样,可以继续一层层交换下去,最多到叶停止(过筛)

最大值堆建堆图示:
886数据结构-笔记三(2)-二叉树-续笔记三(1)_第2张图片
最小值堆建堆图示:
886数据结构-笔记三(2)-二叉树-续笔记三(1)_第3张图片

shiftdown(int pos) {
     
	while(!isLeaf(pos)) {
     	// stop if pos is a leaf
		int j = leftchild(pos);
		int rc = rightchild(pos);
		if((rc<n) && Comp::It(Heap[j],Heap[rc]))
			j = rc; 	// set j to greater child's value
		if(!Copm::It(Heap[pos],Heap[j])
			return ;
		swap(Heap,pos,j);
		pos = j;
	}
}

// 删除最大值
bool removemax(Elem& it) {
     
	if(n==0) return false;	// Heap is empty
	swap(Heap, 0, --n);		// swap max with end
	if(n!=0) shiftdown(0);
	it = Heap[n];
	return true;
}

// 插入
bool insert(const Elem& val) {
     
	if(n>=size) return false;	//Heap is full
	int curr = n++;
	Heap[curr] = val;			//start at end of heap
	// now shift up until curr's parent > curr
	while((curr!=0) && (Comp::gt(Heap[curr],Heap[parent(curr)]))) {
     
		swap(Heap,curr,parent(curr));
		curr = parent(curr)
	}
	return true;
}	

建堆操作的效率

886数据结构-笔记三(2)-二叉树-续笔记三(1)_第4张图片

具体计算过程如下:
886数据结构-笔记三(2)-二叉树-续笔记三(1)_第5张图片


Huffman编码树

  1. 固定长度编码 - fixed-length coding scheme
    · 设所有代码都等长,则表示n个不同的代码需要log2n bit – 固定长度编码 ,如 ASCII码
    · 如果每个字符的使用频率相等的话,固定长度编码是空间效率最高的方法
  2. 数据压缩和不等长编码
    · 利用字母的出现频率来编码,使得经常出现的字母的编码较短,反之不常出现的字母编码较长
    · 数据压缩既能节省磁盘空间,又能提高运算速度 (外存时空权衡的规则)
    · 不等长编码是今天广泛使用的文件压缩技术的核心
    · Huffman编码是最简单的文件压缩技术,它给出了不等长编码的思想

建立Huffman编码树

对于n个字符K0,K1,······,Kn-1,它们的使用频率分别为w0,w1,······,wn-1,请给出它们的编码,使得总编码效率最高。

一个树叶的带权路径长度(Weighted path length) = 权 * 路径长度(即树叶的深度)

Huffman建树:

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

(1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);

(2) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;

(3)从森林中删除选取的两棵树,并将新树加入森林;

(4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。
886数据结构-笔记三(2)-二叉树-续笔记三(1)_第6张图片
886数据结构-笔记三(2)-二叉树-续笔记三(1)_第7张图片
886数据结构-笔记三(2)-二叉树-续笔记三(1)_第8张图片

你可能感兴趣的:(数据结构,数据结构,算法)