小根堆实现优先级队列

   优先级队列操作一个初始为空的元素集合。提供两种基本操作,将一个数插入优先级队列和在优先级队列中取出一个优先级最高的数。在这里数字为正整数且小的有高的优先级。

      实现优先级队列固然可以用一个有序或无序的数组来实现。对于有序的情况来说,取出一个数的时间复杂度为O(1)而插入一个数为O(n);对于无序的情况来说,插入一个数为O(1),而取出一个数为O(n)。当进行n步对优先级队列操作时时间复杂度为O(n2)。这样的效率对于百万级的数据量来说是不可想像的。

      为了折衷可以找一种介于有序与无序之间的数据结构------小根堆。小根堆在整体内不保证有序,但保证与孩子节点之间是有序的,也就是都要小于孩子节点。而对堆的插入与删除操作的时间复杂度为O(lgn)所以对于大数据量来说实现优先级队列来说是可行的。

       在以下的实现中,有两个函数,insert_elem与extract_max。里面核心的是对堆的两种调整方法liftup与liftdown,分别将一个新插入的元素调整到正确的位置;从根将一个数据调整到正确的位置。

  以下给了一个C实现 ,主要有test.cpp、priorityqueue.h、priorityqueue.cpp

priorityqueue.h

#pragma once
#ifndef PRIORITYQUEUE_H
#define PRIORITYQUEUE_H

#include <stdio.h>
#define MAXSIZE 100
#define EMPTYELEM -1

typedef struct priorityqueue{
    int queuesize;
    //这里多出一个元素,为了计算方便一切从数组1位置开始
    int pq[MAXSIZE + 1];
}sctpq,* psctpq;

/*************************************************
Function:  initqp(psctpqf);
Description: 对优先级队列进行初始化
Input: Arg1: pqp 优先级队列结构的指针
Return: void
*************************************************/
void init_pq(psctpq pqp);


/*************************************************
Function:  insert_elem
Description: 向优先级队列中插入数据
Input: Arg1: 要插入的数据
Return: void
*************************************************/
void insert_elem(psctpq pqp, int elem);


/*************************************************
Function:  extract_max
Description: 返回队列中优先级最高的数据
Input: void
Return: 返回的最高优先级最据
*************************************************/
int extract_max(psctpq pqp);


int get_pq_size(psctpq pqp);


/*************************************************
Function:  sift_up
Description: 将新插入的最尾端的节点向上调整
Input: 优先级队列
Return: void
*************************************************/
void sift_up(psctpq pqp);

/*************************************************
Function:  sift_down
Description: 将新第一个元素从队列中移除后,对小根堆进行调整成为新堆
Input: 优先级队列
Return: void
*************************************************/
void sift_down(psctpq pqp);


/*************************************************
Function:  find_left_child
Description: 找到给定堆中节点的左孩子
Input: Arg1: 当前的节点位置
Return: 左孩子的位置,如果没有了就返回-1
*************************************************/
int find_left_child(int pos, psctpq pqp);



/*************************************************
Function:  find_right_child
Description: 找到给定堆中节点的右孩子
Input: Arg1: 当前的节点位置
Return: 右孩子的位置,如果没有了就返回-1
*************************************************/
int find_right_child(int pos, psctpq pqp);



/*************************************************
Function:  find_parent
Description: 找到给定堆中节点的亲人节点
Input: Arg1: 当前的节点位置
Return: 当前节点的亲人的位置,如果是根节点就返回-1
*************************************************/
int find_parent(int pos, psctpq pqp);

void swap(int destpos, int originpos, int* a);

void show(psctpq pqp);

void min_heapify(psctpq pqp, int pos);

#endif
  1 priorityqueue.cpp
  2 
  3 
  4 #include "priorityqueue.h"
  5 
  6 void init_pq(psctpq pqp)
  7 {
  8     int i;
  9     pqp->queuesize = 0;
 10     for (i = 0; i != MAXSIZE; i++)
 11         pqp->pq[i] = EMPTYELEM;
 12 }
 13 
 14 void insert_elem(psctpq pqp, int elem)
 15 {
 16     if (pqp->queuesize > MAXSIZE)
 17     {
 18         printf("max size overflow!\n");
 19         return;
 20     }
 21     pqp->queuesize ++;
 22     pqp->pq[pqp->queuesize] = elem;
 23     sift_up(pqp);
 24 }
 25 
 26 int extract_max(psctpq pqp)
 27 {
 28     if (pqp->queuesize == 0)
 29     {
 30         printf("empty priority queue\n");
 31         return -1;
 32     }
 33     int rtval = pqp->pq[1];
 34     pqp->pq[1] = pqp->pq[pqp->queuesize];
 35     pqp->pq[pqp->queuesize] = 0;
 36     pqp->queuesize --;
 37     sift_down(pqp);
 38     return rtval;
 39 }
 40 
 41 void sift_up(psctpq pqp)
 42 {
 43     int parentpos;
 44     int currentpos;
 45     //只有一个元素不用调整
 46     if (pqp->queuesize == 1)
 47         return;
 48     else
 49     {
 50         parentpos = find_parent(pqp->queuesize, pqp);
 51         currentpos = pqp->queuesize;
 52     //数小的,优先级高,与亲节点的位置交换
 53         while (pqp->pq[parentpos] > pqp->pq[currentpos] && parentpos != -1)
 54         {
 55             swap(parentpos, currentpos, pqp->pq);
 56             currentpos = parentpos;
 57             parentpos = find_parent(currentpos, pqp);
 58         }
 59     }
 60 }
 61 
 62 void sift_down(psctpq pqp)
 63 {
 64     min_heapify(pqp, 1);
 65 }
 66 
 67 void min_heapify(psctpq pqp, int pos)
 68 {
 69     int* a = pqp->pq; 
 70     int lpos, rpos;
 71     lpos = find_left_child(pos, pqp);
 72     rpos = find_right_child(pos, pqp);
 73     //没有孩子节点到叶子节点时返回
 74     if(lpos == -1)
 75         return;
 76     //当没有右孩子时
 77     if(rpos == -1)
 78     {
 79         if(a[pos] <= a[lpos])
 80             return;
 81         else
 82         {
 83             swap(pos, lpos, a);
 84             min_heapify(pqp, lpos);
 85         }
 86     }
 87     //左右孩子都有
 88     else
 89     {
 90         //根最小
 91         if (a[pos] <= a[lpos] && a[pos] <= a[rpos])
 92             return;
 93         //左最小
 94         else if (a[lpos] <= a[rpos])
 95         {
 96             swap(lpos, pos, a);
 97             min_heapify(pqp, lpos);
 98         }
 99         //右最小
100         else
101         {
102             swap(rpos, pos, a);
103             min_heapify(pqp, rpos);
104         }
105     }
106 }
107 
108 int find_left_child(int pos, psctpq pqp)
109 {
110     int leftchildpos;
111     if(pos <= 0 || pos > pqp->queuesize)
112         return -1;
113     leftchildpos = pos << 1;
114     if (leftchildpos > pqp->queuesize)
115         return -1;
116     else return leftchildpos;
117 }
118 
119 int find_right_child(int pos, psctpq pqp)
120 {
121     int rightchildpos;
122     if(pos <= 0 || pos > pqp->queuesize)
123         return -1;
124     rightchildpos = (pos << 1) + 1;
125     if (rightchildpos > pqp->queuesize)
126         return -1;
127     else return rightchildpos;
128 }
129 
130 int find_parent(int pos, psctpq pqp)
131 {
132     int parentpos;
133     if(pos <= 0 || pos > pqp->queuesize)
134         return -1;
135     parentpos = pos >> 1;
136         if (parentpos < 1)
137             return -1;
138         else return parentpos;
139 }
140 
141 void swap(int destpos, int originpos, int* a)
142 {
143     int buffer = a[destpos];
144     a[destpos] = a[originpos];
145     a[originpos] = buffer;
146 }
147 
148 void show(psctpq pqp)
149 {
150     int i = 0;
151     int currentsize = pqp->queuesize;
152     if (currentsize == 0)
153     {
154         printf("empty queue\n");
155         return;
156     }
157     printf("-------------current queue is-----------------\n");
158     for (i; i != currentsize; i++)
159         printf("%d ", pqp->pq[i + 1]);
160     printf("\n");
161     printf("----------------------------------------------\n");
162 }
test.cpp

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

void main()
{
    int i;
    int choice = 5;
    sctpq priorityqueue;
    init_pq(&priorityqueue);
    printf("priority queue init complete\n");
    printf("input a selection :\n");
    printf("1 to show the content of this priority queue\n");
    printf("2 to input a number into the priority queue\n");
    printf("3 to get a number from the priority queue\n");
    printf("0 exit\n");
    while (choice != 0)
    {
        printf("****tap in your choice****\n");
        scanf("%d", &choice);
        switch (choice)
        {
        case -1:
            return;
        case 1:
            show(&priorityqueue);
            break;
        case 2:
            printf("****tap in your number****\n");
            scanf("%d", &i);
            insert_elem(&priorityqueue, i);
            break;
        case 3:
            i = extract_max(&priorityqueue);
            if (i != -1)
                printf("the largest priority number is %d\n", i);
            break;
        }
    }
}
posted @ 2012-09-23 16:37  answer0107  阅读(358)  评论(0)    收藏  举报