优先队列和堆排序
2015-04-08 09:55
218 查看
优先队列
堆
1 基于堆的算法
初始化
自底向上堆化
自顶向下堆化
插入删除一项
2 堆排序
优先队列C语言实现
最大堆的class形式cpp语言实现
数组的堆排序实现
优先队列支持两种基本操作:向优先队列中插入一个新的数据项,删除(最大)优先队列中关键字最大的数据项。 优先队列具有很好的灵活性,能支持的操作:
- 根据N个数据项构造一个优先队列
- 插入一个数据项
- 删除最大值的数据项
- 改变任意给定的数据项的优先级
- 删除任意给定的数据项
- 合并两个优先队列为一个优先队列
排序:先插入所有记录,然后逐个删除最大元素得到逆序的记录。
[注] 研究各种数据结构,我们都将铭记两个基本的权衡策略: 链表内存分配和顺序内存分配(数组)。
不同的实现对于要执行的各种操作具有不同的性能特征,不同的应用问题需要高效的不同操作集的性能。如:有序表删除快插入慢,无序表删除慢插入快。
如果一棵树中每个节点的关键字都大于或等于所有子节点的关键字(如果存在),称树是堆有序的。
堆是一个节点的集合,表示为数组,其中关键字按堆有序的完全二叉树的形式排列。直观想,我们应该用链表表示堆有序的树,但是完全二叉树给了我们使用压缩数组表示的机会。
在位置i处的父节点,两个子节点的位置分别是 2i 和 2i+1.
有两种情况需要修正堆:
(1)某个节点的优先级增加或新节点被插入到堆底,需要向上遍历堆修复
(2)某个节点的优先级降低或删除了最大项,需要向下遍历堆修复
删除时,如果无序表对用与选择排序,有序表对应于插入排序
经典的数组堆排序见本文最后一节
<注意>:模板类的声明和实现必须在一个文件中
main.cpp
假设堆的顶部是从0开始
父节点下标为 i ,则子节点下标为 2*i + 1, 2*i +2
建堆
由上图可以看出,数组中 [ n/2, n-1] 的元素都是堆的叶节点,无需修复;因此我们只需修复[0, n/2-1]的节点,注意应该从后向前(从下到上)修复堆的性质。
排序
上述建堆完成之后,数组中最大值位于堆顶,我们将堆顶元素和数组中最后一个元素交换,此时最大值到达了最终位置;然后修复堆的性质,此时堆的大小是[0, n-1];修复完成后,重复上述步骤。
堆
1 基于堆的算法
初始化
自底向上堆化
自顶向下堆化
插入删除一项
2 堆排序
优先队列C语言实现
最大堆的class形式cpp语言实现
数组的堆排序实现
1 优先队列
普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出 (largest-in,first-out)的行为特征。优先队列支持两种基本操作:向优先队列中插入一个新的数据项,删除(最大)优先队列中关键字最大的数据项。 优先队列具有很好的灵活性,能支持的操作:
- 根据N个数据项构造一个优先队列
- 插入一个数据项
- 删除最大值的数据项
- 改变任意给定的数据项的优先级
- 删除任意给定的数据项
- 合并两个优先队列为一个优先队列
排序:先插入所有记录,然后逐个删除最大元素得到逆序的记录。
[注] 研究各种数据结构,我们都将铭记两个基本的权衡策略: 链表内存分配和顺序内存分配(数组)。
不同的实现对于要执行的各种操作具有不同的性能特征,不同的应用问题需要高效的不同操作集的性能。如:有序表删除快插入慢,无序表删除慢插入快。
2 堆
堆的数据结构,能够支持优先队列的基本操作。在堆中,父节点中的关键字都大于或等于其子节点中的关键字。如果一棵树中每个节点的关键字都大于或等于所有子节点的关键字(如果存在),称树是堆有序的。
堆是一个节点的集合,表示为数组,其中关键字按堆有序的完全二叉树的形式排列。直观想,我们应该用链表表示堆有序的树,但是完全二叉树给了我们使用压缩数组表示的机会。
在位置i处的父节点,两个子节点的位置分别是 2i 和 2i+1.
2.1 基于堆的算法
如果我们对基于堆的优先队列做一点简单修改,可以侵犯堆的条件,然后通过遍历堆,在需要的时候修改堆使其满足堆的条件,我们把这个过程称为堆化或修正堆。有两种情况需要修正堆:
(1)某个节点的优先级增加或新节点被插入到堆底,需要向上遍历堆修复
(2)某个节点的优先级降低或删除了最大项,需要向下遍历堆修复
初始化
//优先队列数据结构 struct PQ { Item data[maxN]; int N; }; //初始化 PQ* PQInit() { PQ* pq = (PQ*)malloc(sizeof(*pq)); pq->N = 0; return pq; }
自底向上堆化
//Insert一项时,从数组尾部插入,向上修复 void PQFixUp(PQ *pq) { int k = pq->N; // k节点的父节点是 k/2 while(k > 1 && less(pq->data[k/2], pq->data[k]))//共比较 lg N 次 { exch(pq->data[k/2], pq->data[k]); k = k/2; } }
自顶向下堆化
如果节点关键字比它的一个或两个子节点的关键字小,那么交换该节点与较大的子节点,这种交换会影响子节点不满足堆性质,所以用同样的方法修正它。//删除最大(顶部)节点,从上向下 fix void PQFixDown(PQ *pq) { int k = 1; int N = pq->N; while(2*k < N) //共比较 2*lg N 次(找更大的子节点lgN, 判断是否交换lgN) { int j = 2*k; if(j < N && less(pq->data[j], pq->data[j+1]))//找较大子节点 j++; if(!less(pq->data[k], pq->data[j])) //父节点不小于最大的子节点时,fix完成 break; exch(pq->data[k], pq->data[j]); k = j; } }
插入、删除一项
//Insert an element,插入到数组尾部,然后向上fix void PQInsert(PQ *pq, Item v) { pq->data[++(pq->N)] = v; PQFixUp(pq); } //delete max-第一个节点;向下fix Item PQDelMax(PQ *pq) { if(PQIsEmpty(pq)) { printf("Error: pq is empty. Can't delete Max\n"); return -1; } exch(pq->data[1], pq->data[pq->N]);//交换最大的与最后一项 (pq->N)--;//剔除最后一项(最大) PQFixDown(pq); return pq->data[pq->N + 1]; }
删除时,如果无序表对用与选择排序,有序表对应于插入排序
2.2 堆排序
对现有堆结构排序经典的数组堆排序见本文最后一节
void PQSort(Item a[], int l, int r) { PQ* pq = PQInit(); int i; for(i = l; i <= r; i++) PQInsert(pq, a[i]); for(i = r; i >= l; i--) a[i] = PQDelMax(pq); }
3 优先队列C语言实现
/*======================================================
Title:基于数组的优先队列实现:k父节点,2k、2k+1为子节点
数据项存储在数组的 1~N 项中。
Functions: 插入一项,向上堆化保持有序;
删除并返回最大项,向下堆化保持有序
Author: quinn
Date: 2015/03/27
=======================================================*/
#include<stdlib.h>
#include<stdio.h>
#define maxN 100 //队列的最大容量
typedef int Item;
typedef struct PQ PQ;
#define exch(A, B) {Item t; t = A; A = B; B = t;}
#define less(A, B) (A < B)
//优先队列数据结构 struct PQ { Item data[maxN]; int N; }; //初始化 PQ* PQInit() { PQ* pq = (PQ*)malloc(sizeof(*pq)); pq->N = 0; return pq; }
bool PQIsEmpty(PQ *pq)
{
return pq->N == 0;
}
//Insert一项时,从数组尾部插入,向上修复
void PQFixUp(PQ *pq)
{
int k = pq->N;
while(k > 1 && less(pq->data[k/2], pq->data[k]))//共比较 lg N 次
{
exch(pq->data[k/2], pq->data[k]);
k = k/2;
}
}
//删除最大(顶部)节点,从上向下 fix
void PQFixDown(PQ *pq)
{
int k = 1;
int N = pq->N;
while(2*k < N) //共比较 2*lg N 次(找更大的子节点lgN, 判断是否交换lgN)
{
int j = 2*k;
if(j < N && less(pq->data[j], pq->data[j+1]))
j++;
if(!less(pq->data[k], pq->data[j])) //父节点不小于最大的子节点时,fix完成
break;
exch(pq->data[k], pq->data[j]);
k = j;
}
}
//Insert an element,插入到数组尾部,然后向上fix
void PQInsert(PQ *pq, Item v)
{
pq->data[++(pq->N)] = v;
PQFixUp(pq);
}
//delete max-第一个节点;向下fix
Item PQDelMax(PQ *pq)
{
if(PQIsEmpty(pq))
{
printf("Error: pq is empty. Can't delete Max\n");
return -1;
}
exch(pq->data[1], pq->data[pq->N]);
(pq->N)--;
PQFixDown(pq);
return pq->data[pq->N + 1];
}
int main()
{
PQ* pq = PQInit();
for(int i = 0; i < 10; i++)
PQInsert(pq, i);
for (int i = 0; i < 11; ++i)
{
printf("%d\n", PQDelMax(pq));
}
}
4 最大堆的class形式cpp语言实现
heap.h<注意>:模板类的声明和实现必须在一个文件中
/* * heap.h : 最大堆的实现 push, top, pop, size, empty * Author: quinn * Date: 2015/07/04 */ #ifndef HEAP_H #define HEAP_H #define maxN 100 template<class T> class MaxHeap { private: T data_[maxN]; int n_; void FixDown(); void FixUp(); void swap(T &, T&); int cmp(const T&, const T&); public: MaxHeap() : n_(0) {}; ~MaxHeap() {}; void push(T item); void pop(); T top(); int size(); bool empty(); }; // 实现最大堆,j > 2*j template<class T> void MaxHeap<T>::push(T item) { data_[++n_] = item; FixUp(); }; template<class T> T MaxHeap<T>::top() { return data_[1]; }; template<class T> void MaxHeap<T>::pop() { if (n_ < 1) return; swap(data_[1], data_[n_]); --n_; FixDown(); }; template<class T> void MaxHeap<T>::FixDown() { int k = 1; while (2 * k < n_) { // 若等于n,则 j+1 == n+1 int j = 2 * k; if (cmp(data_[j], data_[j + 1]) < 0) ++j; if (cmp(data_[k], data_[j]) > 0) break; swap(data_[k], data_[j]); k = j; } }; template<class T> void MaxHeap<T>::FixUp() { int j = n_; while (j > 1) { if (cmp(data_[j / 2], data_[j]) >= 0) break; swap(data_[j], data_[j / 2]); j = j / 2; } }; template<class T> void MaxHeap<T>::swap(T &left, T &right) { T temp = left; left = right; right = temp; }; template<class T> int MaxHeap<T>::cmp(const T &left, const T &right) { if (left == right) return 0; if (left < right) return -1; if (left > right) return 1; }; template<class T> int MaxHeap<T>::size() { return n_; }; template<class T> bool MaxHeap<T>::empty() { if (n_ == 0) return true; return false; } #endif
main.cpp
#include <iostream> #include <stdlib.h> #include "heap.h" using namespace std; int main() { MaxHeap<int> heap; cout << "push item: "; for (int i = 0; i < 10; i++) { int val = rand() % 100; heap.push(val); cout << val << " "; } cout << endl; cout << "heap size = " << heap.size() << endl; cout << "从大到小:"; while (heap.empty() == false) { cout << heap.top() << " "; heap.pop(); } cout << endl; //getchar(); }
4. 数组的堆排序实现
当对数组进行排序时,我们并不需要申请额外的空间去建立堆,而是在数组上原位建堆。假设堆的顶部是从0开始
nums[0] nums[1] nums[2] nums[3] nums[4] nums[5]
父节点下标为 i ,则子节点下标为 2*i + 1, 2*i +2
建堆
由上图可以看出,数组中 [ n/2, n-1] 的元素都是堆的叶节点,无需修复;因此我们只需修复[0, n/2-1]的节点,注意应该从后向前(从下到上)修复堆的性质。
排序
上述建堆完成之后,数组中最大值位于堆顶,我们将堆顶元素和数组中最后一个元素交换,此时最大值到达了最终位置;然后修复堆的性质,此时堆的大小是[0, n-1];修复完成后,重复上述步骤。
#include <iostream> using namespace std; void swap(int &A, int &B) { int temp = A; A = B; B = temp; } // i 为起始修复位置,n 为堆的大小 void FixHeap(int nums[], int i, int n) { if (nums == NULL || i < 0 || n <= 0) return; int temp = nums[i]; // 破坏元素 int j = 2*i + 1; // i 节点的左子结点 while (j < n) { if (j+1 < n && nums[j+1] > nums[j]) // 找出较大的那个子节点 j++; if (temp > nums[j]) break; nums[i] = nums[j]; i = j; j = 2*i + 1; } nums[i] = temp; } void HeapSort(int nums[], int left, int right) { if (nums == NULL || right-left+1 <= 0) return; // 为了简单描述,假设数组是从下标0开始排序 int n = right-left+1; // 建堆,从 n/2-1 ~ 0 修复堆,n/2 ~ n-1 是叶节点 for (int i = n >> 1 - 1; i >= 0; i--) { FixHeap(nums, i, n); } for (int i = n-1; i > 0; i--) { swap(nums[0], nums[i]); FixHeap(nums, 0, i-1); } } int main() { int nums[] = {1,5,3,6,7,3,8,3,12,45,11}; int len = sizeof(nums)/sizeof(nums[0]); HeapSort(nums, 0, len-1); for (int i = 0; i < len; i++) cout << nums[i] << " "; cout << endl; }
相关文章推荐
- 优先队列和堆排序
- 堆排序、优先队列
- 2011-03-04 CLRS Chapter6 Heapsort 堆排序 优先队列
- 优先队列和堆排序
- 堆排序(优先队列)——合并果子
- 算法-优先队列与堆排序
- 完全二叉树,堆,堆排序,优先队列
- 排序算法之堆排序(优先队列)
- 第6章 堆排序,d叉堆,优先队列
- 第六章 堆排序 6.5 优先队列
- 第六章 堆排序 6.5 优先队列
- 堆排序_最大优先队列
- 《算法4》优先队列和堆排序
- 堆和堆的应用:堆排序和优先队列
- 几大内部排序算法(三)--堆(优先队列)和堆排序
- 堆与堆排序—优先队列
- 《常见算法和数据结构》优先队列(3)——堆排序
- 堆排序(优先队列)
- 优先队列和堆排序(转)
- 堆排序,优先队列实现