堆排序

时间复杂度:O(nlgn)
空间复杂度:O(1)

最关键的问题:创建大根堆

关键:创建大根堆


/*
说明:堆排序是完全二叉树顺序存储的应用,存储在数组中。

Q1:假设有N个结点进行堆排序,那么存储在数组中的下标是1~N,0下标不使用。
   为什么用1~N,而不是用0~N-1下标呢?
A1:(1)由父子结点的关系决定的  (2)还有一个重点应用:A[0]在向下调整
    函数AdjustDown函数中保存着i的父节点k的值。

Q2:AdjustDown(A, i, len)函数每执行一次,至多调整几个值?
A2:最初认为最多能调整一个值,其实不然,最多可以调整值的个数是(树高-父节点i所在高度+1)。
    AdjustDown(A, i, len)函数在每次调整完一次父节点i的值后,要更新父节点i变成i的子节点,
    然后在进行调整判断,...,如此循环往复,直到退出循环。

Q3:创建大根堆函数BuildMaxHeap中调用AdjustDown调整函数的调用方向-->是从
    第一个父节点到最后一个父节点?还是从最后一个父节点到第一个父节点?
A3:根据堆排序创建大根堆过程,可得:调整方向是从最后一个父节点,到第一个父
    节点-->因此循环是for(int i=len/2;i>=1;i--)
*/


#include<stdio.h>


/*
A[0]: 始终存放着每一次循环的父节点的值
k: 始终存放着每一次循环的父节点的下标位置
i: 始终存放着k父节点左右结点较大的值

for循环的范围是左孩子结点i的范围
    for(int i=2*k;i<=len;i*=2)

for循环中,
    更新父节点更新的是父节点的下标位置
*/
void AdjustDown(int A[], int k, int len)  //k是要调节的父节点
{
    A[0] = A[k]; //将k父节点的值赋值给A[0],A[0]始终保存着每个父节点的值

    //调节是一个循环的过程,因此写个循环
    for (int i = 2 * k; i <= len; i *= 2)  //i节点初始为父节点k的左孩子
    {
        //在保证有右孩子的基础上,判断k的右孩子是否大于左孩子
        if (i+1<=len && A[i] < A[i + 1]) //i+1<=len表示右孩子在数组中,右孩子存在
            i++;

        if (A[i] > A[0])  //如果孩子i大于父节点,进行处理
        {
            A[k] = A[i];  //将左孩子的值赋值给父节点
            k = i;  //更新左孩子i变成新的父节点,用于下一次循环
        }
        else  //若孩子i不大于父节点,则证明下面已经全部符合大根堆要求,直接break出for循环
            break;
    }

    //执行完所有的for循环调整后,不要忘记把A[0]中存储的最初的父节点A[k]赋值给当前父节点k
    A[k] = A[0];
}

void BuildMaxHeap(int A[], int len)
{
    for (int i = len / 2; i >= 1; i--)
    {
        AdjustDown(A, i, len);
    }
}



int main()
{
    int A[] = { 0,53,17,78,9,45,65,87,32 };

    int length = sizeof(A) / sizeof(A[0]);

    BuildMaxHeap(A, length);

    for (int i = 1; i < length; i++)
        printf("%5d", A[i]);

    printf("\n");
}

这里写图片描述

堆排序


#include<stdio.h>


/*
A[0]: 始终存放着每一次循环的父节点的值
k: 始终存放着每一次循环的父节点的下标位置
i: 始终存放着k父节点左右结点较大的值
*/
void AdjustDown(int A[], int k, int len)  //k是要调节的父节点
{
    A[0] = A[k]; //将k父节点的值赋值给A[0],A[0]始终保存着每个父节点的值

    //调节是一个循环的过程,因此写个循环
    for (int i = 2 * k; i <= len; i *= 2)  //i节点初始为父节点k的左孩子
    {
        //在保证有右孩子的基础上,判断k的右孩子是否大于左孩子
        if (i+1<=len && A[i] < A[i + 1]) //i+1<=len表示右孩子在数组中,右孩子存在
            i++;

        if (A[i] > A[0])  //如果孩子i大于父节点,进行处理
        {
            A[k] = A[i];  //将左孩子的值赋值给父节点
            k = i;  //更新左孩子i变成新的父节点,用于下一次循环
        }
        else  //若孩子i不大于父节点,则证明下面已经全部符合大根堆要求,直接break出for循环
            break;
    }

    //执行完所有的for循环调整后,不要忘记把A[0]中存储的最初的父节点A[k]赋值给当前父节点k
    A[k] = A[0];
}

void BuildMaxHeap(int A[], int len) //创建大根堆
{
    for (int i = len / 2; i >= 1; i--)
    {
        AdjustDown(A, i, len);
    }
}

void swap(int &a, int &b)
{
    int t = a; a = b; b = t;
}

void HeapSort(int A[], int len)  //堆排序
{
    for (int i = 1; i <= len; i++)
    {
        BuildMaxHeap(A, len - i + 1);
        swap(A[1], A[len - i + 1]);
    }
}


int main()
{
    int A[] = { 0,5,17,7,9,5,65,87,32 };

    int length = sizeof(A) / sizeof(A[0]);

    HeapSort(A, length-1);

    for (int i = 1; i < length; i++)
        printf("%5d", A[i]);

    printf("\n");
}

堆排序的应用题:
这里写图片描述

#include<stdio.h>

void AdjustDown(int A[], int k, int len)  //k是要调节的父节点
{
    A[0] = A[k]; //将k父节点的值赋值给A[0],A[0]始终保存着每个父节点的值

    //调节是一个循环的过程,因此写个循环
    for (int i = 2 * k; i <= len; i *= 2)  //i节点初始为父节点k的左孩子
    {
        //在保证有右孩子的基础上,判断k的右孩子是否大于左孩子
        if (i + 1 <= len && A[i] > A[i + 1]) //i+1<=len表示右孩子在数组中,右孩子存在
            i++;

        if (A[i] < A[0])  //如果孩子i大于父节点,进行处理
        {
            A[k] = A[i];  //将左孩子的值赋值给父节点
            k = i;  //更新左孩子i变成新的父节点,用于下一次循环
        }
        else  //若孩子i不大于父节点,则证明下面已经全部符合大根堆要求,直接break出for循环
            break;
    }

    //执行完所有的for循环调整后,不要忘记把A[0]中存储的最初的父节点A[k]赋值给当前父节点k
    A[k] = A[0];
}

//创建小根堆
void BuildMinHeap(int A[], int len)  
{
    for (int i = len / 2; i >= 1; i--)
    {
        AdjustDown(A, i, len);
    }
}
void swap(int &a, int &b)
{
    int t = a; a = b; b = t;
}

//堆排序
void HeapSort(int A[], int len)
{
    for (int i = 1; i <= len; i++)
    {
        BuildMinHeap(A, len - i + 1);
        //printf("%5d", A[1]);
        swap(A[1], A[len - i + 1]);
    }
}


int main()
{
    int A[] = { 0,2, 1, 4, 3, 6, 5, 8, 7, 10, 9 };
    int k = 2;
    int kk = k;
    int len = sizeof(A) / sizeof(A[0]) - 1;   
    int *B = new int[len + 1];
    for (int i = k, j = 1; k <= len;)  //排序1~N-K+1个
    {
        BuildMinHeap(A, 2);
        B[j++] = A[1];
        //printf("%5d", A[1]);
        A[1] = A[++k];
    }

    //剩下的N-K+2~N个结点用堆排序
    A[1] = A[kk];
    HeapSort(A, kk-1);


    for (int i = 1; i <= len; i++)
        printf("%5d", B[i]);
    printf("\n");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值