排序3-堆排序

基本思想

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以看作是对选择排序的改进。

通常堆是通过一维数组来实现的。在起始数组为0的情形中:

  • 父节点i的左子节点在位置(2*i+1);
  • 父节点i的右子节点在位置(2*i+2);
  • 子节点i的父节点在位置floor((i-1)/2);

在堆的数据结构中,堆中的最大值总是位于根节点。堆中定义以下几种操作:

  • 最大堆调整(Max_Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
  • 创建最大堆(Build_Max_Heap):将堆所有数据重新排序
  • 堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算

代码实现

最大堆调整有递归和非递归实现方式。

void Heap_adjust(int arr[], int index, int len)
{

    while(true)
    {
        int iMax = index;
        int iLeft = 2 * index + 1;
        int iRight = 2 * index + 2;

        if(iLeft < len && arr[index] < arr[iLeft])
            iMax = iLeft;
        if(iRight < len && arr[index] < arr[iRight])
            iMax = iRight;
        if(iMax != index)
        {
            swap(arr[index], arr[iMax]);
            index = iMax;
        }
        else
            break;
    }

}

void Heap_adjust2(int arr[], int index, int len)
{
    int iMax = index;
    int iLeft = 2 * index + 1;
    int iRight = 2 * index + 2;

    if(iLeft < len && arr[index] < arr[iLeft])
        iMax = iLeft;

    if(iRight < len && arr[index] < arr[iRight])
        iMax = iRight;

    if(iMax != index)
    {
        swap(arr[index], arr[iMax]);
        Heap_adjust2(arr, iMax, len);
    }
}

void Build_maxheap(int arr[], int len)
{
    for(int i = len / 2; i >= 0; i --)
    {
        Heap_adjust(arr, i , len);
    }
}

void Heap_Sort(int arr[], int len)
{
    Build_maxheap(arr, len);

    for(int i = len - 1; i > 0; i --)
    {
        swap(arr[0], arr[i]);
        Heap_adjust(arr, 0, i);
    }
}

测试代码

#include <iostream>
#include <cstring>
#include <ctime>
#include <cmath>
using namespace std;

#define ArraySize 100000

void swap(int *x, int *y)
{
    int temp;
    temp = *x;
    *x   = *y;
    *y   = temp;
}

void Bubble_sort(int arr[], int len)
{
    for(int i = 0; i < len; i ++)
    {
        for(int j = i + 1; j < len; j ++)
            if(arr[i] > arr[j])
                swap(arr[i], arr[j]);
    }
}

void Bubble_sort1(int arr[], int len)
{
    for(int i = 0; i < len; i ++)
    {
        for(int j = len - 1; j >= i; j --)
        {
            if(arr[i] > arr[j])
                swap(arr[i], arr[j]);
        }
    }
}

void Bubble_sort2(int arr[], int len)
{
    bool flag = true;

    while(flag)
    {
        flag = false;
        for(int i = 0; i < len; i ++)
            for(int j = len - 1; j >= i; j --)
                if(arr[i] > arr[j])
                    swap(arr[i], arr[j]);
    }
}

void Slect_sort(int arr[], int len)
{
    for(int i = 0; i < len; i ++)
    {
        int min_index = i ;
        for(int j = i + 1; j < len; j ++)
        {
            if(arr[min_index] > arr[j])
                min_index = j;
        }

        if(i != min_index)
            swap(arr[i],arr[min_index]);
    }
}

void Insert_sort(int arr[], int len)
{

    for(int i= 1; i < len; i ++)
    {
        int key = arr[i];
        int j = i;
        while(j && arr[j - 1] > key)
        {
            arr[j] = arr[j - 1];
            j --;
        }

        arr[j] = key;
    }
}

void Shell_sort(int arr[], int len)
{
    int increment = len / 2;

    while(increment)
    {
        for(int i = increment; i < len; i ++)
        {
            int key = arr[i];
            /*int j ;
            for(j = i; j >= increment; j -= increment)
            {
                if(arr[j-increment] > key )
                    arr[j] = arr[j-increment];
                else
                    break;

            }*/

            int j = i;
            while(j >= increment && arr[j-increment] > key)
            {
                arr[j] = arr[j-increment];
                j -= increment;
            }

            arr[j] = key;
        }

        increment /= 2;
    }
}

void Shell_sort1(int arr[], int len)
{
    int increment = 0;
    for(increment = len/2; increment > 0; increment /=2)
    {
        for(int i = increment; i < len; i++)
        {
            int key = arr[i];
            int j = 0;
            for(j = i; j >= increment; j -=increment)
            {
                if(arr[j-increment] > key)
                    arr[j] = arr[j-increment];
                else
                    break;
            }

            arr[j] = key;
        }
    }
}

void Shell_sort2(int arr[], int len)
{
    int index = log( 2*len + 1) / log(3.0);

    //cout << index << endl;

    int increment = ( pow(3.0, index) - 1 ) / 2;

    //cout << increment << endl;

    while(increment)
    {
        for(int i = increment; i < len; i ++)
        {
            int key = arr[i];
            /*int j ;
            for(j = i; j >= increment; j -= increment)
            {
                if(arr[j-increment] > key )
                    arr[j] = arr[j-increment];
                else
                    break;

            }*/

            int j = i;
            while(j >= increment && arr[j-increment] > key)
            {
                arr[j] = arr[j-increment];
                j -= increment;
            }

            arr[j] = key;
        }
        index -= 1;
        increment = ( pow(3.0, index) - 1 ) / 2;
    }
}

void Heap_adjust(int arr[], int index, int len)
{

    while(true)
    {
        int iMax = index;
        int iLeft = 2 * index + 1;
        int iRight = 2 * index + 2;

        if(iLeft < len && arr[index] < arr[iLeft])
            iMax = iLeft;
        if(iRight < len && arr[index] < arr[iRight])
            iMax = iRight;
        if(iMax != index)
        {
            swap(arr[index], arr[iMax]);
            index = iMax;
        }
        else
            break;
    }

}

void Heap_adjust2(int arr[], int index, int len)
{
    int iMax = index;
    int iLeft = 2 * index + 1;
    int iRight = 2 * index + 2;

    if(iLeft < len && arr[index] < arr[iLeft])
        iMax = iLeft;

    if(iRight < len && arr[index] < arr[iRight])
        iMax = iRight;

    if(iMax != index)
    {
        swap(arr[index], arr[iMax]);
        Heap_adjust2(arr, iMax, len);
    }
}

void Build_maxheap(int arr[], int len)
{
    for(int i = len / 2; i >= 0; i --)
    {
        Heap_adjust(arr, i , len);
    }
}

void Heap_Sort(int arr[], int len)
{
    Build_maxheap(arr, len);

    for(int i = len - 1; i > 0; i --)
    {
        swap(arr[0], arr[i]);
        Heap_adjust(arr, 0, i);
    }
}

void Print_array(int arr[], int len)
{
    for(int i = 0; i < len; i++)
    {
        cout << arr[i] << " ";
    }
    cout << endl;

}

int main(int argc, char const *argv[])
{
    /* code */
    int Array[ArraySize];
    int Array1[ArraySize];
    int Array2[ArraySize];

    time_t begin , end;

    srand(time(NULL));

    for(int i = 0; i < ArraySize; i ++)
    {
        Array[i] = rand()%ArraySize;
        //cout << Array[i] << " ";
    }

    memcpy(Array1, Array, ArraySize * sizeof(Array1[0]));
    memcpy(Array2, Array, ArraySize * sizeof(Array2[0]));

//  Print_array(Array, ArraySize);

/*  begin = clock();
    Bubble_sort2(Array, ArraySize);
    end = clock();
    cout << "Bubble_sort runtime:   " << double(end - begin) / CLOCKS_PER_SEC << "s" << endl;

    begin = clock();
    Slect_sort(Array1, ArraySize);
    end = clock();
    cout << "Slect_sort runtime:   " << double(end - begin) / CLOCKS_PER_SEC << "s" << endl;

    begin = clock();
    Insert_sort(Array2, ArraySize);
    end = clock();
    cout << "Insert_sort runtime:   " << double(end - begin) / CLOCKS_PER_SEC << "s" << endl;*/ 

    begin = clock();
    Shell_sort1(Array1, ArraySize);
    end = clock();
    cout << "Shell_sort1 runtime:   " << double(end - begin) / CLOCKS_PER_SEC << "s" << endl;

    begin = clock();
    Shell_sort2(Array2, ArraySize);
    end = clock();
    cout << "Shell_sort2 runtime:   " << double(end - begin) / CLOCKS_PER_SEC << "s" << endl;

    begin = clock();
    Heap_Sort(Array, ArraySize);
    end = clock();
    cout << "Heap_Sort runtime:   " << double(end - begin) / CLOCKS_PER_SEC << "s" << endl;

    //Print_array(Array2, ArraySize);
    return 0;
}

运行结果如下:

Shell_sort1 runtime:   0.038s
Shell_sort2 runtime:   0.021s
Heap_Sort runtime:   0.004s

时间复杂度分析

建立N个元素的二叉堆需要花费O(n),在正式排序时,第i次取堆顶的数据事,重建堆需要用O(logi),总共取n?1次堆顶,所以重建堆需要花费O(nlogn)。因此,堆排序的时间复杂度为O(nlogn),又因为堆排序对原数据的初始状态不敏感,所以最好、最坏和平均时间复杂度均为O(nlogn); 可以原地进行,空间复杂度O(1)。

参考资料

堆排序 - 维基百科,自由的百科全书

https://zh.wikipedia.org/wiki/%E5%A0%86%E6%8E%92%E5%BA%8F

常见排序算法 - 堆排序 (Heap Sort) | bubkoo

http://bubkoo.com/2014/01/14/sort-algorithm/heap-sort/

时间: 2024-09-08 00:33:53

排序3-堆排序的相关文章

阿布学排序之堆排序

/** * 需求:堆排序的实现 * 知识储备: * 满二叉树:除叶子结点外的所有结点均有两个子结点,所有叶子结点必须在同一层上. * 完全二叉树: * 若二叉树的深度为h,除第h层外,其它各层(1~h-1)的节点数都达到最大个数,第h层所有结点都连续集中在最左边. * 完全二叉树是有满二叉树而引出来的,对于深度为K的,有N个结点的二叉树,当且仅当每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称它为完全二叉树. * * 二叉堆是完全二叉树或者是近似完全二叉树. * 二叉堆特性: *

排序值 堆排序

按照算法导论上的实现,不过把下标改成从0开始了. 原理: import java.util.Arrays; public class Solution { /** * 每次将堆顶元素交换到最后并从堆中除掉. * * @param a */ public static void heapSort(int[] a) { buildHeap(a); int n = a.length - 1; for (int i = a.length - 1; i > 0; i--) { swap(a, 0, i);

选择排序:堆排序

堆排序(Heap Sort):使用堆这种数据结构来实现排序. 先看下堆的定义: 最小堆(Min-Heap)是关键码序列{k0,k1,-,kn-1},它具有如下特性: ki<=k2i+1, ki<=k2i+2(i=0,1,-) 简单讲:孩子的关键码值大于双亲的. 同理可得,最大堆(Max-Heap)的定义: ki>=k2i+1, ki>=k2i+2(i=0,1,-) 同样的:对于最大堆,双亲的关键码值大于两个孩子的(如果有孩子). 堆的特点: 堆是一种树形结构,而且是一种特殊的完全二

排序 之 堆排序 归并排序

堆的概念 堆是具有下列性质的完全二叉树:每个节点的值都大于或等于其左右孩子结点的值,称为大顶堆:或着每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆. 堆排序 堆排序(Heap Sort)就是利用堆(假设利用大顶堆)进行排序的方法.它的基本思想是,将待排序的序列构造成一个大顶堆.此时,整个序列的最大值就是对顶的根结点.将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次大值.如此反复执行,就可以得到一个

排序算法2--简单选择排序、堆排序

一.简单选择排序和堆排序都属于选择排序 选择排序的思想是:每一趟从待排序的记录中选出关键字最小的记录,按顺序放在以排序的记录序列的后面,知道全部拍完为止. 二.简单选择排序(直接选择排序) 1.简单选择排序法是每次循环找出最值,循环结束后将最值调整到合适位置,交换的次数少. 每次找出当前无序队列中的最小的元素与第一个交换位置,再选择第二小的与第二个交换位置 原始队列:   3 5 6 2 4 1(最小元素1与3交换) 第一步: 1 5 6 2 4 3 (当前最小元素2与5交换) 第二步: 1 2

处理海量数据的三大排序之——堆排序(C++)

在面对大数据量的排序时(100W以上量级数据),通常用以下三种的排序方法:快速排序.归并排序,堆排序.在这个量级上,其他冒泡,选择,插入排序等已经根本没法看了,效率极低,跟前面三种排序差了千百倍,因此不作比较. 这三种排序的平均时间复杂度均为O(nlogn),快速排序,归并排序在面对基本有序序列排序时,效率反会降低.且归并排序需要用到O(n)的临时存储空间.而堆排序没有明显缺点,特别在面对经常会插入新元素的排序需求,堆排序效果最好. 下面是三种排序对100W个无序数组进行排序的时间对比,可以看出

插入排序,希尔排序,堆排序详解

本文将介绍三种排序算法--插入排序,希尔排序,堆排序.本文所有例子都是使用升序 一.插入排序 算法思想 维护一个有序数组,将要插入的数据与有序数组自最后一个元素直到合适位置的数一一比较. eg: 有序数组:1,3,5,6,7   现在待插入数据为2,那么他将会和7,6,5,3,依次作比较,当带插入数据小于有序数组最后的元素大小,则将该元素后移,直到待插入元素找到合适位置为止. 代码实现 void InsertSort(int* a, int size) 02 { 03     assert(a)

插入排序,希尔排序,堆排序

本文将介绍三种排序算法--插入排序,希尔排序,堆排序.本文所有例子都是使用升序 一.插入排序 算法思想 维护一个有序数组,将要插入的数据与有序数组自最后一个元素直到合适位置的数一一比较. eg: 有序数组:1,3,5,6,7   现在待插入数据为2,那么他将会和7,6,5,3,依次作比较,当带插入数据小于有序数组最后的元素大小,则将该元素后移,直到待插入元素找到合适位置为止. 代码实现 void InsertSort(int* a, int size) { assert(a); for (int

排序算法七:选择排序之堆排序

排序算法七:选择排序之堆排序 声明:引用请注明出处http://blog.csdn.net/lg1259156776/ 引言 在我的博文<"主宰世界"的10种算法短评>中给出的首个算法就是高效的排序算法.本文将对排序算法做一个全面的梳理,从最简单的"冒泡"到高效的堆排序等. 上博文讲述了选择排序中的简单排序算法,本文介绍的堆排序是树性选择排序,采用堆这个数据结构来辅助排序. 排序相关的的基本概念 排序:将一组杂乱无章的数据按一定的规律顺次排列起来. 数据

选择排序之堆排序

/* * 选择排序之堆排序 * 按照完全二叉树的顺序存储方式,建立一颗完全二叉树 * 若是大根堆:l(i)>=l(2*i),l(i)>=l(2*i+1) * 若是小根堆:l(i)<=l(2*i),l(i)<=(2*i+1) * 此时,初始堆建成,接下来的工作是进行调整 * 调整的是从[n/2]~1. * 针对大根堆:对于[n/2]处的元素,若其比左右孩子中的较大者小,进行交换 * 同理对于其他的 * void BuildHeap(ET a[],int len){ * for(int