当前位置: 首页 > article >正文

堆排序的思路与常见的问题

堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点.

先上代码 

public class HeapSort {

    void  heapSort(int A[])
    {
        if (A == null || A.length <= 1) {
            return;
        }
        int n = A.length;
        // 1. 构建最大堆(从最后一个非叶子节点开始)
        for (int i = (n - 1) / 2; i >= 0; i--) {
            heapify(A, n, i);
        }

        // 2. 进行堆排序
        for (int i = n - 1; i >= 0; i--) {
            swap(A, 0, i);  // 交换堆顶和最后一个元素
            heapify(A, i, 0);  // 重新调整堆
        }
    }
    // 调整堆(维护堆的性质)
    private void heapify(int[] A, int heapSize, int parentIndex) {
        int largest = parentIndex;
        int leftChild = 2 * parentIndex + 1;
        int rightChild = 2 * parentIndex + 2;

        // 检查左子节点
        if (leftChild < heapSize && A[leftChild] > A[largest]) {
            largest = leftChild;
        }

        // 检查右子节点
        if (rightChild < heapSize && A[rightChild] > A[largest]) {
            largest = rightChild;
        }

        // 如果最大值发生变化,交换并递归调整
        if (largest != parentIndex) {
            swap(A, parentIndex, largest);
            heapify(A, heapSize, largest);
        }
    }

    // 交换两个数组元素
    private void swap(int[] A, int i, int j) {
        int temp = A[i];
        A[i] = A[j];
        A[j] = temp;
    }
}

以数组A = {4, 6, 8, 5, 9}为例 

在其调整之后 

        9
       /   \
      6     8   调整后变为                           
     / \
    5   4                          

         4
       /   \
      6     8
     / 
    5   9 (已排序) 那么首先第一个问题便是

为什么不能直接使用 heapAdjust(A, n, 0); 来构建大顶堆,而是要用 for(int i = (n-1)/2; i >= 0; i--) heapAdjust(A, n, i);

只对 heapAdjust(A, n, 0); 进行一次调用,它仅调整 A[0] 这一棵子树,但不会处理整个数组形成的堆结构,导致并不是所有子树都符合最大堆的定义

构建大顶堆需要同时满足左子树和右子树小于堆顶的元素 若从A[0]开始进行遍历 并且开始递归 只能保证一侧的子树满足要求 而不能保证左子树和右子树同时满足 因为堆排序是类似于完全二叉树的结构 所以需要进行从最后一个非叶子节点 i = (n-1)/2 开始,依次向上调整堆,保证每个子树都符合最大堆的定义,最终整个数组成为一个合法的最大堆.

那么又会有第二个问题

进行堆调整 for(int j=n-1;j>=0;j--)

{ swap(A,j,0);

heapAjust(A,j,0);

}堆调整只需要从0开始呢而不是从 j/2 开始呢

删除堆顶(交换 A[0] 和 A[j]) 之后,A[0] 可能不再是最大值,需要调整。 但 堆的其他部分(A[1] ~ A[j-1])仍然是最大堆,只有 A[0] 可能破坏堆结构。 只需要让 A[0] 向下调整,恢复最大堆。

只需要 heapAdjust(A, j, 0); 只让 A[0] 下沉,保持 A[0] ~ A[j-1] 的最大堆性质。

这比 for (int i = j/2; i >= 0; i--) 更高效,因为只用调整 A[0],不需要重新遍历整个堆 那也就是说 如果交互从i=j/2也没有错 只不过是多加计算了 没有任何意义 没有调整的一侧根本不需要改变 因为在构建堆的时候已经比较过了 即调整的意义在于不会遍历整个堆 只会让堆的一侧进行运算 因为只修复 A[0],不影响其他节点 所以不可能访问所有的非叶子节点  即最优解是最优解是 heapAdjust(A, j, 0);


http://www.kler.cn/a/593198.html

相关文章:

  • 今日bug
  • P1118 [USACO06FEB] Backward Digit Sums G/S
  • Tailwind CSS 学习笔记(二)
  • IDEA的常用设置与工具集成
  • 高性能Java并发编程:线程池与异步编程最佳实践
  • 批处理脚本编译vs工程
  • RK3568平台设备树文件功能解析(鸿蒙系统篇)
  • 2025年PHP微服务框架推荐及对比
  • 深度学习框架PyTorch——从入门到精通(1)下载与安装
  • 卷积神经网络(CNN)与反向传播
  • 关于redis中的分布式锁
  • 青少年编程与数学 02-011 MySQL数据库应用 05课题、结构化查询语言SQL
  • gem rbenv介绍【前端扫盲】
  • k8s中的组件
  • Scala 文件 I/O
  • 在react当中利用IntersectionObserve实现下拉加载数据
  • 云原生无服务器计算:事件驱动的原子化运算革命
  • 12 File文件对象:创建、获取基本信息、遍历文件夹、查找文件;字符集的编解码 (黑马Java视频笔记)
  • Git 常用命令完全指南:从入门到高效协作
  • 基于x11vnc的ubuntu远程桌面