优先队列和堆排序

    • 优先队列
      • 1 基于堆的算法

        • 初始化
        • 自底向上堆化
        • 自顶向下堆化
        • 插入删除一项
      • 2 堆排序
    • 优先队列全部代码

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 优先队列全部代码

/*======================================================
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));
    }

}
时间: 2024-10-07 11:39:57

优先队列和堆排序的相关文章

算法2.4——优先队列与堆排序

优先队列(Priority Queues)的使用和队列(删除最老的元素)以及栈(删除最新的元素)类似.举个实用例子:也就是从10亿个元素中选出最大的10个,有了优先队列,就只需要用一个能存储10个元素的队列即可. 而二叉堆很好实现优先队列的基本操作.其中二叉堆是一组能够用队友徐的完全二叉树排序的元素.其中关于堆的算法,有上浮swim和下沉sink,另外一般来说A[0]不使用,直接使用A[1]. 堆排序的实现 实现堆排序需要解决两个问题: 1.如何由一个无序序列建成一个堆? 2.如何在输出堆顶元素

优先队列之堆排序

1.最大堆 import java.util.Scanner ; public class MaxPQ<Key extends Comparable<Key>>{ private int N = 0 ; private Key[] pq ; public MaxPQ(int max){ pq = (Key[])new Comparable[max+1] ; } public MaxPQ(Key[] a){ pq = (Key[])new Comparable[a.length+1]

算法-优先队列与堆排序

我们自己每天使用的电脑能同时运行多个应用程序,没有感觉到卡顿,电脑为每个应用程序的事件分配了一个优先级,移动端的手机也是,通常不管我们是在看电影,发短信只要有电话,电话绝对是优先级最高的.这个时候我们需要一种合理的数据结构删除最大元素和插入元素,我们可以称之为优先队列.实现这种优先队列最合适的数据结构可以通过经典的二叉堆来实现,相对于其他数据结构更高效. 优先队列 开始撸代码之前我们需要明白几个概念: 1.当一棵二叉树的每个节点都大于等于等于它的两个子节点时,它称之为堆有序. 2.二叉堆是一组能

【算法设计-优先队列】优先队列的实现与操作

优先队列是堆排序的一个具体应用. 优先队列分为如下几个操作: 1.INSERT(S,x)把元素x插入到优先队列中. 2.MAXIMUM(S):返回s中具有最大关键字的元素. 3.EXTRACT_MAX(S):去掉S中最大关键字的元素 4.INCREASE_KEY(S,x,k):将元素x的关键字值增加到k,k是不小于x的元素. 优先队列的应用: 1.共享计算机系统的作业调度.最大优先队列要i记录要执行的各个作业以及他们之间的相对优先级.当一个作业完成或者被中断后,调度器调用EXTRACT_MAX从

最大优先队列

最大优先队列 前言 堆排序是一种集插入排序和选择排序的有点于一身的排序算法,但是在后面能学习到更加好的快速排序算法,性能优于堆排序.堆这钟数据结构还有许多其他的用处,例如作为高效的优先队列.优先队列分为最大优先队列和最小优先队列,今天学习了如何用最大堆来实现最大优先队列. 优先队列是一种用来维护由一组元素构成的集合S的数据结构,其中的每一个元素都有一个相关的值,称为关键字.一个最大优先队列应该支持下面的四种操作: MAXIMUM(S):将集合S中的最大关键字的元素返回. EXTRACT_MAX(

《C算法.第1卷,基础、数据结构、排序和搜索(第三版)》pdf

下载地址:网盘下载 内容简介  · · · · · · <C算法>介绍了当今最重要的算法,共分3卷,<C算法(第1卷):基础.数据结构.排序和摸索>是第1卷.第1卷分4部分.共16章.第一部分"基础知识"(第1-2章)介绍了基本算法分析原理.第二部分"数据结构"(第3-5章)讲解算法分析中必须掌握的数据结构知识.主要包括基本数据结构.抽象数据结构.递归和树.第三部分"排序"(第6-11章)按章节顺序分别讨论了基本排序方法(

【特征匹配】SIFT原理之KD树+BBF算法解析

继上一篇中已经介绍了SIFT原理点击打开链接,最后得到了一系列特征点,每个特征点对应一个128维向量.假如现在有两副图片都已经提取到特征点,现在要做的就是匹配上相似的特征点. 相似性查询有两种基本方式:1.范围查询:即给点查询点和查询阈值,从数据集中找出所有与查询点距离小于阈值的点. 2.K近邻查询:给点查询点及正整数K,从数据集中找到与查询点最近的K个数据,当K=1时,就是最近邻查询. 特征匹配算子可以分为两类:1.穷举法:即将数据集中的点与查询点逐一计算距离,如果图1提取到N1个特征点,图2

数据结构算法[c语言]

共16章,共四部分:基础知识,数据结构,排序和搜索. 所有的实现都是使用C语言缩写.任何语言都有优缺点,选用C语言是因为它使用的广泛. 第一章: 导论 第二章: 算法分析原理 第三章: 基本数据结构 第四章: 抽象数据 第五章: 递归和数 第六章: 基本排序方法 第七章: 快速排序 第八章: 归并和归并排序 第九章: 优先队列和堆排序 第十章: 基数排序 第十一章: 特殊目的的排序方法 第十二章: 符号表和二叉搜索树 第十三章: 平衡树 第十四章: 哈希方法 第十五章: 基数搜索 第十六章: 外

数据结构(三):非线性逻辑结构-特殊的二叉树结构:堆、哈夫曼树、二叉搜索树、平衡二叉搜索树、红黑树、线索二叉树

在上一篇数据结构的博文<数据结构(三):非线性逻辑结构-二叉树>中已经对二叉树的概念.遍历等基本的概念和操作进行了介绍.本篇博文主要介绍几个特殊的二叉树,堆.哈夫曼树.二叉搜索树.平衡二叉搜索树.红黑树.线索二叉树,它们在解决实际问题中有着非常重要的应用.本文主要从概念和一些基本操作上进行分类和总结. 一.概念总揽 (1) 堆 堆(heap order)是一种特殊的表,如果将它看做是一颗完全二叉树的层次序列,那么它具有如下的性质:每个节点的值都不大于其孩子的值,或每个节点的值都不小于其孩子的值