《算法导论》学习笔记——堆排序

本文深入探讨了堆排序算法的核心概念,包括堆的定义、最大堆的维护、构建最大堆的方法以及堆排序的过程。通过实例分析,详细阐述了如何将待排序数组转化为最大堆,并解释了堆排序的复杂度分析。最后,提供了C、Java和Python三种语言的代码实现,帮助读者理解和实践堆排序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

堆排序

1.什么是堆

  (二叉)堆是一个不完全二叉树,即除了最底层之外,该书是完全满的。那么怎样把堆和一个待排序的数组联系起来呢?这就涉及到二叉堆(二叉树)的一些性质:
  - 性质1:设父亲节点的编号为i,则左孩子的编号为2i,右孩子的编号为2i,即LeftChild[i] = 2iRightChild[i] = 2i+1
  - 性质2:二叉堆分为两种,最大堆最小堆,最大堆的性质为除了根节点以外的所有节点i满足条件
      A[Parent(i)] <= A[i]其中Parent[i] = i/2向下取整
      也就是说,某个节点的值至多与其父亲节点的值一样大。最小堆的性质类似,不再赘述。

  所以,可以将最大(最小)堆看成是一定程度按大小排列的数组,见下图。那我们应该如何将一个待排序的数组转化为最大堆呢?我们要解决的第一个问题怎样使得一个堆满足最大堆得性质。

191652297352019.png

2.维护最大堆的性质

  如下图所示,假设现在有一个堆,2号节点不满足最大堆的性质,我们的操作时与其最大的孩子4号节点交换位置,但是某些情况比如交换后导致原来最大孩子处4号节点的值14由于交换后变成一个较小的值4,使得现在的4号节点也不满足最大堆性质,我们只要将刚才的步骤在应用到现在4号节点,即交换4号节点与9号节点的位置。可以看出,这里存在着分治策略的思想。这一步骤算法复杂度为O(lgn)

191653415169115.png

3.最大堆构建

  对于一个输入数组A,若将其映射为堆的话的,根据1中的性质,令i = A.length向下取整,那么A[i]就是最后一个有孩子的节点,从这个节点出发遍历到A[1],对这些节点进行维护最大堆性质的操作,即可得到最大堆,如下图所示。一定要注意遍历的顺序是A[i] down to A[1]而不是A[1] up to A[i],原因是如果采用后面的顺序,不能满足每次循环过程中循环不变量的正确性,具体解释参见《算法导论》。这一步骤复杂度分析有点复杂,为O(n)

191656272669153.png
191657287814513.png

4.堆排序

  根据前面的分析我们已经将一个数组处理成最大堆,但如果我们将其按编号输出得到的并不是按大小完全排好的序列。根据最大堆的性质,编号为A[1]的节点始终是最大的。如下图所示,我们可以将A[1]和A[n]交换位置,然后对新的A[1]进行最大堆维护,然后去掉A[n],将新的A[n]与新的A[1]再次交换,进行最大堆维护,去掉A[n]......直到堆中元素的数目只剩一个,则必然是最小的元素。所以从A[n]遍历到A[2]即可。

191658566416621.png
191659114065432.png

  因此,根据以上分析,堆排序的算法复杂度为O(nlgn)

5.代码实现(C,Java,Python)

  需要注意的是,在上面的分析中,为了易于理解,数组的编号从1开始,但是在实际计算中数组的编号从0开始,所以此时LeftChild[i] = 2iRightChild[i] = 2i+1

C

#include <stdio.h>
#include <stdlib.h>

int length;

void swap(int* a, int* b) {
    int tmp;
    tmp = *a;
    *a = *b;
    *b = tmp;
}

void max_heapify(int* array, int i, int len) {
    int l = 2 * i + 1, r = 2 * i + 2, largest;
    if(l <= len && array[l] > array[i])
        largest = l;
    else
        largest = i;
    if(r <= len && array[r] > array[largest])
        largest = r;
    if(largest != i) {
        swap(&array[largest], &array[i]);
        max_heapify(array, largest, len);
    }
}
    
void build_max_heap(int* array) {
    int i;
    for(i = (length - 1) / 2; i >= 0; i--)
        max_heapify(array, i, length - 1);
}


void heapsort(int* array) {
    int i, local_length = length - 1;
    build_max_heap(array);
    for(i = length - 1; i > 0; i--) {
        swap(&array[0], &array[i]);
        local_length -= 1;
        max_heapify(array, 0, local_length);
    }
}

int main() {
    int *array, i;
    printf("Enter the length of array: ");
    scanf("%d", &length);
    array = (int *)malloc(length * sizeof(int));
    for(i = 0; i < length; i++)
        scanf("%d", &array[i]);
    heapsort(array);
    for(i = 0; i < length; i++)
        printf("%d ", array[i]);
    printf("\n");
    free(array);
    return 0;
}

Java

import java.util.*;

public class HeapSort {
    public static void display(Iterator<Integer> it) {
        while(it.hasNext()) {
            Integer element = it.next();
            System.out.print(element + " ");
        }
    }
    public static void main(String[] args) {
        ArrayList<Integer> array = new ArrayList<Integer>();
        Scanner in = new Scanner(System.in);
        System.out.print("Enter the length of array: ");
        int length = in.nextInt();
        for(int i = 0; i < length; i++)
            array.add(in.nextInt());
        in.close();
        Sort sort = new Sort(array);
        sort.heapSort();
        display(array.iterator());
    }
}

class Sort{
    public Sort(ArrayList<Integer> array) {
        this.array = array;
    }

    public void heapSort() {
        buildHeap();
        int localLength = array.size() - 1;
        for(int i = array.size() - 1; i > 0; i--) {
            int[] list = swap(array.get(0), array.get(i));
            array.set(0, list[0]);
            array.set(i, list[1]);
            localLength -= 1;
            maxHeapify(0, localLength);
        }
    }
    
    public void buildHeap() {
        for(int i = (array.size() - 1) / 2; i >= 0; i--) {
            maxHeapify(i, array.size() - 1);
        }
    }

    public void maxHeapify(int i, int len) {
        int l = 2 * i + 1, r = 2 * i + 2, largest;
        if(l <= len && array.get(l) > array.get(i))
            largest = l;
        else
            largest = i;
        if(r <= len && array.get(r)  > array.get(largest))
            largest = r;
        if(largest != i) {
            int[] list = swap(array.get(largest), array.get(i));
            array.set(largest, list[0]);
            array.set(i, list[1]);
            maxHeapify(largest, len);
        }
    }

    public int[] swap(int a, int b) {
        int[] list = new int[2];
        list[0] = b;
        list[1] = a;
        return list;
    }

    private ArrayList<Integer> array;
}

Python

heapSort.py

def swap(A, a, b):
        c = A["data"][b]
        A["data"][b] = A["data"][a]
        A["data"][a] = c

def maxHeapify(A, i):
        leftChildNum = 2 * i + 1
    rightChildNum = 2 * i + 2
        if leftChildNum <= A["length"] and A["data"][leftChildNum] > A["data"][i]:
            largest = leftChildNum
        else:
            largest = i
        if rightChildNum <= A["length"] and A["data"][rightChildNum] > A["data"][largest]:
            largest = rightChildNum
        if largest != i:
            swap(A, largest, i)
            maxHeapify(A, largest)
    
def buildMaxHeap(A):
        for i in range(A["length"]/2, -1, -1):
        maxHeapify(A, i)
    
def heapSort(A):
        sortedList = []
        buildMaxHeap(A);
        for i in range(A["length"], 0, -1):
            sortedList.append(A["data"][0])
            swap(A, i, 0)
            A["length"] -= 1
            maxHeapify(A, 0)
        sortedList.append(A["data"][0])
        sortedList.reverse()
        return sortedList

test.py

import heapSort

A = {"length": 9, "data": [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]}
sortedList = heapSort.heapSort(A)
print sortedList

转载于:https://www.cnblogs.com/zhxbao/p/heap_sort.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值