比较排序3:堆排序

sorting(按从小到大排序)

一、堆排序(选择排序)

不稳定

步骤:

大顶堆:每个节点的值都大于等于其左右子节点的值(用于升序)

小顶堆:每个节点的值都小于等于其左右子节点的值(用于排序)

1、构造初始堆

将序列构造成二叉树(按下标构造)

2、构造大顶堆

(1)从最后一个非叶子节点开始,从右向左,从下至上进行调整(找出父节点、左子节点、右子节点中最大的与父节点交换,使得父节点最大),使得堆顶元素最大

(2)将堆顶元素与末尾元素交换,这样末尾元素最大(固定此末尾元素),重复(1),找到第二大元素,循环直到排好序

c++代码:

 
 
#include <math.h>
#include <iostream>
#include <vector>
using namespace std;
//树结构
struct Tree{
int val;
Tree* left;
Tree* right;
Tree(int i) : val(i),left(NULL),right(NULL) {}
};


void HeapSort(vector<int>& sequence)
{
    /***********************/
    /******1、构造初始堆*******/
    const int c_len = sequence.size();  //待排序序列长度
    vector<Tree> trees;
    for(int i=0; i<c_len; ++i)
    {
        Tree tree(sequence[i]);
        trees.push_back(tree);
    }
    //计算二叉树层数
    float f_layers = log(c_len+1) / log(2);
    int layers = ceil(f_layers);  //二叉树层数
    //按层序遍历构造二叉树
    int layer_nodes, init_nodes, next_nodes;
    int node_index, left_index, right_index;
    for(int i=1; i<layers; ++i)
    {
        //第i层的节点编号为[init_nodes,next_nodes)
        layer_nodes = pow(2, i-1);  //第i层节点个数
        next_nodes = pow(2, i) - 1;  //第i+1层第一个节点编号
        init_nodes = next_nodes - layer_nodes;  //第i层第一个节点编号
        for(node_index=init_nodes; node_index<next_nodes; ++node_index)
        {
            left_index = next_nodes + (node_index - init_nodes) * 2;  //左子节点的编号
            if(left_index >= c_len) break;
            trees[node_index].left = &trees[left_index];
            right_index = left_index + 1;  //右子节点的编号
            if(right_index >= c_len) break;
            trees[node_index].right = &trees[right_index];
        }
    }
    /********************/


    /*******************/
    /****2、构造大顶堆*****/
    int temp;  //交换用的临时值
    int len = c_len;  //待排序序列有效长度(去除末尾已经排好序的节点)
    int current_layer, valid_layer, last2_index;
    float f_current_layer, f_valid_layer;
    while(len > 1)
    {
        f_valid_layer = log(len+1) / log(2);
        valid_layer = ceil(f_valid_layer);  //去除末尾已经排好序的节点之后,树的有效层数
        last2_index = pow(2, valid_layer) - 2;  //按有效层数,倒数第二层最后一个节点的编号

        /********************************************/
        /****2(1)对堆进行调整,使得堆顶元素最大******/
        for(int i=last2_index; i>=0; --i)
                if(trees[i].left != NULL)
                {
                    f_current_layer = log(i+1) / log(2) + 1;
                    current_layer = floor(f_current_layer);  //当前节点(第i个节点)所在层数
                    left_index = pow(2, current_layer) - 1 + (i - pow(2, current_layer-1) + 1) * 2;  //当前节点的左子节点编号
                    right_index = left_index + 1;    //当前节点的右子节点编号
                    if(left_index <= len-1)  //左子节点为有效节点
                        if((right_index<=len-1) && (trees[i].right!=NULL))  //右子节点为有效节点
                            if((trees[i].left->val) >= (trees[i].right->val))
                                if((trees[i].left->val) > (trees[i].val))
                                {
                                    temp = trees[i].val;
                                    trees[i].val = trees[i].left->val;
                                    trees[i].left->val = temp;
                                }
                            else
                                if((trees[i].right->val) > (trees[i].val))
                                {
                                    temp = trees[i].val;
                                    trees[i].val = trees[i].right->val;
                                    trees[i].right->val = temp;
                                }
                        else
                            if((trees[i].left->val) > (trees[i].val))
                            {
                                temp = trees[i].val;
                                trees[i].val = trees[i].left->val;
                                trees[i].left->val = temp;
                            }
                }
        /**********************************/

        /**********************************/
        /*****2(2)将堆顶元素与末尾元素交换*****/
        temp = trees[0].val;
        trees[0].val = trees[len-1].val;
        trees[len-1].val = temp;

        --len;
    }
    /******************************/


    /*****得到排序后的sequence*****/
    len = sequence.size();
    for(int i=0; i<len; i++)
        sequence[i] = trees[i].val;
}

空间复杂度:如果原序列以初始堆的形式给出,只有temp占用空间

                   S(n) = 1

时间复杂度:

任意情况:时间复杂度 = 外层循环次数(while) x 内层循环次数(for) x 常数

                外层循环次数为序列长度n, 内层循环次数为树的高度lgn+1向下取整

                即T(n) = (n x lgn) x 常数 = θ(nlgn)

猜你喜欢

转载自blog.csdn.net/qw_sunny/article/details/80625331