不知

堆排序

2021-11-02|来源:|发布者:

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

堆排序可以说是一种利用堆的概念来排序的选择排序。分为两种方法:大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;

1. 算法步骤

1、创建一个堆 H[0……n-1];

2、把堆首(最大值)和堆尾互换;

3、把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;

4、重复步骤 2,直到堆的尺寸为 1。

2. 动图演示

堆排序堆排序

3. 时间复杂度

堆排序的平均时间复杂度为 Ο(nlogn)。

4. 算法实现

堆排序C语言

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

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

void max_heapify(int arr[], int start, int end) 
{
    //建立父节点指标和子节点指标
    int dad = start;
    int son = dad * 2 + 1;
    while (son <= end)  //若子节点指标在范围内才做比较
        {
            if (son + 1 <= end && arr[son] < arr[son + 1]) 
            //先比较两个子节点大小,选择最大的
            son++;
        if (arr[dad] > arr[son]) //如果父节点大於子节点代表调整完毕,直接跳出函数
            return;
        else  //否则交换父子内容再继续子节点和孙节点比较
        {
            swap(&arr[dad], &arr[son]);
            dad = son;
            son = dad * 2 + 1;
        }
    }
}

void heap_sort(int arr[], int len) 
{
    int i;
    //初始化,i从最後一个父节点开始调整
    for (i = len / 2 - 1; i >= 0; i--)
        max_heapify(arr, i, len - 1);
    //先将第一个元素和已排好元素前一位做交换,再重新调整,直到排序完毕
    for (i = len - 1; i > 0; i--) 
    {
        swap(&arr[0], &arr[i]);
        max_heapify(arr, 0, i - 1);
    }
}

int main() {
    int arr[] = { 3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6 };
    int len = (int) sizeof(arr) / sizeof(*arr);
    heap_sort(arr, len);
    int i;
    for (i = 0; i < len; i++)
        printf("%d ", arr[i]);
    printf("\n");
    return 0;
}

堆排序C++

#include <iostream>
#include <algorithm>
using namespace std;

void max_heapify(int arr[], int start, int end) 
{
    //建立父节点指标和子节点指标
    int dad = start;
    int son = dad * 2 + 1;
    while (son <= end)  //若子节点指标在范围内才做比较
    {    
        if (son + 1 <= end && arr[son] < arr[son + 1]) //先比较两个子节点大小,选择最大的
            son++;
        if (arr[dad] > arr[son]) //如果父节点大於子节点代表调整完毕,直接跳出函数
            return;
        else  //否则交换父子内容再继续子节点和孙节点比较
        {
            swap(arr[dad], arr[son]);
            dad = son;
            son = dad * 2 + 1;
        }
    }
}

void heap_sort(int arr[], int len) 
{
    //初始化,i从最後一个父节点开始调整
    for (int i = len / 2 - 1; i >= 0; i--)
        max_heapify(arr, i, len - 1);
    //先将第一个元素和已经排好的元素前一位做交换,再从新调整(刚调整的元素之前的元素),直到排序完毕
    for (int i = len - 1; i > 0; i--) 
    {
        swap(arr[0], arr[i]);
        max_heapify(arr, 0, i - 1);
    }
}

void main() 
{
    int arr[] = { 3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6 };
    int len = (int) sizeof(arr) / sizeof(*arr);
    heap_sort(arr, len);
    for (int i = 0; i < len; i++)
        cout << arr[i] << ' ';
    cout << endl;
    system("pause");
}

堆排序PHP

function hsort(array &$arr, $len){
    $idx = $len - 1;
    //创建堆操作,并使得堆有序
    for($k=floor($len/2); $k>=0; $k--){
        sink($arr, $k, $idx);
    } 

    //排序操作
    while($idx>0){
        //获取最大值操作
        list($arr[0], $arr[$idx]) = [$arr[$idx], $arr[0]];
        $idx--;
        //堆调整下沉
        sink($arr, 0, $idx);        
    }

}

//堆调整下沉,小数字下沉
function sink(array &$arr, $low, $high){

    //从$low开始找子节点
    while($low*2+1<=$high){
        //获取low的直接左子节点
        $j = $low*2+1;

        //判断$low位置节点子节点中的较大子节点
        if($j<$high && $arr[$j]<$arr[$j+1]){
            $j++;
        }

        if($arr[$low]>$arr[$j]){
            break;
        }
        //交换low节点和子节点的值
        list($arr[$low], $arr[$j]) = [$arr[$j], $arr[$low]];

        //继续排查下沉后子节点是否需要进行下沉操作
        $low = $j;
    }
}

$a = [4,3,6,7,5,1,9,0,2];
hsort($a, count($a));
print_r($a);

堆排序JAVA

/**
* 选择排序-堆排序
* @param array 待排序数组
* @return 已排序数组
*/
public static int[] heapSort(int[] array) {
	//这里元素的索引是从0开始的,所以最后一个非叶子结点array.length/2 - 1
	for (int i = array.length / 2 - 1; i >= 0; i--) {  
		adjustHeap(array, i, array.length);  //调整堆
	}

	// 上述逻辑,建堆结束
	// 下面,开始排序逻辑
	for (int j = array.length - 1; j > 0; j--) {
		// 元素交换,作用是去掉大顶堆
		// 把大顶堆的根元素,放到数组的最后;换句话说,就是每一次的堆调整之后,都会有一个元素到达自己的最终位置
		swap(array, 0, j);
		// 元素交换之后,毫无疑问,最后一个元素无需再考虑排序问题了。
		// 接下来我们需要排序的,就是已经去掉了部分元素的堆了,这也是为什么此方法放在循环里的原因
		// 而这里,实质上是自上而下,自左向右进行调整的
		adjustHeap(array, 0, j);
	}
	return array;
}

/**
* 整个堆排序最关键的地方
* @param array 待组堆
* @param i 起始结点
* @param length 堆的长度
*/
public static void adjustHeap(int[] array, int i, int length) {
	// 先把当前元素取出来,因为当前元素可能要一直移动
	int temp = array[i];
	for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {  //2*i+1为左子树i的左子树(因为i是从0开始的),2*k+1为k的左子树
		// 让k先指向子节点中最大的节点
		if (k + 1 < length && array[k] < array[k + 1]) {  //如果有右子树,并且右子树大于左子树
			k++;
		}
		//如果发现结点(左右子结点)大于根结点,则进行值的交换
		if (array[k] > temp) {
			swap(array, i, k);
			// 如果子节点更换了,那么,以子节点为根的子树会受到影响,所以,循环对子节点所在的树继续进行判断
				i  =  k;
					} else {  //不用交换,直接终止循环
			break;
		}
	}
}

/**
* 交换元素
* @param arr
* @param a 元素的下标
* @param b 元素的下标
*/
public static void swap(int[] arr, int a, int b) {
	int temp = arr[a];
	arr[a] = arr[b];
	arr[b] = temp;
}

堆排序JavaScript

var len;    // 因为声明的多个函数都需要数据长度,所以把len设置成为全局变量

function buildMaxHeap(arr) {   // 建立大顶堆
    len = arr.length;
    for (var i = Math.floor(len/2); i >= 0; i--) {
        heapify(arr, i);
    }
}

function heapify(arr, i) {     // 堆调整
    var left = 2 * i + 1,
        right = 2 * i + 2,
        largest = i;

    if (left < len && arr[left] > arr[largest]) {
        largest = left;
    }

    if (right < len && arr[right] > arr[largest]) {
        largest = right;
    }

    if (largest != i) {
        swap(arr, i, largest);
        heapify(arr, largest);
    }
}

function swap(arr, i, j) {
    var temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

function heapSort(arr) {
    buildMaxHeap(arr);

    for (var i = arr.length-1; i > 0; i--) {
        swap(arr, 0, i);
        len--;
        heapify(arr, 0);
    }
    return arr;
}

堆排序C#语言

/// <summary>
/// 堆排序
/// </summary>
/// <param name="arr">待排序数组</param>
static void HeapSort(int[] arr)
{
    int vCount = arr.Length;
    int[] tempKey = new int[vCount + 1];
    // 元素索引从1开始
    for (int i = 0; i < vCount; i++)
    {
        tempKey[i + 1] = arr[i];
    }
    // 初始数据建堆(从含最后一个结点的子树开始构建,依次向前,形成整个二叉堆)
    for (int i = vCount / 2; i >= 1; i--)
    {
        Restore(tempKey, i, vCount);
    }
    // 不断输出堆顶元素、重构堆,进行排序
    for (int i = vCount; i > 1; i--)
    {
        int temp = tempKey[i];
        tempKey[i] = tempKey[1];
        tempKey[1] = temp;
        Restore(tempKey, 1, i - 1);
    }
    //排序结果
    for (int i = 0; i < vCount; i++)
    {
        arr[i] = tempKey[i + 1];
    }
}
/// <summary>
/// 二叉堆的重构(针对于已构建好的二叉堆首尾互换之后的重构)
/// </summary>
/// <param name="arr"></param>
/// <param name="rootNode">根结点j</param>
/// <param name="nodeCount">结点数</param>
static void Restore(int[] arr, int rootNode, int nodeCount)
{
    while (rootNode <= nodeCount / 2) // 保证根结点有子树
    {
        //找出左右儿子的最大值
        int m = (2 * rootNode + 1 <= nodeCount && arr[2 * rootNode + 1] > arr[2 * rootNode]) ? 2 * rootNode + 1 : 2 * rootNode;
        if (arr[m] > arr[rootNode])
        {
            int temp = arr[m];
            arr[m] = arr[rootNode];
            arr[rootNode] = temp;
            rootNode = m;
        }
        else
        {
            break;
        }
    }
}

堆排序Python

def big_endian(arr,start,end):    
root=start    
child=root*2+1 #左孩子    
while child<=end:
#孩子比最后一个节点还大,也就意味着最后一个叶子节点了,就得跳出去一次循环,已经调整完毕     
	if child+1<=end and arr[child]<arr[child+1]:
	#为了始终让其跟子元素的较大值比较,如果右边大就左换右,左边大的话就默认           
		child+=1            
	if arr[root]<arr[child]:
	#父节点小于子节点直接交换位置,同时坐标也得换,这样下次循环可以准确判断:是否为最底层,
	#是不是调整完毕                
		arr[root],arr[child]=arr[child],arr[root]                
		root=child                
		child=root*2+1            
	else:               
	break
	
def heap_sort(arr): #无序区大根堆排序    
first=len(arr)//2 - 1    
for start in range(first,-1,-1):
#从下到上,从左到右对每个节点进行调整,循环得到非叶子节点        
	big_endian(arr,start,len(arr)-1) #去调整所有的节点    
for end in range(len(arr)-1,0,-1):        
	arr[0],arr[end]=arr[end],arr[0] #顶部尾部互换位置        
	big_endian(arr,0,end-1) #重新调整子节点的顺序,从顶开始调整    
return arr

def main():    
l=[3,1,4,9,6,7,5,8,2,10]    
print(heap_sort(l))

if __name__=="__main__":    
main()

堆排序Go语言

func heapSort(arr []int) []int {
	arrLen := len(arr)
	buildMaxHeap(arr, arrLen)
	for i := arrLen - 1; i >= 0; i-- {
			swap(arr, 0, i)
			arrLen -= 1
			heapify(arr, 0, arrLen)
	}
	return arr
}

func buildMaxHeap(arr []int, arrLen int) {
	for i := arrLen / 2; i >= 0; i-- {
			heapify(arr, i, arrLen)
	}
}

func heapify(arr []int, i, arrLen int) {
	left := 2*i + 1
	right := 2*i + 2
	largest := i
	if left < arrLen && arr[left] > arr[largest] {
			largest = left
	}
	if right < arrLen && arr[right] > arr[largest] {
			largest = right
	}
	if largest != i {
			swap(arr, i, largest)
			heapify(arr, largest, arrLen)
	}
}

func swap(arr []int, i, j int) {
	arr[i], arr[j] = arr[j], arr[i]
}
用手机扫一扫访问本站
利民吧文章数据均来自于互联网,版权归原作者所有。如有侵犯您权利的资源,请联系我们处理。
Copyright © 2016-2024 利民吧 版权所有