优先队列

优先队列满足两种操作:insert和deletemin(deletemax),insert类似入队列,deletemin类似出队列,最小堆和最大堆是事先决定好的,这里全用最小堆

应用:操作系统进程、外部排序、贪婪算法

实现方式:

1.使用简单链表,表头O(1)插入,deletemin花费O(N),或者始终让链表排序,插入O(N),deleteminO(1),由于删除次数一定小于插入,因此采用O(1)插入,O(N)删除

2.使用二叉查找树,对两种操作都以O(logN)时间完成,缺陷是二叉查找树支持很多在优先队列中不需要的操作,增加了编码复杂性

3.二叉堆

结构性:是完全二叉树

堆序性:某个节点的儿子值比自己大

二叉堆代码如下:

#ifdef _cplusplus
extern "C" {
#endif

#include <stdio.h>
#include <stdlib.h>

struct HeapStruct;
typedef struct HeapStruct * PriorityQueue;
typedef int ElementType;

PriorityQueue Initialize(int MaxElements);
void Destroy(PriorityQueue H);
void MakeEmpty(PriorityQueue H);
void Insert(ElementType X, PriorityQueue H);
ElementType DeleteMin(PriorityQueue H);
ElementType FindMin(PriorityQueue H);
int IsEmpty(PriorityQueue H);
int IsFull(PriorityQueue H);

struct HeapStruct
{
    int Capacity;
    int Size;
    ElementType * Elements;
};

int IsEmpty(PriorityQueue H)
{
    return 0 == H->Size;
}

int IsFull(PriorityQueue H)
{
    return H->Size == H->Capacity;
}

/* 初始化优先队列,特定是有一个哑头,是一个比所有元素都小的元素 */
PriorityQueue Initialize(int MaxElements)
{
    PriorityQueue H;
    H = malloc(sizeof(struct HeapStruct));
    H->Elements = malloc((MaxElements + 1)* sizeof(ElementType));
    H->Capacity = MaxElements;
    H->Size = 0;
    H->Elements[0] = 0x80000000;

    return H;
}

/* 空穴上冒法 */
void Insert(ElementType X, PriorityQueue H)
{
    int i;

    if (IsFull(H))
        return;

    for (i = ++H->Size; H->Elements[i / 2] > X; i /= 2)
        H->Elements[i] = H->Elements[i / 2];
    H->Elements[i] = X;
}

/* 下滤法 */
ElementType DeleteMin(PriorityQueue H)
{
    int i, Child;
    ElementType MinElement, LastElement;

    if (IsEmpty(H))
        return H->Elements[0];

    MinElement = H->Elements[1];
    LastElement = H->Elements[H->Size--];

    for (i = 1; i * 2 <= H->Size; i = Child)
    {
        Child = i * 2;
        /* 在两个儿子中找较小的 */
        if (Child < H->Size && H->Elements[Child] > H->Elements[Child + 1])
            ++Child;

        /* 若最后一个元素比较小的儿子都小,那么可以结束了,否则继续下滤 */
        if (LastElement > H->Elements[Child])
            H->Elements[i] = H->Elements[Child];
        else
            break;
    }
    H->Elements[i] = LastElement;

    return MinElement;
}

/* 将位置为t的节点的值降低X */
void DecreaseKey(int t, ElementType X, PriorityQueue H)
{
    int i, newElement = H->Elements[t] - X;
    for (i = t; H->Elements[i / 2] > newElement; i /= 2)
        H->Elements[i] = H->Elements[i / 2];
    H->Elements[i] = newElement;
}

/* 将位置为t的节点的值增加X */
void IncreaseKey(int t, ElementType X, PriorityQueue H)
{
    int i, Child;
    int newElement = H->Elements[t] - X;

    for (i = t; i * 2 <= H->Size; i = Child)
    {
        Child = i * 2;
        if (Child < H->Size && H->Elements[Child] > H->Elements[Child + 1])
            ++Child;
        if (newElement > H->Elements[Child])
            H->Elements[i]= H->Elements[Child];
        else
            break;
    }
    H->Elements[i] = newElement;
}

/* 删除位置为t的节点,先上浮到跟,然后deletemin */
void Delete(int t, PriorityQueue H)
{
    DecreaseKey(t, 0x7fffffff, H);
    DeleteMin(H);
}

/* 将位置t的节点下滤到合适的位置,是BuildHeap的辅助函数 */
void PercolateDown(int t, PriorityQueue H)
{
    int i, Child;
    int Element = H->Elements[t];

    for (i = t; i * 2 <= H->Size; i = Child)
    {
        Child = i * 2;
        if (Child < H->Size && H->Elements[Child] > H->Elements[Child + 1])
            ++Child;
        if (Element > H->Elements[Child])
            H->Elements[i] = H->Elements[Child];
        else
            break;
    }
    H->Elements[i] = Element;
}

/* 将一个数组中的内容读取到优先队列 */
PriorityQueue BuildHeap(ElementType *a, int N)
{
    int i;
    PriorityQueue H = Initialize(N);

    /* 将数字先全部插进去 */
    for (i = 0; i < N; i++)
        H->Elements[i + 1] = a[i];

    /* 从中间的位置开始下滤,也就是倒数第二层 */
    for (i = N / 2; i > 0; i /= 2)
        PercolateDown(i, H);
}

void main()
{

}

#ifdef _cplusplus
}
#endif

优先队列解决选择问题:

选择问题是N个数中找第k小的,使用优先队列有两种方案

1.将N个数据建立一个最小堆,花费O(N),deletemin操作k次,这样第k次就是想要的结果,每次deletemin花费时间是O(logN),一共花费O(klogN + N)时间,若k接近N / 2,那么时间是O(NlogN)

2.抽取k个数据建立一个最大堆,花费O(k),然后将剩下的N - k个依次和堆的min进行比较,若大,那么不处理,否则deletemin然后插入,每次的花费是O(1)的比较和O(logk)的deletemin,一共花费O(k + (N - k)logK),若k接近N / 2,那么时间是O(NlogN)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值