• 堆得简单介绍以及堆排序


    首先看一下堆的定义:

    对于n个元素的序列{k1,k2,k3,……,kn},当且仅当满足下列关系时,称之为堆:

    K(i) <= K(2*i) && K(i) <= K(2*i+1)      此时的堆为小顶堆

    K(i) >= K(2*i) && K(i) >= K(2*i+1)      此时的堆为大顶堆

    (i = 1,2,……,n/2(下取整))

    注意:堆得存储是用一维数组来存储的。

    若将堆对应的序列看成是一个完全二叉树,则堆得含义表明:

    完全二叉树中所有非终端结点的值均不大于(或不小于)其左右孩子结点的值。

    因此,若序列{K1,K2,……,Kn} 是大顶堆,则堆顶元素必为序列中n个元素的最大值;反之,若序列是小顶堆,则堆顶元素必为序列中n个元素的最小值。

    堆排序就是利用的这个性质。

    堆排序的过程如下:

    假设要从小到大排序,我们构建一个大顶堆,则堆顶元素是最大值。将堆顶元素和最后一个元素互换,则最后一个元素变成了n个元素中的最大值。之后再将剩下的 n-1 个元素调整成为大顶堆,将堆顶元素和第n-1 个元素互换,则第n-1 个元素变成了n个元素中的次大值……循环这个过程,不断调整堆,最后得到一个有序的序列。

    在上面堆排序的过程中,有两个问题需要解决:

    (1)如何将一个初始的序列构建成一个大顶堆?

    (2)再得到最大元素后,剩下的n-1个元素如何再次调整成为一个大顶堆?

    实际上,初始序列构建大顶堆也是一个不断调整堆得过程。因此,只要解决第二个问题就可以。

    下图是一个大顶堆:

    当把堆顶元素20和最后一个元素互换之后,最后一个元素变成了序列中的最大值。如下图:

    但是,此时堆顶元素违反了大顶堆的性质,堆顶元素的左右孩子仍旧满足大顶堆的性质。因此,此时需要对堆进行调整。因为左子树的值大于右子树的值,所以将3和17互换,如下图:

    此时,左子树又违反了大顶堆得性质,所以需要调整左子树,如下图:

    至此,一次调整完毕,堆顶元素成为了次大元素。

    实际上,调整堆就是这样一个不断筛选比较的过程,不断的和左右子树比较,一直到不需要交换为止。

    将一个无序序列构建成一个大顶堆的过程就是一个反复筛选的过程。将此序列看成是一个完全二叉树,则最后一个非叶子节点是第 n/2(下取整)个元素,因此,筛选只需从第 n/2(下取整)个元素开始。

    假设有序列:{49,38,65,97,76,13,27},初始二叉树是:

    从第3个元素,也就是65开始调整堆,65大于左右子树的值,因此不需要调整。

    然后是第2个元素,也就是从38开始调整堆,38和左右子树比较,将97和38互换,调整后如下图:

    然后是第1个元素,也就是从49开始调整堆,49和左右子树比较,将97和49互换,互换之后,因为49破坏了左子树大顶堆的性质,因此需要继续调整,将49和左右子树比较,然后将49和76互换,调整过程如下图:

    至此,将一个无序的序列调整成为了一个大顶堆。

    同理,堆排序也分为两个过程:(1)将初始化序列调整成为一个大顶堆(2)用最后一个元素和堆顶元素交换,然后不断调整剩下的元素成为一个新的大顶堆。

    代码如下:

    const int N = 8;
    int num[N] = {-1,49,38,65,97,76,13,27};  //从第一个元素开始存储
    
    //调整堆的函数
    void heapAdjust(int pos,int total){
    	int temp = num[pos];
    	for(int j = 2 * pos; j <= total; j *= 2){
    		if(j < total){  //说明还有右子树
    			if(num[j] < num[j + 1]){   //筛选出左右子树中较大的值
    				j += 1;
    			}
    		}
    		if(temp >= num[j])  //不需要再继续向下调整了
    			break;
    		num[pos] = num[j];
    		pos = j;
    	}
    	num[pos] = temp;
    }
    
    void heapSort(){
    	//首先将数组构建成一个大顶堆
    	for(int i = (N-1) / 2;i >=1;--i){
    		heapAdjust(i,N - 1);
    	}
    	//开始堆排序
    	for(int i = N-1; i > 1; --i){
    		int temp = num[i];   //交换第一个元素和最后一个元素
    		num[i] = num[1];
    		num[1] = temp;
    		heapAdjust(1,i - 1);  //交换完之后,重新调整堆
    	}
    }
    

      

  • 相关阅读:
    ios常见面试题
    UIButton 头文件常见属性和方法
    UILabel头文件常见属性
    UIButton 文档翻译(持续更新)
    UITextView
    iOS国际化
    55分钟学会正则表达式
    提示框的使用UIAlertController(UIAlertView和UIActionSheet二合一,包含通知中心的使用)
    macbook恢复Finder消失的个人收藏:桌面、文稿、下载、图片
    Socket
  • 原文地址:https://www.cnblogs.com/acBool/p/4842151.html
Copyright © 2020-2023  润新知