算法题目0 -- 堆排序算法

堆排序算法

简介:堆是一棵完全二叉树,其中每个节点的值都大于等于(或小于等于)其左右子节点的值。堆分为最大堆和最小堆两种,最大堆的根节点是整个堆中的最大值,最小堆的根节点是整个堆中的最小值。

在堆排序中,首先将待排序的数据组织成一个二叉堆。然后,取出堆顶元素,将其与堆底最后一个元素交换位置,并将堆的大小减1。接着将堆顶元素下沉,使得剩余元素重新组成一个堆。重复上述操作,即可完成排序。

堆排序具有良好的时间复杂度,最坏时间复杂度为O(nlogn),空间复杂度为O(1)。相比其他排序算法,堆排序在大数据量时表现更优秀。

一. 基础知识

  1. 最大堆的定义:一个大小为n的堆是一颗包含n个结点的完全二叉树,树中的每个结点的关键字值大于等于其双亲结点的关键字值。(最小堆则是小于等于其双亲结点关键字值)

    当这颗完全二叉树以顺序对方式存储时,事实上排成了结点序列( k 0 k_0 k0, k 1 k_1 k1, k 2 k_2 k2, k 3 k_3 k3 ,…, k n − 1 {k_{n-1}} kn1)。
    所以最大堆中元素的关系:k[i]>=k[2i+1] && k[i] >=k[2i+2]

  2. 堆排序思想

    • 将初始序列构成最大顶堆,则heap[0]为最大元素
    • 将堆顶元素heap[0]与最后一个元素交换,此时得到新的序列(heap[0],heap[1],heap[2],…heap[n-2]),heap[n-1]。其中heap[n-1]为最大元素,将不参与新的建堆过程
    • 由于交换元素后,原序列将不满足堆性质,对上步生成的n-1个元素重新进行建堆过程,即重复(1),(2).
  3. 建堆与排序图解

原始序列 第一个结点调换 xxx

参考:建堆图解

二. 算法实现

#include <stdio.h>
#include <iostream>
using namespace std;
void create_heap(int *heap,int n);
void adjust_down(int *heap,int r,int n);
int main()
{
    int arr[] = {16,7,3,20,17,8,13};
    int size = 7;
    while(size>1) {
    create_heap(arr,size);
    int temp = arr[size-1];
    arr[size-1] = arr[0];
    arr[0] = temp;
    size--;
    }
    
    for(int i =0;i<7;i++)
        cout << arr[i] << " ";
    cout << endl;
    return 0;
}
// n is upper index
void adjust_down(int *heap, int r, int n)
{
    int child = 2*r + 1;
    int temp = heap[r];
    while (child <= n) {
        if((child<n) && (heap[child+1] > heap[child])) child++;
        if(temp>heap[child]) break;
        heap[(child-1)/2] = heap[child];
        child = 2 * child + 1;
    }
    heap[(child-1)/2] = temp;
}
void create_heap(int *heap,int n)
{
    for(int i=(n-1)/2;i>-1;i--)
    {
        adjust_down(heap, i, n-1);
    }
}

三.注意的问题

  1. 建堆过程中,heap[(child-1)/2] = heap[child],这一句我开始理解为heap[r]=heap[child],其实这样是不对的,因为建堆过程中,有向下调整的过程,临时值temp = heap[r]并不是简单的和第一代子结点交换就行,而是一直向下调整,直到合适的位置

  2. create_heap函数中的n代表的是最大索引,而不是关键字个数

  3. 每次建堆都要用到下调堆结构函数,索引i的子代结点的索引为2*i+1, 2*i+2

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值