• 堆排序实现


    1、堆排序算法描写叙述:

    (1)定义

    n个keyword序列Kl,K2,…,Kn称为(Heap)。当且仅当该序列满足例如以下性质(简称为堆性质):
    1)ki<=k(2i)且ki<=k(2i+1)(1≤i≤ n/2)。当然。这是小根堆。大根堆则换成>=号。//k(i)相当于二叉树的非叶子结点,K(2i)则是左子节点。k(2i+1)是右子节点
    2)若将此序列所存储的向量R[1..n]看做是一棵全然二叉树存储结构,则堆实质上是满足例如以下性质的全然二叉树:
    树中任一非叶子结点的keyword均不大于(或不小于)其左右孩子(若存在)结点的keyword。

    (2)用大根堆排序的基本思想

    ① 先将初始文件R[1..n]建成一个大根堆。此堆为初始的无序区
    ② 再将keyword最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n]。且满足R[1..n-1].keys≤R[n].key
    ③因为交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。

    然后再次将R[1..n-1]中keyword最大的记录R[1]和该区间的最后一个记录R[n-1]交换。由此得到新的无序区R[1..n-2]和有序区R[n-1..n]。且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,相同要将R[1..n-2]调整为堆。

    ……
    直到无序区仅仅有一个元素为止。
    (3)大根堆排序算法的基本操作:
    ①建堆。建堆是不断调整堆的过程,从len/2处開始调整,一直到第一个节点,此处len是堆中元素的个数。建堆的过程是线性的过程。从len/2到0处一直调用调整堆的过程。相当于o(h1)+o(h2)…+o(hlen/2) 当中h表示节点的深度。len/2表示节点的个数,这是一个求和的过程,结果是线性的O(n)。
    ②调整堆:调整堆在构建堆的过程中会用到。并且在堆排序过程中也会用到。利用的思想是比較节点i和它的孩子节点left(i),right(i),选出三者最大(或者最小)者。假设最大(小)值不是节点i而是它的一个孩子节点。那边交互节点i和该节点,然后再调用调整堆过程,这是一个递归的过程。调整堆的过程时间复杂度与堆的深度有关系,是lgn的操作,由于是沿着深度方向进行调整的。
    ③堆排序:堆排序是利用上面的两个过程来进行的。首先是依据元素构建堆。

    然后将堆的根节点取出(通常是与最后一个节点进行交换)。将前面len-1个节点继续进行堆调整的过程,然后再将根节点取出,这样一直到全部节点都取出。堆排序过程的时间复杂度是O(nlgn)。由于建堆的时间复杂度是O(n)(调用一次)。调整堆的时间复杂度是lgn,调用了n-1次。所以堆排序的时间复杂度是O(nlgn)。

    2、堆的存储
    一般用数组来表示堆,若根结点存在序号0处, i结点的父结点下标就为(i-1)/2。

    i结点的左右子结点下标分别为2*i+12*i+2

    3、堆排序算法实现(最大堆)
    #include <iostream>
    
    void printArray(int theArray[], int n)
    {
    	for(int i = 0; i < n; i++)
    	{
    		std::cout << theArray[i] << " ";
    	}
    	std::cout << std::endl;
    }
    
    void adjustHeap(int theArray[], int n, int start)  //从start索引处结点開始调整 
    {
    	if(start < 0 || start >= n)
    		return;
    	int fatherIndex;
    	int leftIndex;
    	int rightIndex;
    	int tmp;
    	
    	fatherIndex = start;
    	leftIndex = 2 * fatherIndex + 1;        //start索引处结点的左孩子的索引 
    	rightIndex = leftIndex + 1;             //start索引处结点的右孩子的索引 
    	
    	if(rightIndex < n)          //start索引处结点存在右孩子结点. 
    	{
    		if(theArray[fatherIndex] < theArray[rightIndex])     //右孩子结点大于start索引处结点 。交换 
    		{
    			tmp = theArray[fatherIndex];
    			theArray[fatherIndex] = theArray[rightIndex];
    			theArray[rightIndex] = tmp; 
    		}
    	}
    	if(leftIndex < n)       //start索引处结点存在左孩子结点,但不一定存在右孩子结点
    	{
    		if(theArray[fatherIndex] < theArray[leftIndex])     //右孩子结点大于左孩子结点树,交换 
    		{
    			tmp = theArray[fatherIndex];
    			theArray[fatherIndex] = theArray[leftIndex];
    			theArray[leftIndex] = tmp;
    		}
    	} 
    	adjustHeap(theArray, n, start-1);         //再次递归调整start索引处的上一个结点 
    }
    
    void constructHeap(int theArray[], int n)
    {
    	int start = n / 2; 
    	adjustHeap(theArray, n, start);                 //从length/2处開始调整 
    }
    
    void heapSort(int theArray[], int n)
    {
    	if(n == 1)                                    //当未排序序列中仅仅剩一个元素时,直接跳出递归 
    		return;
    	int length = n;
    	
    	//printArray(theArray, length);              //打印出每次建立最大堆之后,数组排列情况 
    	
    	//将最大堆的堆顶元素与堆末尾元素交换,并取出该元素作为已排序数组元素 
    	int tmp = theArray[0];                    
    	theArray[0] = theArray[length-1];
    	theArray[length-1] = tmp;
    	 
    	//再次调整交换后的堆,使得为满足最大堆 
    	--length;                 //未排序序列长度减一 
    	int start = length / 2;   
    	adjustHeap(theArray, length, start);
    	heapSort(theArray, length);             //递归进行堆排序 
    }
    
    int main(int argc, char *argv[]) {
    	int myArray[] = {5,90,28,4,88,58,38,18,19,20};
    	int length = sizeof(myArray) / sizeof(myArray[0]);
    	constructHeap(myArray, length);
    	heapSort(myArray, length);
    	printArray(myArray, length);
    	return 0;
    }

    4、堆的时间复杂度和空间复杂度
    堆的最好、最坏以及平均时间复杂度是O(nlogn).
    堆的空间复杂度是O(1).
    感想:刚開始实现建立堆时,以为须要建立一个二叉树辅助帮助,事实上这个所谓堆(即全然二叉树)仅仅是逻辑结构。真实的物理结构还是那个顺序数组。在建立大堆或小堆的过程中,均是以顺序数组为基础,而逻辑上是操作一颗全然二叉树,故全然不须要建立一个辅助全然二叉树。

  • 相关阅读:
    appfabric cache配置,实验记录
    appfabric cache存储ef 查询结果的bug
    CruiseControl.NET svn获取 自动编译 ftp上传
    @Ajax.RenderAction 把局部页改为ajax加载
    分布式架构下的mvc 异步controller ajax comet 长连接
    win7重装iis,搞死
    验证码识别的基本思路及方法
    C# 获取程序当前文件夹
    在c#中 限制文本框只能输入数字
    string字符串 获取指定位置范围的子字符串
  • 原文地址:https://www.cnblogs.com/jhcelue/p/7257012.html
Copyright © 2020-2023  润新知