数据结构与算法-优先级队列

优先级队列

概念

搜索树结构和词典结构,都支持覆盖数据全集的访问和操作。也就是说,其中存储的每一数据对象都可作为查找和访问目标
优先级队列,较之此前的数据结构反而有所削弱。具体地,这类结构将操作对象限定于当前的全局极值者。而维护一个偏序关系

  • 接口
template <typename T> struct PQ {
	//优先级队列PQ模板类
	virtual void insert ( T ) = 0;
	//按照比较器确定的优先级次序插入词条
	virtual T getMax() = 0;
	//叏出优先级最高的词条
	virtual T delMax() = 0;
	//删除除优先级最高的词条

};
  • 应用
/******************************************************************************************
2 * Huffman树极造算法:对传入癿Huffman森枃forest逐步合幵,直刡成为一棵树
3 ******************************************************************************************
4 * forest基亍优先级队列实现,此算法适用亍符合PQ接口癿仸何实现斱式
5 * 为Huffman_PQ_List、Huffman_PQ_ComplHeap和Huffman_PQ_LeftHeap共用
6 * 编译前对应工程叧需讴置相应标志:DSA_PQ_List、DSA_PQ_ComplHeap戒DSA_PQ_LeftHeap
7 ******************************************************************************************/
HuffTree* generateTree ( HuffForest* forest ) {
	while ( 1 < forest->size() ) {
		HuffTree* s1 = forest->delMax();
		HuffTree* s2 = forest->delMax();
		HuffTree* s = new HuffTree();
		s->insertAsRoot ( Huffchar ( '^', s1->root()->data.weight + s2->root()->data.weight ) );
		s->attachAsLC ( s->root(), s1 );
		s->attachAsRC ( s->root(), s2 );
		forest->insert ( s );
		//将合幵后癿Huffman树揑回Huffman森枃
	}
	HuffTree* tree = forest->delMax();
	//至此,森枃中癿最后一棵树
	return tree;
	//即全尿Huffman编码树
}

完全二叉堆

  1. 什么是堆
  • 它是一个完全二叉树,即除了最后一层节点不是满的,其他层节点都是满的,即左右节点都有。

  • 它不是二叉搜索树,即左节点的值都比父节点值小,右节点的值都不比父节点值小,这样查找的时候,就可以通过二分的方式,效率是(log N)。

  • 它是特殊的二叉树,它要求父节点的值不能小于子节点的值。这样保证大的值在上面,小的值在下面。所以堆遍历和查找都是低效的,因为我们只知道从根节点到子叶节点的每条路径都是降序的,但是各个路径之间都是没有联系的,查找一个值时,你不知道应该从左节点查找还是从右节点开始查找。

  • 它可以实现快速的插入和删除,效率都在(log N)左右。所以它可以实现优先级队列。

  1. 大顶堆与小顶堆
    堆序性也可对称地约定为“堆顶以外的每个节点都不低(小)于其父节点”,此时同理,优先级最低的词条,必然始终处于堆顶位置。为以示区别,通常称前(后)者为大(小)顶堆

  2. 模板类与一些常用方法

//必要的方法
int Parent(int i){
	//PQ[i]的父节点(floor((i-1)/2),i无论正负)
	return ((i - 1) >> 1);
}
int LChild(int i){
	//PQ[i]的左孩子
	return (1 + (i << 1));
}
int RChild(int i){
	//PQ[i]的右孩子
	return ((i + 1) << 1);
}
Boolean InHeap(int n,int i){
	//判断PQ[i]是否合法
	return ((-1 < i) && (i < n));
}
Boolean ParentValid(int i){
	return (Parent(i) >= 0);
}
Boolean LChildValid(int n,int i){
	//判断PQ[i]是否有一个(左)孩子
	return InHeap(n,LChild(i));
}
Boolean RChildValid(int n,int i){
	//判断PQ[i]是否有两个孩子
	return InHeap(n,RChild(i));
}
int Bigger(Vector<T> PQ,int i,int j){
	//取大者(等时前者优先)
	return (lt(PQ.elementAt(i),PQ.elementAt(j)) ? j : i);
}
int ProperParent(Vector<T> PQ,int n, int i){
	/*父子(至多)三者中的大者*/
	return (RChildValid(n,i) ? Bigger(PQ,Bigger(PQ,i,LChild(i)),RChild(i)) :
	                (LChildValid(n,i) ? Bigger(PQ,i,LChild(i)) : i));
}
//相等时父节点优先,如此可避免不必要的交换
Boolean lt(T a,T b){
	return ((int)a > (int)b);
}
void swap(T a,T b){
	T c = a;
	a = b;
	b = c;
}
void copyFrom(T[] A,int lo,int hi){
	int i = 0;
	while (lo < hi){
		this.set(i++, A[lo++]);
	}
}
int LastInternal(int n){
	return ((n + 1) / 2 - 1);
}

元素插入

  • 原理

  • 实现

@Override
    public void insert(T e) {
	//插入词条
	this.addElement(e);
	percolateUp(elementCount - 1);
}
protected int percolateUp(int i){
	//上滤
	while (ParentValid(i)){
		int j = Parent(i);
		if (lt(elementAt(i),elementAt(j))) break;
		swap(elementAt(i),elementAt(j));
		i = j;
	}
	return i;
}
  • 实例

元素删除

  • 原理

  • 实现

@Override
    public T delMax() {
	//删除词条
	T maxElem = elementAt(0);
	this.setElementAt(elementAt(elementCount - 1),0);
	this.remove(--elementCount);
	percolateDown(elementCount,0);
	return maxElem;
}
protected int percolateDown(int n,int i){
	//下滤
	int j = 0;
	while (i != (j = ProperParent(this,n,j))){
		swap(elementAt(i),elementAt(j));
		i = j;
	}
	return i;
}
  • 实例

批量建堆

  • 原理

  • 实现

public  PQ_CompHeap(T[] A, int n){
	//批量构造
	copyFrom(A,0,n);
	heapify(n);
}
protected void heapify(int n){
	//Floyd建堆算法
	for (int i = LastInternal(n);i >= 0;i--){
		//自下而上,依次
		percolateDown(n,i);
		//下滤各内部节点
	}
}
protected int percolateDown(int n,int i){
	//下滤
	int j = 0;
	while (i != (j = ProperParent(this,n,j))){
		swap(elementAt(i),elementAt(j));
		i = j;
	}
	return i;
}
  • 实例

就地堆排序

  • 原理

  • 实现

//堆排序
public void heapSort(T[] A,int lo,int hi){
	copyFrom(A,lo,hi);
	int n = hi - lo;
	heapify(n);
	while (0 < --n){
		swap(A[0],A[n]);
		percolateDown(n,0);
	}
}
  • 实例

左视堆

概念

对于堆的合并来说,可以想到的有两种方法:

  • 简单的取出并插入

  • 将两个堆中词条视为无顺序的,用堆合并

  • 堆不需要与二叉树一样保持平衡
    左视堆:左式堆是优先级队列的另一实现方式,可高效地支持堆合并操作。其基本思路是:在保持堆序性的前提下附加新的条件,使得在堆的合并过程中,只需调整很少量的节点。具体地,需参与调整的节点不超过O(logn)个,故可达到极高的效率。

模板类

使用的是二叉树结构

空节点路径长度

左倾性

最右侧通路

package com.atguigu.domin;
import com.atguigu.self.BinNode;
import com.atguigu.self.BinTree;
/**
 * @anthor shkstart
 * @create 2020-08-13 15:07
 */
public class PQ_LeftHeap<T> extends BinTree<T> implements PQ<T> {
	@Override
	    public void insert(T e) {
		//插入元素
		BinNode<T> v = new BinNode<T>(e);
		_root = merge(_root,v);
		_root.parent = null;
		_size++;
	}
	@Override
	    public T getMax() {
		//取出优先级最高的元素
		return _root.data;
	}
	@Override
	    public T delMax() {
		//删除优先级最高的元素
		BinNode<T> lHeap = _root.lc;
		//左子堆
		BinNode<T> rHeap = _root.rc;
		//右子堆
		T e = _root.data;
		//备份堆顶处的最大元素
		_size--;
		//删除一个节点
		_root = merge(lHeap,rHeap);
		//原左右堆合并
		if (_root != null){
			//更新父子链接
			_root.parent = null;
		}
		return e;
		//返回源节点处数据
	}
	public BinNode<T> merge(BinNode<T> a, BinNode<T> b){
		if (a ==null) return b;
		if (b == null) return a;
		if (lt(a.data,b.data)) swap(b,a);
		a.rc = merge(a.rc,b);
		a.rc.parent = a;
		if (a.rc == null || a.rc.npl < a.rc.npl){
			swap(a.lc,a.rc);
		}
		a.npl = (a.rc != null) ? a.rc.npl + 1:1;
		return a;
	}
	//一些必要的方法
	void swap(BinNode<T> a,BinNode<T> b){
		BinNode<T> c = a;
		a = b;
		b = c;
	}
	Boolean lt(T a,T b){
		return ((int)a < (int)b);
	}
}

合并

  • 原理

  • 实现
public BinNode<T> merge(BinNode<T> a, BinNode<T> b){
	if (a ==null) return b;
	if (b == null) return a;
	if (lt(a.data,b.data)) swap(b,a);
	a.rc = merge(a.rc,b);
	a.rc.parent = a;
	if (a.rc == null || a.rc.npl < a.rc.npl){
		swap(a.lc,a.rc);
	}
	a.npl = (a.rc != null) ? a.rc.npl + 1:1;
	return a;
}
//一些必要的方法
void swap(BinNode<T> a,BinNode<T> b){
	BinNode<T> c = a;
	a = b;
	b = c;
}
Boolean lt(T a,T b){
	return ((int)a < (int)b);
}
  • 实例

基于合并的插入和删除

  • 原理

  • 实现
@Override
    public void insert(T e) {
	//插入元素
	BinNode<T> v = new BinNode<T>(e);
	_root = merge(_root,v);
	_root.parent = null;
	_size++;
}
@Override
    public T getMax() {
	//取出优先级最高的元素
	return _root.data;
}
@Override
    public T delMax() {
	//删除优先级最高的元素
	BinNode<T> lHeap = _root.lc;
	//左子堆
	BinNode<T> rHeap = _root.rc;
	//右子堆
	T e = _root.data;
	//备份堆顶处的最大元素
	_size--;
	//删除一个节点
	_root = merge(lHeap,rHeap);
	//原左右堆合并
	if (_root != null){
		//更新父子链接
		_root.parent = null;
	}
	return e;
	//返回源节点处数据
}
  • 实例

锦标赛排序

原始算法

  • 原理

  • 实现

相关链接

https://www.cnblogs.com/binarylei/p/10115921.html#24-堆排序-vs-快速排序

package com.atguigu.domin;
/**
 * @anthor shkstart
 * @create 2020-08-13 16:31
 */
public class TourSort extends PQ_CompHeap{
	public  static final int MAX = 10000;
	private class Node//用node来存储竞赛排序过程中的节点,包括里面的数据和数据在数组中的ID
	{
		public int data;
		public int id;
		public Node()
		        {
		}
		public Node(int data, int id) {
			this.data = data;
			this.id = id;
		}
	}
	public void Adjust(Node[] B, int idx){
		//当去除最小元素以后,我们要调整数组
		while (idx != 0){
			if (idx / 2 == 0){
				if (B[idx].id == -1){
					B[Parent(idx)] = B[idx + 1];
				} else {
					B[Parent(idx)] = B[idx].data > B[idx+1].data ? B[idx+1] : B[idx];
				}
			} else {
				if (B[idx].id == -1){
					B[Parent(idx)] = B[idx - 1];
				} else {
					B[Parent(idx)] = B[idx].data > B[idx-1].data ? B[idx-1] : B[idx];
				}
			}
		}
	}
	public void creat(int[] A) {
		int n = 1;
		while (n < A.length){
			n *= 2;
		}
		int s = n - 1;
		Node[] B = new Node[2*n - 1];
		for (int i = 0;i < n;i++){
			if (i < A.length){
				B[n - 1 + i].data = A[i];
				B[n - 1 + i].id =n - 1 + i;
			} else {
				B[n - 1 + i].data = MAX;
				B[n - 1 + i].id = -1;
			}
		}
		while (--s >= 0){
			if (B[LChild(s)].id == (-1)) {
				B[s] = B[RChild(s)];
			} else if (B[RChild(s)].id == (-1)) {
				B[s] = B[LChild(s)];
			} else {
				B[s] = (B[LChild(s)].data < B[RChild(s)].data) ? B[LChild(s)] : B[RChild(s)];
			}
		}
		for ( int i = 0; i < A.length; i++)//实际排序的过程
		{
			A[i] = B[0].data;
			//取出最小的
			B[B[0].id].id = -1;
			Adjust(B, B[0].id);
		}
	}
}

改进算法

  • 原理

  • 实现(还未写)

多叉堆

  • 原理

  • 效率

  • 实现(还未写)

相关链接

原文地址:https://www.cnblogs.com/suit000001/p/13507596.html