美文网首页
5. 堆和堆排序

5. 堆和堆排序

作者: 鬼谷神奇 | 来源:发表于2016-02-26 15:44 被阅读46次
一、基本知识
  1. 初始化堆:按照完全二叉树的结构,从上到下、从左到右依次放入元素,然后可以认为所有的叶子结点已经符合大(小)顶堆,接下来,从下到上、从右到左依次调整所有非叶子结点,直至满足大(小)顶堆的要求。
  2. 堆的插入:每次插入一个结点,只需调整插入结点与其父结点的位置,以及由于与父结点的调整带来的其它不满足堆定义的结点。
  3. 堆的存储:双亲数组表示法,索引从0开始。结点i的父结点为(i-1)/2;左孩子为2*i+1; 结点右孩子为2*i+2;
  4. 堆的删除:每次只能删除根节点。首先最后一个元素和根节点交换,删除根节点,重新按照第一步调整堆结构。

基本算法(小顶堆):

#include <iostream>
#include <algorithm>

using namespace std;

//依次插入元素,建立堆,从下到上进行堆调整
void minHeapFixUp(int * a, int i)
{
    int insertValue = a[i];
    int parentIndex = (i-1)/2;

    while(parentIndex >= 0 && i != 0)
    {
        if(a[parentIndex] <= insertValue)
            break;

        a[i] = a[parentIndex];
        i = parentIndex;
        parentIndex = (i-1)/2;
    }

    a[i] = insertValue;
}

//从某个结点向下调整堆
void minHeapFixDown(int * a, int i, int n)
{
    int rootValue = a[i];
    int childIndex = 2*i + 1;

    while(childIndex < n)
    {
        if(childIndex + 1 < n && a[childIndex] > a[childIndex+1])
            childIndex++;

        if(rootValue <= a[childIndex])
            break;

        a[i] = a[childIndex];
        i = childIndex;
        childIndex = 2*i + 1;
    }

    a[i] = rootValue;
}

//删除元素
void minHeapDelete(int * a, int n)
{
    swap(a[0], a[n-1]);
    
    minHeapFixDown(a, 0, n-1);
}

int main()
{
    int a[9] = {7,1,2,3,6,5,8,9,4};
    int n = sizeof(a)/sizeof(int);

    //依次插入元素,建立堆
    //for(int i = 0; i < 9; ++i)
        //minHeapFixUp(a, i);

    //堆化数组
    for(int i = (n-1)/2; i >= 0; --i)
    {
        minHeapFixDown(a, i, n);
    }
    
    for(int i = 0; i < n; ++i)
        cout << a[i] << " ";
    cout << endl;

    //堆排序
    for(int i = n-1; i >= 1; --i)
    {
        swap(a[0], a[i]);
        minHeapFixDown(a, 0, i);
    }

    for(int i = 0; i < n; ++i)
        cout << a[i] << " ";
    cout << endl;

    return 0;
}
二、示例

题意:寻找最小的k个数
算法思想:由于是找出最小的k个数,k个数不必有序,剩下的n-k个数也不必有序,因此:

  1. 使用容量为k的最大堆存储最先遍历到的k个元素
  2. 依次遍历剩下的n-k个元素,与大顶堆的根结点进行比较,如果a[i] < a[0],替换,否则,忽略

建堆的时间复杂度O(k),更新堆的复杂度是O((n-k)logk),因此最坏情况下的时间复杂度是O(nlogk)

#include <iostream>
#include <algorithm>

using namespace std;

void minHeapFixUp(int * a, int i)
{
    int insertValue = a[i];
    int parentIndex = (i-1)/2;

    while(parentIndex >= 0 && i != 0)
    {
        if(a[parentIndex] >= insertValue)
            break;

        a[i] = a[parentIndex];
        i = parentIndex;
        parentIndex = (i-1)/2;
    }

    a[i] = insertValue;
}

//从某个结点向下调整堆
void minHeapFixDown(int * a, int i, int n)
{
    int rootValue = a[i];
    int childIndex = 2*i + 1;

    while(childIndex < n)
    {
        if(childIndex + 1 < n && a[childIndex] < a[childIndex+1])
            childIndex++;

        if(rootValue >= a[childIndex])
            break;

        a[i] = a[childIndex];
        i = childIndex;
        childIndex = 2*i + 1;
    }

    a[i] = rootValue;
}

void getKs(int * a, int k, int n)
{
    for(int i = 0; i < k; ++i)
        minHeapFixUp(a, i);

    for(int i = k; i < n; ++i)
    {
        if(a[i] < a[0])
        {
            a[0] = a[i];
            minHeapFixDown(a, 0, k);
        }
    }

    for(int i = 0; i < k; ++i)
        cout << a[i] << " ";
    cout << endl;
}

int main()
{
    int a[9] = {7,1,2,3,6,5,8,9,4};
    int n = sizeof(a)/sizeof(int);

    //找出最小的k个元素
    getKs(a, 3, n);

    return 0;
}
三、使用STL模板堆算法

主要函数:

  1. make_heap(first_pointer, end_pointer, compare_function) 没有比较函数时,默认为大顶堆,函数功能:将一段的数组或向量初始化堆结构
  2. push_heap(first_pointer, end_pointer, compare_function) 在[first, last-1]是堆结构的前提下,将之后的元素加入堆
  3. pop_heap(first_pointer, end_pointer, compare_function) 将first和last元素交换,调整[first, last-1]为堆结构
  4. sort_heap(first_pointer, end_pointer, compare_function) 将符合堆结构的一段数组或向量排序(前提必须是堆)
  5. is_heap(first_pointer, end_pointer, compare_function) 判断一段数组或向量是否是堆,是返回1
#include <iostream>
#include <algorithm>

using namespace std;

void print(int * a, int n)
{
    for(int i = 0; i < n; ++i)
        cout << *(a+i) << " ";
    cout << endl;
}

bool cmp(int a, int b)
{
    return a > b;
}

int main()
{
    int a[20] = {2,1,3,4,5,6,9,8,7,0};

    //默认为大顶堆
    make_heap(a, a+10, cmp);
    print(a, 10);

    //push_heap并不是添加一个元素,而是,假设[first, last-1]已经是一个堆,再把堆中的新元素加进来,组成一个新的堆
    a[10] = 11; a[11] = 12; a[12] = -1;
    push_heap(a, a+13, cmp);
    print(a, 13);

    //pop_heap并不是弹出一个元素,而是,将first与last元素交换,然后将[first, last-1]组成一个新的堆
    pop_heap(a, a+11, cmp);
    print(a, 11);

    //sort_heap对一个堆进行排序,前提必须是堆
    sort_heap(a, a+10, cmp);
    print(a, 10);

    return 0;
}

相关文章

  • 5. 堆和堆排序

    一、基本知识 初始化堆:按照完全二叉树的结构,从上到下、从左到右依次放入元素,然后可以认为所有的叶子结点已经符合大...

  • 堆 - 堆和堆排序

    什么是堆 堆是一种特殊的树,它有两个特点: 堆是一个完全二叉树 堆中每个节点的值都必须大于等于(或小于等于)其子树...

  • 堆和堆排序

    堆的简介 堆排序是一种复杂度为Nlog(N)的排序算法。介绍堆排序之前先讲一讲什么是堆。这里介绍的是数据结构中的二...

  • 堆和堆排序

    堆: 堆是具有下列性质的完全二叉树:每个节点的值都大于或等于左右孩子节点的值,称为大顶堆;每个节点的值都小于或等于...

  • 堆和堆排序

    姓名:王怀帅 学号:16040410035 转载自:http://www.jianshu.com/p/86428c...

  • 堆和堆排序

    优先队列 优先队列是什么:与常见的队列不同的是,优先队列并不遵循“先进先出”的原则,反而是根据优先级来确定是否先出...

  • 堆和堆排序

    1. 堆的概念 堆是一种特殊的树,一个堆需要满足如下两个条件: 一个堆是一个完全二叉树; 堆中每个节点的值都必须大...

  • 堆和堆排序

    堆: 1,堆是一个完全二叉树;完全二叉树要求除了最后一层,其他层的节点都是满的,最后一层的节点都靠左排列。2,堆中...

  • 堆和堆排序

    一、堆的定义 (1)堆树是一颗完全二叉树; (2)堆树中某个节点的值总是不大于或不小于其孩子节点的值; (3)堆树...

  • 堆和堆排序

    什么是堆? 如何存储一个堆(如何实现一个堆?) 堆的插入、删除操作 如何基于堆实现排序?(建堆和排序) 为什么快速...

网友评论

      本文标题:5. 堆和堆排序

      本文链接:https://www.haomeiwen.com/subject/benikttx.html