序列——堆排序-大根堆(堆大顶)

1.小根堆

如果根是儿童的存在留下的根值左孩子小于值;如果根是儿童的权利的存在的根值比他们的孩子的权利少值。

2.大根堆

如果根是儿童的存在留下的根值多名离开自己的孩子值。子女则根节点的值大于右子女的值。

3.结论

(1)堆是一棵全然二叉树(假设公有h层,那么1~h-1层均满,在h层连续缺失若干个右叶子)。

(2)小根堆的根节点的值是最小值,大根堆的根节点的值是最大值。

(3)堆适合于採用顺序存储。

4.堆的插入算法

将一个数据元素插入到堆中,使之依旧成为一个堆。

算法描写叙述:先将结点插入到堆的尾部,再将该结点逐层向上调整,直到依旧构成一个堆。调整方法是看每一个子树是否符合大(小)根堆的特点,不符合的话则调整叶子和根的位置。

5.堆的删除算法

堆在删除元素时,仅仅能够删除根节点。

算法描写叙述:将根节点删除后用堆尾结点进行填补,调整二叉树,使之依旧成为一个堆。

6.堆排序(大根堆,小根堆类似)

其基本思想为(大根堆):

1)将初始待排序keyword序列(R1,R2....Rn)构建成大顶堆,此堆为初始的无序区,构建的过程是每一个非叶子结点都经过一次调整,调整顺序为从底层至顶层(调整过程中含有递归),这样调整下来这个二叉树总体上就是一个大根堆(或小根堆)了;

2)将堆顶元素R[1]与最后一个元素R[n]交换。此时得到新的无序区(R1,R2,......Rn-1)和新的有序区(Rn),且满足R[1,2...n-1]<=R[n];

3)因为交换后新的堆顶R[1]可能违反堆的性质,因此须要对当前无序区(R1,R2,......Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2....Rn-2)和新的有序区(Rn-1,Rn)。不断反复此过程直到有序区的元素个数为n-1,则整个排序过程完毕。

操作步骤例如以下:

1)初始化堆:将R[1..n]构造为堆;

2)将当前无序区的堆顶元素R[1]同该区间的最后一个记录交换,然后将新的无序区调整为新的堆。

因此对于堆排序,最重要的两个操作就是构造初始堆和调整堆,其实构造初始堆其实也是调整堆的过程,仅仅只是构造初始堆是对全部的非叶节点都进行调整。

操作过程图示:

从上述过程可知。堆排序其实也是一种选择排序,是一种树形选择排序。

仅仅只是直接选择排序中。为了从R[1...n]中选择最大记录,需比較n-1次,然后从R[1...n-2]中选择最大记录需比較n-2次。其实这n-2次比較中有非常多已经在前面的n-1次比較中已经做过。而树形选择排序恰好利用树形的特点保存了部分前面的比較结果。因此能够降低比較次数。对于n个keyword序列。最坏情况下每一个节点需比較log2(n)次。因此其最坏情况下时间复杂度为nlog2(n)。堆排序为不稳定排序,不适合记录较少的排序。

关于log2(n)的理解:依据堆排序的过程,每次将大根堆根节点的值跟最后一个叶子的值进行交换,那假设最后的叶子结点正好是最小的数。那么这个叶子结点就会一层层的被放到子树终于放到叶子结点的位子(不是前面的叶子结点的位置了)。这种话这个叶子结点经过的层数就刚好为log2(n)。然而其它没有交换的二叉树的分支,由于曾经都是大根堆。所以大根堆的性质还是没有变化,这一点对理解程序至关重要。C语言程序例如以下:

/*堆排序(大根堆)*/
#include <stdio.h>

/*注意:这个函数仅仅会在调整被交换的位置为大根堆。未交换的分支不会处理,
所以不能将一个非大根堆二叉树的根结点传递过来让这个函数将其处理为大根堆*/
void heap_ajust(int *a, int i, int size)  /*a为堆存储数组,size为堆的大小*/
{
    int lchild = 2*i;       //i的左孩子节点序号
    int rchild = 2*i +1;     //i的右孩子节点序号
    int max = i; /*存放三个顶点中最大的数的下标*/
	int temp;
    if(i <= size/2)          //假设i是叶节点就不用进行调整
    {
        if(lchild<=size && a[lchild]>a[max])
        {
            max = lchild;
        }
        if(rchild<=size && a[rchild]>a[max])
        {
            max = rchild;
        }
        if(max != i)
        {
            temp = a[i]; /*交换a[i]和a[max]的值*/
			a[i] = a[max];
			a[max] = temp;
            heap_ajust(a, max, size); /*被交换的位置曾经是大根堆,如今可能不是大根堆
			                            所以须要又一次调整使其成为大根堆结构*/
        }
    }
}

void build_bheap(int *a, int size) /*建立大根堆*/
{
    int i;
    for(i=size/2; i >= 1; i--) /*非叶节点最大序号值为size/2*/
    {
        heap_ajust(a, i, size); /*每一个非叶子结点都须要调用这个函数*/
    }
} 

void heap_sort(int *a, int size) /*堆排序*/
{
    int i;
	int temp;

    build_bheap(a, size);
    for(i=size; i >= 1; i--)
    {
        temp = a[1];
		a[1] = a[i];
		a[i] = temp; /*交换堆顶和最后一个元素,即每次将剩余元素中的最大者放到最后面*/
        heap_ajust(a, 1, i-1); /*又一次调整堆顶节点成为大顶堆,仅仅有被交换的分支才有可能不是大根堆*/
    }
} 

int main(int argc, char *argv[])
{
    int a[]={0,16,20,3,11,17,8};
    int size = sizeof(a)/sizeof(int) -1;
	int i;

	printf("size = %d\n", size);
    heap_sort(a, size);
	printf("Sort over:");
    for(i=1;i <= size; i++)
        printf("%d ", a[i]);
    printf("\n");

    return 0;
}

程序执行截图为:

參考博文地址:http://www.cnblogs.com/dolphin0520/archive/2011/10/06/2199741.html

版权声明:本文博客原创文章,博客,未经同意,不得转载。

时间: 2024-08-03 16:47:53

序列——堆排序-大根堆(堆大顶)的相关文章

小顶堆,大顶堆

什么是字节序 在不同的计算机体系结构中,对于数据(比特.字节.字)等的存储和传输机制有所不同,因而引发了计算机领域中一个潜在但是又很重要的问题,即通信双方交流的信息单元应该以什么样的顺序进行传送.如果达不成一致的规则,计算机的通信与存储将会无法进行.目前在各种体系的计算机中通常采用的字节存储机制主要有两种:大端(Big-endian)和小端(Little-endian).这里所说的大端和小端即是字节序. MSB和LSB MSB是Most Significant Bit/Byte的首字母缩写,通常

剑指offer:数据流中的中位数(小顶堆+大顶堆)

1. 题目描述 /** 如何得到一个数据流中的中位数? 如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值. 如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值. 我们使用 Insert()方法读取数据流,使用 GetMedian()方法获取当前读取数据的中位数. */ 2. 思路 /** 最大堆和最小堆 * 每次插入小顶堆的是当前大顶堆中最大的数 * 每次插入大顶堆的是当前小顶堆中最小的数 * 这样保证小顶堆中的数永远大于等于大顶堆中的数(值

堆排序——大根堆(大顶堆)

1.小根堆 若根节点存在左子女则根节点的值小于左子女的值:若根节点存在右子女则根节点的值小于右子女的值. 2.大根堆 若根节点存在左子女则根节点的值大于左子女的值:若根节点存在右子女则根节点的值大于右子女的值. 3.结论 (1)堆是一棵完全二叉树(如果公有h层,那么1~h-1层均满,在h层连续缺失若干个右叶子). (2)小根堆的根节点的值是最小值,大根堆的根节点的值是最大值. (3)堆适合于采用顺序存储. 4.堆的插入算法 将一个数据元素插入到堆中,使之依然成为一个堆. 算法描述:先将结点插入到

大顶堆第二弹----堆排序(递归实现)

1 package tooffer; 2 3 import java.util.ArrayList; 4 import java.util.Arrays; 5 6 public class BigHeap { 7 8 9 10 /* 11 *交换堆中的两个元素 12 */ 13 private void swap(ArrayList<Integer> heapList,int srcIndex,int dstIndex) 14 { 15 int tmp = heapList.get(srcIn

堆排序—大根堆,小根堆

1.小根堆 若根节点存在左子女则根节点的值小于左子女的值:若根节点存在右子女则根节点的值小于右子女的值. 2.大根堆 若根节点存在左子女则根节点的值大于左子女的值:若根节点存在右子女则根节点的值大于右子女的值. 3.结论 (1)堆是一棵完全二叉树(如果公有h层,那么1~h-1层均满,在h层连续缺失若干个右叶子). (2)小根堆的根节点的值是最小值,大根堆的根节点的值是最大值. (3)堆适合于采用顺序存储. 4.堆的插入算法 将一个数据元素插入到堆中,使之依然成为一个堆. 算法描述:先将结点插入到

python 大顶堆 小顶堆

http://www.coder4.com/archives/3844 需1求:给出N长的序列,求出TopK大的元素,使用小顶堆,heapq模块实现. import heapq import random class TopkHeap(object): def __init__(self, k): self.k = k self.data = [] def Push(self, elem): if len(self.data) < self.k: heapq.heappush(self.data

wikioi 2573 大顶堆与小顶堆并用

题目描述 Description 我们使用黑匣子的一个简单模型.它能存放一个整数序列和一个特别的变量i.在初始时刻,黑匣子为空且i等于0.这个黑匣子能执行一系列的命令.有两类命令: ADD(x):把元素x放入黑匣子:GET:把i加1的同时,输出黑匣子内所有整数中第i小的数.牢记第i小的数是当黑匣子中的元素已非降序排序后位于第i位的元素. 下面的表6_4是一个11个命令的例子: 表6_4 编号 命令 i 黑匣子内容 输出 1 ADD(3) 0 3 2 GET 1 3 3 3 ADD(1) 1 1,

wikioi 1052 大顶堆

题目描述 Description 王钢是一名学习成绩优异的学生,在平时的学习中,他总能利用一切时间认真高效地学习,他不但学习刻苦,而且善于经常总结.完善自己的学习方法,所以他总能在每次考试中得到优异的分数,这一切很大程度上是由于他是一个追求效率的人. 但王钢也是一个喜欢玩的人,平时在学校学习他努力克制自己玩,可在星期天他却会抽一定的时间让自己玩一下,他的爸爸妈妈也比较信任他的学习能力和学习习惯,所以在星期天也不会象其他家长一样对他抓紧,而是允许他在星期天上午可以自由支配时间. 地鼠游戏是一项需要

寻找最小的k个数(大顶堆方法)

题目描述:查找最小的k个元素,输入n个整数,输出其中最小的k个. 一般的排序方法,如快排,时间复杂度为O(n*logn+k); 大顶堆方法,时间复杂度为O(k+(n-k)*logk); 如果建立k个元素的最小堆的话,那么其空间复杂度势为O(N),而建立k个元素的最大堆的空间复杂度为O(k); 当面对海量数据处理的时候,大顶堆的方法是较为靠谱的,并且可以在面试时短时间内完成代码. 1 class Solution { 2 public: 3 void Swap(int &a,int &b)