zoukankan      html  css  js  c++  java
  • 分治算法四:基于二叉堆的优先队列

    一、背景介绍

    1、优先队列:队列中的元素被指定优先级,元素按优先级出队。针对二叉堆,根节点就为最值。队列有两个基本操作:入队和出队。

    2、入队操作分析:

    将待入队的元素放在堆的末尾,判断该元素与与其父节点的优先级,确定是否需要进行交换;若需要交换,则在交换后继续进行判断。

    3、出队操作分析:

    将队首元素取出,将队尾元素覆盖当前队首元素(队列长度减一),并维护剩余元素组成堆的性质。

    4、数据类型:数据集合+各种操作。

    二、代码实现

    二叉堆的性质维护heapify函数分析,请参考分治算法四:二叉堆的创建

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <assert.h>
    
    /************************************堆性质维护********************************************/
    // 比较函数
    int intGreater(void *x, void *y)
    {
        return *(int *)x - *(int *)y;
    }
    int intLess(void *x, void *y)
    {
        return *(int *)y - *(int *)x;
    }
    
    // 交换两个变量的值
    void swap(void *x, void *y, int size)
    {
        void *temp = (void*)malloc(size);
        memcpy(temp, x, size);
        memcpy(x, y, size);
        memcpy(y, temp,size);
        free(temp);
    }
    
    // 根据子节点i,获取父节点索引
    int parent(int i)
    {
        return (i - 1) / 2;
    }
    
    // 在左右子树满足堆特性的前提下(最简单情形为左右子树为单个元素),判断父节点加入后,是否满足堆特性并进行调整
    void heapify(void *a, int size, int parent, int heapSize, int(*comp)(void *, void *))
    {
        // 根据父节点索引i,得到左叶子节点和右叶子节点的索引,根节点的索引从0开始
        int left = 2 * parent + 1;
        int right = 2 * parent + 2;
        int most;
        
        // 比较左叶子节点和父节点大小,most = max(left, parent)
        if (left < heapSize && comp(a + left * size, a + parent * size) > 0) {
            most = left;
        } else {
            most = parent;
        }
        // 比较右叶子节点和most节点, most = max(right, most)
        if (right < heapSize && comp(a + right * size, a + most * size) > 0) {
            most = right;
        }
    
        // 此时most =  max(parent, left, right); 若不满足堆特性,将most与父节点进行交换,并对交换后的叶子节点继续判断
        if (most != parent) {
            swap(a + parent * size, a + most * size, size);
            heapify(a, size, most, heapSize, comp);
        }
    }
    
    
    /*******************************************优先队列***********************************************/
    
    // 数据结构:优先队列
    typedef struct {
        int length;    // 队列的最大长度
        int heapSize;  // 队列当前元素个数
        void *heap;    // 队列头指针(堆的根节点指针),存储空间
    } priorityQueue;
    
    // 创建并初始化队列:入参size为队列元素的大小,用于适配不用类型的元素,n为队列元素个数
    priorityQueue creatQueue(int size, int n)
    {
        priorityQueue q;
        q.length = n;
        q.heapSize = 0;     // 队列初始化时,当前元素个数为0
        q.heap = (void*)malloc(n * size);     // 为当前队列申请内存
        return q;
    }
    
    // 入队算法:e为入队元素指针,size为队列元素大小
    void enterQueue(priorityQueue *q, int size, void *e, int(*compare)(void *, void *))
    {
        // 判断队列是否满
        if (q->heapSize == q->length) {
            return;
        }
        // 入队后,队列元素数量加一
        int i = q->heapSize ++;
        // 将待入队元素,复制到队列尾部
        memcpy(q->heap + i * size, e, size);
        // 调整:判断新增的尾部节点与其父节点的优先级,若尾部优先级高,则与父节点进行交换;并继续进行判断
        while (i > 0 && compare(q->heap + parent(i) * size, q->heap + i * size) < 0) {
            swap(q->heap + i * size, q->heap + parent(i) * size, size);
            i = parent(i);
        }
    }
    
    // 出队算法:返回队列中最优先元素
    void *outQueue(priorityQueue *q, int size, int(*compare)(void *, void *))
    {
        // 判断队列非空
        assert(q->heapSize >= 1);
        // 保存待出队元素
        void *top = (void*)malloc(size);
        memcpy(top, q->heap, size);
        // 出队后,队列元素数量减一
        q->heapSize --;
        // 将当前队尾元素覆盖队首元素(即待出队元素,且待出队元素已经保存)
        memcpy(q->heap, q->heap + (q->heapSize) * size, size);
        // 队首元素变更后,进行堆性质调整
        heapify(q->heap, size, 0, q->heapSize, compare);
        return top;
    }
    
    // 判断队列是否为空
    int enpty(priorityQueue q)
    {
        return q.heapSize < 1;
    }
    
    // 清理队列环境
    void clearQueue(priorityQueue *q)
    {
        free(q->heap);
        q->heap = NULL;
        q->heapSize = 0;
        q->length = 0;
    }
    
    /*******************************************测试代码***********************************************/
    int main(void)
    {
        int arr[] = {5, 1, 9, 4, 6, 2, 0, 3, 8, 7};
        int arrSize = sizeof(arr) / sizeof(arr[0]);
    
        priorityQueue q = creatQueue(sizeof(int), 10);
        for (int i = 0; i < 10; i++) {
            enterQueue(&q, sizeof(int), arr + i, intLess);
        }
    
        while(!enpty(q)) {
            printf("%d ", *(int*)outQueue(&q, sizeof(int), intLess));
        }
        printf("
    ");
    
        clearQueue(&q);
    
        while (1);
        return 0;
    }
    
    

    三、测试结果

  • 相关阅读:
    vue 使用 <iframe> 嵌入网页 地址实现动态配置
    vue 视频播放 vue-video-player
    vue 实现自定义序号, 并且翻页序号累加。
    关于 vue 使用 Handsontable 表格数据导出
    node.js Stream流的使用
    手把手教如何搭建node+egg项目 引入Sequelize
    实现 通过数据库里一个字段值相等 则把 他合为一条数据
    最近在项目中碰到把对象数组转为键值对,
    js 的数组怎么push一个对象. Js数组的操作push,pop,shift,unshift JavaScrip
    for循环
  • 原文地址:https://www.cnblogs.com/HZL2017/p/14444605.html
Copyright © 2011-2022 走看看