最大堆(最小堆)

  最大堆是一种很有用的数据结构,它是一颗完全二叉树,并且如果一个节点有儿子节点,其关键字都不小于其儿子节点的关键字。(最小树反之:节点值不大于儿子节点的完全二叉树。)

  最大堆使用的一个典型的地方就是找出无序数字中,最大的一个数字。比如100亿整数中找出最小的前100个数字,典型的解决方案之一就是:先去处前边一百个值,创建一个最大堆,然后顺序读入的剩下的每个值,如果值小于根节点值,则删除根节点,把这个值插入,重建最大堆。重复这过程。最后就得到了最小的前100个数字(如果找前100个最大的值,就建立一个最小堆)。此算法时间复杂性为nlog100。log100为重建最大堆(size=100)的复杂度,n为数字的数量。

  由于最大堆是完全二叉树,所以很适合用数组存贮,而且根据完全二叉树的一些定义可以很快得到当前节点的父节点或者左右儿子节点。

对于n个结点的完全二叉树,顺序存贮,下标为i的节点:

  1.如果i != 1, 则其父节点为 1/2,1 为根节点

  2.若 2i <= n,则左儿子为 2i;2i > n,没有左儿子

  3.若2i+1 <= n, 则右儿子为 2i+1; 2i + 1 > n, 没有右儿子。

下面是最大堆一些常用的操作函数(最小堆类似,不再给出):

  1 #define MAX_ELEMENTS 200 /* maximun heap size+1 */
  2 #define HEAP_FULL(n) (n == MAX_ELEMENTS-1)
  3 #define HEAP_EMPTY(n) (!n)
  4 typedef struct {
  5   int key;
  6   /* other fields */
  7 } element;
  8 element heap[MAX_ELEMENTS];
  9 int n = 0;
 10
 11 //插入函数
 12 void insert_max_heap(element item, int *n)
 13 {
 14   /*insert item into a max heap of current size *n */
 15   int i;
 16   if (HEAP_FULL(*n)) {
 17     fprintf(stderr, "The heap is full. \n");
 18     exit(1);
 19   }
 20   i = ++(*n); //把值放入数组最后
 21   while ((i != 1) && (item.key > heap[i/2].key)) {
 22 //如果没到根节点,切当前节点值大于其父节点值,就把父节点下移
 23     heap[i] = heap[i/2];
 24     i /= 2; //找到父节点索引继续循环
 25   }
 26   heap[i] = item; //循环结束时候的位置就是真正要插入的位置
 27 }
 28 //这个插入函数可以用下面的move_up函数重写,要插入值放在数组最后,然后不停move_up即可
 29
 30 //删除最大值
 31 element delete_max_heap(int *n)
 32 {
 33   /* delete element with the highest key from the
 34      heap
 35   */
 36   int parent, child;
 37   element item, temp;
 38   if (HEAP_EMPTY(*n)) {
 39     fprintf(stderr, "The heap is empty.\n");
 40     exit(1);
 41   }
 42   /* save value of the element with the hightest key */
 43   item = heap[1];
 44   /* use last element in heap to adjust heap */
 45   temp = heap[(*n)--]; //删除最后一个值临时保存起来
 46   parent = 1;
 47   child = 2;
 48   while (child <= *n) { //子节点索引小于等于最大长度才继续
 49     /* find the larger child of the current parent */
 50     if ((child < *n) && (heap[child].key < heap[child+1].key)) {
 51 //找出两个儿子中较大的值
 52         child++;
 53     }
 54     if (temp.key >= heap[child].key) break; //移动的值大于当前值,结束循环
 55     heap[parent] = heap[child];//根节点不再了,把儿子中较大的移动上去
 56     parent = child;
 57     child *= 2;
 58   }
 59   heap[parent] = temp;
 60   return item;
 61 }
 62 //这个函数也是可以用下面的move函数改写,删除第一个值(最大值),然后吧最后一个值放入最大值的位置,然后不停move_down即可
 63
 64 //改变任意值的优先级
 65 //如果变大,就move_up,变小,就move_down
 66 void change_element_value(element item, int index, int *n)
 67 {
 68   if ((index <= 0) || (index > *n)) {
 69     fprintf(stderr, "No this element.\n");
 70     exit(1);
 71   }
 72   if (item.key = heap[index].key) {
 73     return;
 74   }
 75   if (item.key > heap[index].key) {
 76     heap[index] = item;
 77     move_up(index);
 78   }else {
 79     move_down(index, n);
 80   }
 81 }
 82
 83 void swap_element(int m, int n)
 84 {
 85   element temp;
 86   temp = heap[m];
 87   heap[m] = heap[n];
 88   heap[n] = temp;
 89 }
 90 void move_up(int i)
 91 {
 92   /* need check i‘s range, I don‘t do it at this. */
 93   while (i != 1) {
 94     if (heap[i] > heap[i/2]) {
 95       swap(i, i/2);
 96       i /= 2;
 97     }else {
 98       break;
 99     }
100   }
101 }
102 void move_down(int i, int *n)
103 {
104   int next;
105   while (2*i <= *n) {
106     next = 2*i;
107     if (next < *n && heap[next].key < heap[next+1].key) {
108       next += 1;
109     }
110     if (heap[i] < heap[next]) {
111       swap(i, next);
112       i = next;
113     }else {
114       break;
115     }
116   }
117 }
118
119 //创建最大堆
120 //这个函数原理简单,是遍历给定的那组数字,然后一个一个的insert,
121 //最后就得到了最大堆
122 void make_heap1(int a[], int length, int *n)
123 {
124   /* insert one by one */
125   int i = 0;
126   for ( ; i < length; ++i) {
127     element temp;
128     temp.key = a[i];
129     insert_max_heap(temp, *n);
130   }
131 }
132
133 //这个函数稍微复杂点:实在给定数组之上,直接调整位置,来构成最大堆(最大堆数组存储)
134 //原理:假设数组足够大,不会越界。把第0位的值存入最后。因为最大堆不用第0位。
135 //然后从数组最后开始遍历,先找到最后一个值的父节点,然后move_down构造最小堆
136 //从后往前,依次重复这个过程,知道第一个值,最大堆完成(有点递归的意思)
137 void make_heap2(int a[], int length)
138 {
139   a[length] = a[0];
140   int i;
141   for (i = length; i >= 1; --i) {
142     if (i/2 >= 1) {
143       move_down(i/2, length);
144     }
145   }
146 }

最大堆(最小堆)

时间: 2024-12-09 04:12:02

最大堆(最小堆)的相关文章

Black Box--[优先队列 、最大堆最小堆的应用]

Description Our Black Box represents a primitive database. It can save an integer array and has a special i variable. At the initial moment Black Box is empty and i equals 0. This Black Box processes a sequence of commands (transactions). There are t

最大堆/最小堆/优先队列 实现代码(c++)

自我感觉代码写的比较乱,这方面要好好注意一下. 总结: 1.在使用vector<int>::size_type 类似的类型时,千万要注意循环的条件判断,很容易发生溢出的危险!所以我最后很懒的选择使用int  - -. 2.下标表示和元素个数表示之间的细微差别.下标之间的变换关系: 父节点 parent(i)=(i-1)/2; 左孩子 left(i)=2*i+1;右孩子 right(i)=2*i+2 class Max_Heap{ typedef int index; public: Max_H

最小堆_最大堆

在大数查找中会遇到一类问题,例如在100亿条数据中找出 最大的(最小的) 前1000个元素.以int型4Byte为例,有1*1010*4 B = 4*1010/(230) B = 37.25G. 直接读取到内存中显然不合适,那么就需要: 首先,读取前1000个元素,建立一个最小堆(最大堆): 其次,之后每读取一个元素就与最小堆根元素(1000个数中最小值)进行比较: 如果,新元素大于(小于)堆顶元素 则,删除堆顶元素,将新元素插入堆顶.然后调整堆序,删除堆顶....循环往复 #define le

Google 面试题:Java实现用最大堆和最小堆查找中位数 Find median with min heap and max heap in Java

Google面试题 股市上一个股票的价格从开市开始是不停的变化的,需要开发一个系统,给定一个股票,它能实时显示从开市到当前时间的这个股票的价格的中位数(中值). SOLUTION 1: 1.维持两个heap,一个是最小堆,一个是最大堆. 2.一直使maxHeap的size大于minHeap. 3. 当两边size相同时,比较新插入的value,如果它大于minHeap的最大值,把它插入到minHeap.并且把minHeap的最小值移动到maxHeap. ...具体看代码 1 /*********

C++ priority_queue 最大堆、最小堆

问题描述 通常在刷题的时候,会遇到最大堆.最小堆的问题,这个时候如果自己去实现一个也是OK的,但是通常时间不太够,那么如何处理?这时,就可以借助C++ STL的priority_queue. 具体分析 需要注意的是,C++ STL默认的priority_queue是将优先级最大的放在队列最前面,也即是最大堆.那么如何实现最小堆呢? 假设有如下一个struct: struct Node { int value; int idx; Node (int v, int i): value(v), idx

最小堆和最大堆的JAVA实现

/** * 文件名:BinaryHeap.java * 时间:2014年11月3日下午7:15:34 * 作者:修维康 */ package chapter6; import java.util.*; /** * 类名:BinaryHeap 说明:建立一个最小堆 */ class MinHeap<AnyType extends Comparable<? super AnyType>> { private int currentSize; private static final i

C++ multiset通过greater、less指定排序方式,实现最大堆、最小堆功能

STL中的set和multiset基于红黑树实现,默认排序为从小到大. 定义三个multiset实例,进行测试: multiset<int, greater<int>> greadterSet; multiset<int, less<int>> lessSet; multiset<int> defaultSet; for (int i = 0; i < 10; i++) { int v = int(arc4random_uniform(10

最大堆,最小堆及堆排序

基本概念: 1.完全二叉树:若二叉树的深度为h,则除第h层外,其他层的结点全部达到最大值,且第h层的所有结点都集中在左子树. 2.满二叉树:满二叉树是一种特殊的的完全二叉树,所有层的结点都是最大值. 定义: 1.堆是一颗完全二叉树: 2.堆中的某个结点的值总是大于等于(最大堆)或小于等于(最小堆)其孩子结点的值. 3.堆中每个结点的子树都是堆树. 堆的数据结构如下: struct MaxHeap { EType *heap; //存放数据的空间,下标从1开始存储数据,下标为0的作为工作空间,存储

数据结构-最大堆、最小堆【手动实现】

0,堆的简介 数据结构中的堆是一种特殊的二叉树,不同于 Java 内存模型中的堆. 堆必须符合以下两个条件: 是一棵完全二叉树. 任意一个节点的值都大于(或小于)左右子节点的值. 从第一点可以知道,堆适合用数组来存储. 第二点中,若父节点都大于等于左右子节点,则被称为大顶堆,反之则为小顶堆. 图-最大堆及其存储方式 0.1节点的父.子节点关系 一个节点[根节点除外]的父节点地址为其地址的二分之一,它的左子节点地址为其地址值的2倍,右子节点地址为其地址2倍加1.  例如:现在有个节点的地址为3,其