• 二叉树(堆)


    堆的性质:

    堆中某个节点的值总是不大于或不小于其父节点的值;
    堆总是一棵完全二叉树。
    大堆:任何父亲≥孩子
    小堆:任何父亲≤孩子

    接下来,我们要做的便是对堆进行增加和删除:

    首先是增加操作,我们这里采用向上调整的方式来进行增加:

    1. void AdjustUP(HPDataType* a, int child)
    2. {
    3. int parent = (child - 1) / 2;
    4. while (child > 0)
    5. {
    6. if (a[child] < a[parent])
    7. {
    8. Swap(&a[child], &a[parent]);
    9. child = parent;
    10. parent = (parent - 1) / 2;
    11. }
    12. else
    13. {
    14. break;
    15. }
    16. }
    17. }

    时间复杂度:O(logN)

    比如我们有这样一组数据:

    int a[] = { 65,100,70,32,50,60 };

    然后对其进行操作如下;

    1. void HeapPush(HP* php, HPDataType x)
    2. {
    3. assert(php);
    4. //扩容
    5. if (php->size == php->capacity)
    6. {
    7. int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
    8. HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * newCapacity);
    9. if (tmp == NULL)
    10. {
    11. perror("realloc fail");
    12. exit(-1);
    13. }
    14. php->a = tmp;
    15. php->capacity = newCapacity;
    16. }
    17. php->a[php->size] = x;
    18. php->size++;
    19. AdjustUP(php->a, php->size - 1);
    20. }

    运行结果为:

    结果:

    嘿嘿,怎么着?这不就是小堆嘛?

    那么呢,接下来我们便进行数据的删除操作

    我们先来考虑这样一个问题:删除哪个数据最有价值呢?

    显然是删除根because挪动覆盖第一个位置根,关系全乱了,剩下的值,不一定是堆

    你看,你看,这不是没乱吗?

    雷布斯:这绝对是来捣乱的(这种情况呢,显然只是一个巧合)

    不信,我们再来看一个:

    你看全乱了,所以这种方式好不好呢?

    那不好怎么办呢?别慌,让我们娓娓道来:

    我们不影响其他位置,加上尾插和尾删的效率很好,所以只把要删除的元素和最后一个元素换换位置,你看:

    向下调整: 

    1. void AdjustDown(HPDataType* a, int n, int parent)
    2. {
    3. int child = parent * 2 + 1;
    4. while (child < n)
    5. {
    6. //找出小的那个孩子
    7. if (child+1 < n && a[child + 1] < a[child])
    8. {
    9. ++child;
    10. }
    11. if (a[child] < a[parent])
    12. {
    13. Swap(&a[child], &a[parent]);
    14. //继续向下调整
    15. parent = child;
    16. child = parent * 2 + 1;
    17. }
    18. else
    19. {
    20. break;
    21. }
    22. }
    23. }

    时间复杂度:O(logN)

     其次,我们仔细看,经过这一系列操作,我们是不是还把这一组数据中的次小元素给找了出来?

    那我们再继续pop,是不是又找到了第三小?依次反复……是不是就成为了排序

    我们来操作一下,对某个数组进行排序。

    如果要求的是升序,应该选择 大堆 还是 小堆 呢?

    升序:建大堆

    堆顶跟最后一个交换        最大的数据排好了        剩下数据向下调整,选出次大的,代价是logN

    合计是:N*logN

    1. //升序
    2. void HeapSort(int* a, int n)
    3. {
    4. //建堆 (大堆)or (小堆)
    5. for (int i = 1; i < n; i++)
    6. {
    7. AdjustUP(a, i);
    8. }
    9. int end = n - 1;
    10. while (end > 0)
    11. {
    12. Swap(&a[0], &a[end]);
    13. AdjustDown(a, end, 0);
    14. --end;
    15. }
    16. }

  • 相关阅读:
    Docker容器:网络模式与资源控制
    瑞吉外卖项目:移动端导入用户地址簿与菜品展示功能实现
    spring cache 的常规使用
    7z命令行
    奶茶店冬天怎么提升销量 | 奶茶技术培训
    Java SE - 简单介绍-基本数据类型
    day16学习总结
    [Python] Python编程技巧总结[不断更新....]
    多线程 | 《Java 并发编程艺术》的学习
    CubeMX+BabyOS 使用方法
  • 原文地址:https://blog.csdn.net/m0_58232983/article/details/132893924