现在的位置: 首页 > 综合 > 正文

堆与堆排序

2018年12月17日 ⁄ 综合 ⁄ 共 1785字 ⁄ 字号 评论关闭

堆排序是一种排序方式。

堆是一种数据结构,全称二叉堆,也叫优先队列。堆至少支持两种操作,insert入堆和deletemin删除掉堆中最小的节点。堆是一颗完全二叉树,因为完全二叉树的性质,我们可以用数组来代替而不需要指针。对于数组中任意元素i,它的左儿子下标是2i,右儿子是2i+1,父亲在i / 2取下限。为了便于计算,数组中的一个元素不存放相关数据。因此堆得结构应该包含一个数组,一个代表最大值的整数和当前堆得大小的整数。

struct Heap{
    int size;
    int capactiy;
    ElementType * elems
};

堆的性质是快速被执行,为了找出最小元素,我们应该将最小元放在根,且任意子树也是一个堆,也应该符合这个原则。堆中除了根节点以为每一个节点的关键字都大于等于其父节点的关键字。

Heap * init(int maxelements){
    Heap * h;
    if(maxelements < Minsize){
        error("size is too small");
    }
    h = malloc(sizeof(struct Heap));
    if(h == NULL)
        eooro("out of space");
    h -> Elements = malloc((maxelements + 1) * sizeof(Elementype));
    h -> capacity = maxelements;
    h -> size = 0;
    h -> Elements[0] = MinData;
    return h;
}

插入操作,将一个元素插入到堆得过程是首先在堆的最后一个元素后边取一个空白节点。然后判断要插入的元素如果插入空白节点的话,是否否符合堆得特性,即节点的值小于其父节点,如果符合则插入完毕,否则将空白节点的与父节点交换,在以此类推,直至堆得根节点。

void insert(Elementtype x, Heap * h){
    int i;
    if(isfull(h)){
        error("heap is full");
        return;
    }
    for(i = ++h->size; h -> Elements[i / 2] > x; i /= 2){
        h -> Elements[i] = h -> Elements[i / 2];
    }
    h -> Elements[i] = x;
}

堆得根节点就是最小的元素,所以取最小值时间复杂度为O(1),删除最小元素时,根节点变成了一个空节点,取空节点的较小的子节点(这里需要判断是否有左右子节点),交换两者,以此类推,直到空节点移动到了最后的一个位置,通过size与下标判断。这里的删除还可以认为是一次筛选,而且可以不设定是从顶部去筛选,该函数是将筛选的结点作为根节点实现堆的特性,为了方便建堆。

void heapadjust(Heap * h, int s, int m){
    int rc = h -> ELmements[s], j;
    for(j = 2 * s; j <= m; j *= 2){
        if(j < m && h -> Elements[j] < h -> Elements[j+1])
            j++;
        if(rc < h -> Elements[j])
            h -> Elements[j/2] = h -> Elemenst[j];
        else
            break;
    }
    h -> Elements[j/2] = rc;
}

删除根节点的时间复杂度是O(log K),k是节点个数。

堆得创建时间复杂度是O(n),设有n个元素,高h = logn,从下向上创建,而终端节点是不需要上述“筛选”操作的,可以得到最后一个非终端节点是n/2取下限。第i层上节点数最多有2^(i-1)个,以该层节点为根节点的堆高度为h - i + 1,每次每个节点需要比较两次,孩子节点比较,孩子于父节点比较,调用次数不会超过n/2取下限。

所以时间复杂度S = 2^(h-1) + 2^(h-2)*2 + … + 2 * (h - 1); 2S - S = S = 2^h -2h + 4 = n - 2logn + 4 = O(n);

堆的创建于排序都需要上述筛选的算法,堆得排序又分大顶堆和小顶堆,其实就是从大到小和从小到大排序。

void Heapsort(Heap * h){
    for(int i = h -> size / 2; i > 0; i--)
        Heapadjust(h, i, h -> size);
    for(int i = h -> size; i > 0; i--){
        swap(h -> Elements[1], h -> Elements[i]);
        Heapadjust(h, 1, i - 1);
    }
}

堆排序的时间复杂度就是建堆和不断筛选上,即使在最坏的情况下,也是O(nlogn)。

抱歉!评论已关闭.