//author: W.
//利用最小优先级队列实现对k个已序队列的合并排序。6.5-8,P82
//时间复杂度:O(nlgk)
//因为优先级队列的操作时间为lgk,共需要n次操作,所以是O(nlgk)
//算法思想:由于k个队列是已序的,那么取出每个队列中的最小元素组建成最小堆,该最小堆包含k个元素
//每次取出最小堆的堆顶元素,即最小值,放入输出队列中。
//然后二种情况:1)该元素不是其所属队列的最末元素,则将该元素所属的队列中下一个元素插入最小堆,则最小堆仍为k个元素
// 2)该元素是其所属队列的最末元素,则最小堆元素个数数减少了一个
//按照上述运行,当所有的队列都完成最末元素时,最小堆元素个数为0,此时输出队列中包含了所有队列的元素,且已排序
//
//注意:该程序中的输入队列是用数组给出,输出也是在数组中。
//如果输入是链表的形式,那么该方法也可以实现。如果输出是直接输出到某地方(比如直接打印到终端)那么该方法也可以实现。
//可见这种利用堆的方式进行排序是有很多优点的。
//一个最小优先级队列包含以下几个操作
//INSERT(S, x) :把元素x插入集合S. 时间复杂度O(lgn)
//MINIMUM(S):返回S中具有最小关键字的元素.时间复杂度O(1)
//EXTRACT-MIN(S):去掉并返回S中的具有最小关键字的元素.时间复杂度O(lgn)
//INCREASE-KEY(S, x, k):将元素x的关键字的值减小到k,这里k值不能大于x的原关键字的值.时间复杂度O(lgn)
//可见优先级队列操作的任意操作可在O(lgn)时间内完成。
#include <stdio.h>
#include <stdlib.h>
//对于优先级序列中存储的数据,书中描述“需要在对中的每个元素里存储对应的应用对象的柄。同样的,我们需要将堆中元素的柄存储到其对应的应用对象中”
//该结构中无需了解该元素在堆中的位置,因为只需要插入和提取的操作,所以不需要记录heap_index
//key为待排序数值
//array为该值所在的数组
//array_index为该值所在数组中所在的索引位置
//array_length为array的长度
typedef struct{
int key;
int* array;
int array_index;
int array_length;
} Data;
//每个待排序的数组用int S[]来表示
//下面结构存储了某个待排序的数组的头地址和数组长度,以便传递待排序数组
typedef struct{
int* S;
int length;
} InputArray;
typedef Data* PData;
#define NULLPDATA NULL
PData HEAP_MINIMUM(const PData A[], const int heap_size)
{
if(heap_size < 1)
{
return NULLPDATA;
}
return A[0];
}
void ElemCopy(PData A[], int dest_index, int src_index)
{
A[dest_index] = A[src_index];
}
void ElemSwap(PData A[], int index1, int index2)
{
PData temp;
temp = A[index1];
A[index1] = A[index2];
A[index2] = temp;
}
void MinHeapify(PData A[], const int length, int i)
{
int left = 2 * i + 1; //见P71,注意书上是根从1开始计数的,而程序中的数组下标是从0开始计数的
int right = 2 * i + 2;
int largest = i;
if((left <= length-1) && (A[left]->key < A[largest]->key))
{
largest = left;
}
if((right <= length-1) && (A[right]->key < A[largest]->key))
{
largest = right;
}
if(largest != i)
{
ElemSwap(A, i, largest);
MinHeapify(A, length, largest);
}
}
PData HEAP_EXTRACT_MIN(PData A[], int* pheap_size)
{
if(*pheap_size < 1)
{
return NULLPDATA;
}
PData max = A[0];
ElemCopy(A, 0, *pheap_size-1);
--(*pheap_size);
MinHeapify(A, *pheap_size, 0);
return max;
}
void HEAP_INCREASE_KEY(PData A[], int index, int key)
{
if(key > A[index]->key)
{
return ;//更新的key必须小于等于原key。(key表示优先级)
}
A[index]->key = key;
while((index > 0) && (A[(index-1)/2]->key > A[index]->key)) //index的父结点的下标为(i-1)/2,见P71,注意书上是根从1开始计数的,而程序中的数组下标是从0开始计数的
{
ElemSwap(A, index, (index-1)/2);
index = (index-1)/2;
}
}
void MIN_HEAP_INSERT(PData A[], int* pheap_size, PData newdata)
{
++(*pheap_size);
A[*pheap_size-1] = newdata;
HEAP_INCREASE_KEY(A, *pheap_size-1, A[*pheap_size-1]->key);
}
//heap为排序需要使用的堆,该堆空间应该提前分配,因为堆空间最大只需count个元素
//all_array为待排序的列表头组成的数组
//count为待排序数组个数
void InitHeap(PData heap[], InputArray all_array[], const int count)
{
int i;
int heap_size = 0;
for(i = 0; i < count; ++i)
{
PData pdata = (PData)malloc(sizeof(Data));
pdata->array = all_array[i].S;
pdata->array_length = all_array[i].length;
pdata->array_index = 0;
pdata->key = pdata->array[pdata->array_index];
MIN_HEAP_INSERT(heap, &heap_size, pdata);
}
}
int Sort(PData heap[], const int length, int outputArray[])
{
PData pdata;
int heap_size = length;
int output_index = 0;
//初始化堆是最小堆,堆中每个元素所处的数据结构都正确。outputArray中无元素,则是已排列。
while((pdata = HEAP_EXTRACT_MIN(heap, &heap_size)) != NULLPDATA) //保持:每次循环时保持heap为最小堆,并使得堆中的每个元素的结构都是正确的。当某个元素的key已经是该key所在数组的最末元素时,则删除该堆元素,即堆长度减少1。否则把新的key构建出新的data插入堆中。每次执行完循环时outputArray[0]~outputArray[output_index-1]都是从小到大排列的.
{
outputArray[output_index++] = pdata->key;
if(++pdata->array_index != pdata->array_length)
{
pdata->key = pdata->array[pdata->array_index];
MIN_HEAP_INSERT(heap, &heap_size, pdata);
}
else
{
free(pdata);
}
}
//结束:堆为空,堆中分配的空间已经都释放。output_index为所有链表的元素和(n)则从outputArray[0]~outputArray[n-1]已排序
return output_index; //返回总元素数
}
void test_MinPriorityQueueForSort()
{
int i;
int a1[] = {12, 13, 14, 22, 23, 24};
int a2[] = {17, 18, 19, 27, 28, 29};
int a3[] = {2, 3, 4, 5, 6, 15, 16};
int a4[] = {51, 52, 53, 54, 55, 56, 57, 58, 59};
int a5[] = {7, 8, 9, 10, 111, 223};
InputArray input_array[5];
input_array[0].S = a1;
input_array[0].length = sizeof(a1)/sizeof(int);
input_array[1].S = a2;
input_array[1].length = sizeof(a2)/sizeof(int);
input_array[2].S = a3;
input_array[2].length = sizeof(a3)/sizeof(int);
input_array[3].S = a4;
input_array[3].length = sizeof(a4)/sizeof(int);
input_array[4].S = a5;
input_array[4].length = sizeof(a5)/sizeof(int);
int input_count = 0;
for(i = 0; i < 5; ++i)
{
input_count += input_array[i].length;
}
printf("input_count = %d/n", input_count);
PData heap[5];
int output[100];
int output_count = 0;
InitHeap(heap, input_array, 5);
output_count = Sort(heap, 5, output);
printf("output_count = %d/n", output_count);
for(i = 0; i < output_count; ++i)
{
printf("%d ", output[i]);
}
printf("/n");
}
int main(int argc, char** argv)
{
test_MinPriorityQueueForSort();
return 0;
}
//输出:
//input_count = 34
//output_count = 34
//2 3 4 5 6 7 8 9 10 12 13 14 15 16 17 18 19 22 23 24 27 28 29 51 52 53 54 55 56 57 58 59 111 223