W. :利用最小优先级队列实现对k个已序队列的合并排序。习题6.5-8

//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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值