【算法】————7、堆排序

本文详细介绍了堆排序算法的基本原理,包括构建大顶堆的过程、堆排序的具体步骤及其实现方法,并提供了JavaScript和Java代码示例。此外,还分析了堆排序的时间复杂度、空间复杂度及其稳定性。

算法简介

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

算法描述和实现

具体算法描述如下:

  • <1>.将初始待排序关键字序列(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,则整个排序过程完成。

Javascript代码实现:

/*方法说明:堆排序
@param  array 待排序数组*/
function heapSort(array) {
    console.time('堆排序耗时');
    if (Object.prototype.toString.call(array).slice(8, -1) === 'Array') {
        //建堆
        var heapSize = array.length, temp;
        for (var i = Math.floor(heapSize / 2) - 1; i >= 0; i--) {
            heapify(array, i, heapSize);
        }

        //堆排序
        for (var j = heapSize - 1; j >= 1; j--) {
            temp = array[0];
            array[0] = array[j];
            array[j] = temp;
            heapify(array, 0, --heapSize);
        }
        console.timeEnd('堆排序耗时');
        return array;
    } else {
        return 'array is not an Array!';
    }
}
/*方法说明:维护堆的性质
@param  arr 数组
@param  x   数组下标
@param  len 堆大小*/
function heapify(arr, x, len) {
    if (Object.prototype.toString.call(arr).slice(8, -1) === 'Array' && typeof x === 'number') {
        var l = 2 * x + 1, r = 2 * x + 2, largest = x, temp;
        if (l < len && arr[l] > arr[largest]) {
            largest = l;
        }
        if (r < len && arr[r] > arr[largest]) {
            largest = r;
        }
        if (largest != x) {
            temp = arr[x];
            arr[x] = arr[largest];
            arr[largest] = temp;
            heapify(arr, largest, len);
        }
    } else {
        return 'arr is not an Array or x is not a number!';
    }
}
var arr=[91,60,96,13,35,65,46,65,10,30,20,31,77,81,22];
console.log(heapSort(arr));//[10, 13, 20, 22, 30, 31, 35, 46, 60, 65, 65, 77, 81, 91, 96]

JAVA:

 

package flyingcat.sort;
/**
 *
 * @author FlyingCat
 * Date: 2013-8-26
 *
 */
public class ArrayHeap {
    private int[] array;
    public ArrayHeap(int[] arr) {
        this.array = arr;
    }
    private int getParentIndex(int child) {
        return (child - 1) / 2;
    }
    private int getLeftChildIndex(int parent) {
        return 2 * parent + 1;
    }
    /**
     * 初始化一个大根堆。
     */
    private void initHeap() {
        int last = array.length - 1;
        for (int i = getParentIndex(last); i >= 0; --i) { // 从最后一个非叶子结点开始筛选
            int k = i;
            int j = getLeftChildIndex(k);
            while (j <= last) {
                if (j < last) {                     
                    if (array[j] <= array[j + 1]) { // 右子节点更大
                        j++;
                    }
                }
                if (array[k] > array[j]) {           //父节点大于子节点中较大者,已经找到最终位置
                    break; // 停止筛选
                } else {
                    swap(k, j);
                    k = j; // 继续筛选
                }
                j = getLeftChildIndex(k);
            }// loop while
        }// loop i
    }
    /**
     * 调整堆。
     */
    private void adjustHeap(int lastIndex) {
        int k = 0;
        while (k <= getParentIndex(lastIndex)) {
            int j = getLeftChildIndex(k);
            if (j < lastIndex) {
                if (array[j] < array[j + 1]) {
                    j++;
                }
            }
            if (array[k] < array[j]) {
                swap(k, j);
                k = j; // 继续筛选
            } else {
                break; // 停止筛选
            }
        }
    }
    /**
     * 堆排序。
     * */
    public void sort() {
        initHeap();
        int last = array.length - 1;
        while (last > 0) {
            swap(0, last);
            last--;
            if (last > 0) { // 这里如果不判断,将造成最终前两个元素逆序。
                adjustHeap(last);
            }
        }
    }
    private void swap(int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}

算法性能/复杂度

堆排序的时间复杂度非常稳定(我们可以看到,对输入数据不敏感),为O(n㏒n)复杂度,最好情况与最坏情况一样。

但是,其空间复杂度依实现不同而不同。上面即讨论了两种常见的复杂度:O(n)与O(1)。本着节约空间的原则,我推荐O(1)复杂度的方法。

算法稳定性

堆排序存在大量的筛选和移动过程,属于不稳定的排序算法。

算法适用场景

堆排序在建立堆和调整堆的过程中会产生比较大的开销,在元素少的时候并不适用。但是,在元素比较多的情况下,还是不错的一个选择。尤其是在解决诸如“前n大的数”一类问题时,几乎是首选算法。

算法分析

  • 最佳情况:T(n) = O(nlogn)
  • 最差情况:T(n) = O(nlogn)
  • 平均情况:T(n) = O(nlogn)

 

### 堆排序算法详解 堆排序是一种基于比较的高效排序算法,其核心思想是利用堆这种数据结构来完成排序操作。堆可以被看作是一棵完全二叉树,并且满足堆积性质:对于最大堆而言,任意节点的关键字都不小于其子节点的关键字;而对于最小堆,则相反。 #### 一、基本概念 堆排序分为两种主要形式——最大堆和最小堆。在最大堆中,父节点始终大于等于其子节点[^4]。因此,在一个数组表示的最大堆中,第一个元素总是当前集合中的最大值。同样地,在最小堆中,父节点始终小于等于其子节点。 #### 二、主要过程 堆排序的过程主要包括以下几个方面: 1. **建堆** 将输入的数据构建成一个初始堆(通常是从最后一个非叶子节点向上逐层调整)。这一阶段的目标是使整个数据集符合堆定义的要求。 2. **堆调整** 当移除堆顶元素后,需要重新调整剩下的部分以保持堆特性不变。这一步骤称为“下沉”,即将新的根节点与其较大的孩子交换位置直到恢复堆属性为止[^3]。 #### 三、特点分析 - 时间复杂度稳定为 O(n log n),无论最好情况还是最坏情况下都适用; - 是一种原地排序方法,不需要额外存储空间; - 不稳定性:由于可能涉及多次覆盖写入操作,所以它不是稳定的排序方式[^2]。 #### 四、C代码实现示例 以下是使用 C 编程语言编写的简单版本的堆排序程序: ```c #include <stdio.h> // 调整堆函数 void heapify(int arr[], int n, int i){ int largest = i; // 初始化最大为根节点 int l = 2*i + 1; // 左子节点 int r = 2*i + 2; // 右子节点 if (l < n && arr[l] > arr[largest]) largest = l; if (r < n && arr[r] > arr[largest]) largest = r; if(largest !=i ){ swap(&arr[i], &arr[largest]); heapify(arr,n,largest); } } // 主要堆排序逻辑 void heapsort(int arr[],int n){ for(int i=n/2 -1;i>=0;i--){ heapify(arr,n,i); } for(int i= n-1 ;i>0;i--){ swap(&arr[0],&arr[i]); heapify(arr,i,0); } } ``` 上述代码展示了如何通过递归调用来维护堆结构并最终完成排序任务[^1]。 --- ### Java 实现示例 如果考虑另一种主流编程语言如 Java 的话,也可以按照相似思路编写如下所示的堆排序类: ```java public class HeapSort { public void sort(int[] array) { int length = array.length; // 构造初始堆 for (int i = length / 2 - 1; i >= 0; i--) { adjustHeap(array, i, length); } // 进行n-1次循环处理 for (int j = length - 1; j > 0; j--) { // 把当前最大的放到最后面去 swap(array, 0, j); // 对前面j-1个数再次进行堆化 adjustHeap(array, 0, j); } } private static void adjustHeap(int[] array,int index ,int size){ int temp=array[index]; for(int k=index*2+1;k<size;k=k*2+1){ if(k+1<size&&array[k]<array[k+1]){ k++; } if(temp>=array[k])break; array[index]=array[k]; index=k; } array[index]=temp; } private static void swap(int[] data, int a, int b){ int tmp=data[a]; data[a]=data[b]; data[b]=tmp; } } ``` 这段代码实现了完整的堆排序流程,包括初始化堆以及后续每次删除后的重排工作。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FLy_鹏程万里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值