美文网首页
堆 | 定义、操作、堆排序

堆 | 定义、操作、堆排序

作者: zilla | 来源:发表于2019-08-03 17:07 被阅读0次

参考:胡凡,曾磊《算法笔记》

定义

堆是一棵完全二叉树

  • 完全二叉树:结点数量n,叶子结点数ceiling(n/2)

树中每个结点的值都不小于(或不大于)其左右孩子结点的值。

  • 若所有结点值都有父>=子,称为 大顶堆。这时每个结点都是以它本身为根的树的最大结点。
  • 若所有结点值都有父<=子,称为 小顶堆。这时每个结点都是以它本身为根的树的最小结点。

应用

  • 优先队列的实现
    默认使用 大顶堆

操作 & 实现

堆是一棵完全二叉树,用数组可实现。(下标【1,n】)

给定初始序列建堆 O(n)
  • 将初始序列按照层序排成一棵完全二叉树。
  • 从下往上,从右往左,找到第一个非叶子结点自上而下调整,范围是下标【1,floor(n/2)】。追着这一个结点调整,每次调整后再检查他和孩子结点的大小是否还需调整,直到不需调整/没有孩子结点。
  • 然后找下一个非叶子结点,重复第二步……
  • 一直到完成根结点的调整。

⚠️倒着枚举、调整:保证每个结点都是以其为根结点的子树的权值最大结点。

删除堆顶元素 O(log n)

用最后一个结点覆盖堆顶元素,结点数减1,自上而下调整根结点。

添加一个元素x O(log n)

结点数加1,把x放在数组最后,从x自下而上调整。

  • 向上调整的写法
    upAdjust(int low = 1, int high)
    low为1(堆顶下标),high是x下标。
    追着x,始终与父亲结点比较,若x比父亲结点大,则交换。
    直到到达堆顶父亲结点权值较大

堆排序

  • 首先建堆,堆顶为最大元素。
  • 倒着遍历数组,直到堆中剩下一个元素。
    当前遍历到下标i,交换堆顶元素与元素i,对堆顶作向下调整,范围【1,i - 1】,堆结点数减1
    相当于不断去掉当前最大值—堆顶元素。

⚠️我实现时踩的坑

  • ⚠️downAdjust函数的参数应该是当前调整的下标起点low和下标终点high
  • downAdjust函数中lc,rc都要及时更新!!!(只更新lc什么鬼啊,给自己跪了orzzzzzzzzz
  • 建堆时调用向下调整,第二个参数始终是堆的结点数。但堆排序时,堆的结点数每次迭代减1。

1098 Insertion or Heap Sort (25 分)

判断排序的中间结果是使用插入排序还是堆排序得到的,并输出下一次迭代后的序列。
本题使用了最大堆。

#include <cstdio>
#include <algorithm>

using namespace std;
int origin[105], partial_sorted[105], temp_seq[105];
int nn;

bool isSame(const int a[], const int b[]) {
    for (int i = 1; i <= nn; ++i)
        if (a[i] != b[i]) return false;
    return true;
}

void downAdjust(int curr, int high) {
    int lc = 2 * curr, rc = 2 * curr + 1, larger_index;
    while (lc <= high) {
        if (rc <= high && origin[rc] > origin[lc]) { // has rc
            larger_index = rc;
        } else larger_index = lc;
        if (origin[larger_index] > origin[curr]) { //存在比自身大的孩子
            swap(origin[larger_index], origin[curr]);
            curr = larger_index;
            lc = 2 * curr;
            rc = lc + 1; // update lc,rc
        } else break; //curr就是以自己为根子树的最大结点
    }
}

void buildHeap() {
    for (int i = nn / 2; i >= 1; --i) {
        downAdjust(i, nn);
    }
}

int main() {
    scanf("%d", &nn);
    for (int i = 1; i <= nn; ++i) {
        scanf("%d", &origin[i]);
        temp_seq[i] = origin[i];
    }
    for (int i = 1; i <= nn; ++i) {
        scanf("%d", &partial_sorted[i]);
    }
    // InsertionSort or N
    for (int i = 2; i < nn; ++i) {
        sort(temp_seq + 1, temp_seq + i + 1);
        if (isSame(temp_seq, partial_sorted)) {
            puts("Insertion Sort");
            sort(temp_seq + 1, temp_seq + i + 2);
            for (int j = 1; j < nn; ++j)
                printf("%d ", temp_seq[j]);
            printf("%d\n", temp_seq[nn]);
            return 0;
        }
    }
    buildHeap();
    for (int i = nn; i >= 3; --i) {
        swap(origin[1], origin[i]);
        downAdjust(1, i - 1);
        if (isSame(origin, partial_sorted)) {
            puts("Heap Sort");
            swap(origin[1], origin[i - 1]);
            downAdjust(1, i - 2);
            for (int j = 1; j < nn; ++j)
                printf("%d ", origin[j]);
            printf("%d\n", origin[nn]);
            return 0;
        }
    }
    return 0;
}

相关文章

  • 堆 | 定义、操作、堆排序

    参考:胡凡,曾磊《算法笔记》 定义 堆是一棵完全二叉树。 完全二叉树:结点数量n,叶子结点数ceiling(n/2...

  • 堆的基本操作与堆排序(C/C++实现)

    原理参考:堆和堆排序原理介绍 堆的基本操作(以最小堆为例) 基本数组的定义 向下调整操作 向下调整操作一般是针对一...

  • 堆排序原理以及代码解析

    前言 堆排序是排序算法的一种,相对来说较难理解,本文分析了堆排序的原理,并且剖析了源码。 堆定义 堆是具有以下性质...

  • 堆排序

    前言 本文主要介绍堆的构造, 元素在堆中的上浮操作以及下沉操作,最后讲述基于这些操作的堆排序, 不对优先队列作介绍...

  • 堆排序

    一、定义 堆排序(Heap Sort)是一种基于优先队列(堆实现)的排序方式。堆排序的步骤如下: 初始时待排序元素...

  • 排序

    一、选择排序 1.堆排序 定义:堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序可参考http...

  • 树结构入门教程-赫夫曼树

    前面我们学习了堆排序,关于堆排序就两点需要我们清楚,ruguoxuqiu如果是升序操作,我们需要构建大顶堆,如果是...

  • 堆排序(dart实现)

    堆排序 [toc] 堆排序是选择排序的一种优化 1.执行流程 对序列进行原地建堆 重复执行下面的操作,直到对的元素...

  • 堆的相关操作与堆排序

    堆中插入、删除元素 先看图解: 堆中插入元素 插入元素总是插入在数组中的最后一个位置,然后从其父节点依次向上调整堆...

  • 堆排序的实现

    用大顶堆实现堆排序

网友评论

      本文标题:堆 | 定义、操作、堆排序

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