首页 > 编程语言 >基础算法-堆排序

基础算法-堆排序

时间:2023-04-21 20:13:31浏览次数:23  
标签:arr 位置 parent int 堆排序 元素 基础 算法 节点

思路

堆是一种完全二叉树,其中每个节点的值都大于或等于其子节点的值,被称为“大根堆”;或者每个节点的值都小于或等于其子节点的值,被称为“小根堆”。在堆排序中,我们使用的是大根堆,即根节点的值是最大的元素。

堆排序的基本思路是:

  1. 建立一个大根堆。将待排序的序列构建成一个大根堆,即所有非叶子节点的值都大于或等于其子节点的值。
  2. 交换堆顶元素和末尾元素。将堆顶元素与末尾元素进行交换,将最大值放在末尾位置。
  3. 调整堆。将剩余的元素重新构建成一个大根堆,重复执行步骤2和步骤3,直到整个序列有序。

具体地,我们可以将序列看成一个完全二叉树,将其转化为一个数组来处理。建立大根堆的过程可以使用“向下调整”来实现,具体步骤如下:

  1. 从最后一个非叶子节点开始,对每个节点进行***向下调整 * 操作。具体地,将当前节点的值与其左右子节点的值进行比较,如果当前节点的值小于左右子节点的值中的较大值,就将当前节点的值与左右子节点中的较大值进行交换,然后将指针移动到左右子节点中的较大值所在的位置,继续向下调整

    当我们将数组构建成一个大根堆之后,堆的根节点(也就是数组的第一个元素)一定是堆中的最大值。因此,我们将堆顶元素(即根节点)与堆中最后一个元素进行交换,这样最大值就被放在了数组的末尾位置。接着,我们将堆的范围缩小一位,即将堆的范围设为除去已经排好序的最后一个元素以外的其余元素,再重新调整堆,使得堆的根节点再次成为当前堆中的最大值。如此反复,直到堆的范围缩小到只剩一个元素,整个数组就被排好序了

  2. 重复执行步骤1,直到所有的非叶子节点都被调整过一遍。

建立好大根堆后,我们就可以进行堆排序了。具体步骤如下:

  1. 将堆顶元素与末尾元素进行交换,将最大值放在末尾位置。

    当堆是一个完全二叉树时,可以通过以下方式计算一个节点的父节点和左右子节点的位置:

    1. 父节点的位置:假设当前节点的位置是 i,那么它的父节点的位置为 (i-1)/2
    2. 左子节点的位置:假设当前节点的位置是 i,那么它的左子节点的位置为 2i+1
    3. 右子节点的位置:假设当前节点的位置是 i,那么它的右子节点的位置为 2i+2

    需要注意的是,这种计算方式只适用于堆是一个完全二叉树的情况,如果堆不是一个完全二叉树,那么就需要使用其他的方式计算节点的位置。

  2. 缩小堆的范围,将剩余的元素重新构建成一个大根堆。

  3. 重复执行步骤1和步骤2,直到整个序列有序。

代码

,public static void heapSort(int[] arr) {
    if (arr == null || arr.length <= 1) {
        return;
    }
    // 1. 建立大根堆
    buildHeap(arr);
    // 2. 交换堆顶元素和末尾元素,重建堆
    int end = arr.length - 1;
    while (end > 0) {
        swap(arr, 0, end);
        end--;
        adjustHeap(arr, 0, end);
    }
}

// 建立大根堆
private static void buildHeap(int[] arr) {
    int parent = (arr.length - 2) / 2; // 最后一个非叶子节点的位置,即最后一个节点的父节点
    for (int i = parent; i >= 0; i--) {
        adjustHeap(arr, i, arr.length - 1);
    }
}

// 调整堆
private static void adjustHeap(int[] arr, int parent, int end) {
    int child = 2 * parent + 1; // 左子节点的位置
    int temp = arr[parent]; // 当前节点的值
    while (child <= end) {
        // 选择左右子节点中的较大值
        if (child + 1 <= end && arr[child] < arr[child + 1]) {
            child++;
        }
        // 如果当前节点的值小于左右子节点的较大值,则进行交换
        if (temp < arr[child]) {
            arr[parent] = arr[child];
            parent = child;
            child = 2 * parent + 1;
        } else {
            break;
        }
    }
    arr[parent] = temp;
}

// 交换数组中的两个元素
private static void swap(int[] arr, int i, int j) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
  1. 为什么adjustHeap()函数最后需要调用arr[parent] = temp
  • adjustHeap() 函数中,我们将堆顶元素和堆的最后一个元素进行交换,然后调整堆使其重新成为大根堆。在调整堆的过程中,我们将堆顶元素向下移动,将其与其左右子节点中较大的那个节点进行比较,如果比该节点小,就将该节点上移,直到不再小于其子节点为止。
  • 当我们找到了堆顶元素的合适位置之后,我们需要将其放到该位置上。但是此时,堆顶元素的值已经被赋给了某个子节点,原本存储堆顶元素的位置已经被占据了。为了将堆顶元素放到正确的位置上,我们需要将其存储到堆顶元素原本所在的位置上。
  • 具体而言,arr[parent] = temp; 这句话的作用是将堆顶元素(即原本存储在 parent 位置上的元素)的值替换成 temp,这样就可以将堆顶元素放到正确的位置上了。
  1. 为什么arr[parent] = temp被放在了while循环外面呢
  • arr[parent] = temp; 这段代码放在 while 循环内部是没有问题的,因为它只是将堆顶元素存储到正确的位置上,不会对堆的调整造成影响。但是,将其放在循环外面可以更清晰地表达代码的意图,也可以避免重复的赋值操作。
  • 具体而言,当 while 循环结束时,我们已经找到了堆顶元素的正确位置,此时的 parent 指向的就是该位置。因此,在循环外面执行 arr[parent] = temp; 语句,将堆顶元素存储到 parent 位置上,就可以完成堆的调整了。
  • 如果将其放在循环内部,那么每次循环都要执行一次 arr[parent] = temp; 语句,这样就会造成不必要的赋值操作。因此,将其放在循环外面可以避免这个问题,同时也更清晰地表达代码的意图。

堆排序扩展题目

已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离可以不超过k,并且k相对于数组来说比较小。请选择一个合适的排序算法针对这个数据进行排序。

思路

java中的PriorityQueue用的小根堆

import java.util.PriorityQueue;

public class SortAlmostSortedArray {

    public static void sort(int[] arr, int k) {
        // 创建一个小根堆,存放当前窗口内的元素
        PriorityQueue<Integer> minHeap = new PriorityQueue<>();

        // 遍历数组,将元素加入小根堆中,如果堆中的元素数量超过了 k,就将堆顶元素弹出
        for (int i = 0; i < arr.length; i++) {
            // 将当前元素加入小根堆
            minHeap.offer(arr[i]);

            // 如果堆中的元素数量超过了 k,就将堆顶元素弹出并放入结果数组中
            if (minHeap.size() > k) {
                arr[i - k] = minHeap.poll();
            }
        }

        // 将剩余的元素按顺序依次放入结果数组中
        while (!minHeap.isEmpty()) {
            arr[arr.length - minHeap.size()] = minHeap.poll();
        }
    }

    public static void main(String[] args) {
        int[] arr = {6, 5, 3, 2, 8, 10, 9};
        int k = 3;
        System.out.println("Before sorting -> "+Arrays.toString(arr));
        sort(arr, k);
        System.out.println("After sorting -> "+Arrays.toString(arr));
    }

}

标签:arr,位置,parent,int,堆排序,元素,基础,算法,节点
From: https://www.cnblogs.com/maoqifansBlog/p/17341625.html

相关文章

  • 基本算法-基数排序
    思想当我们需要对一组数据进行排序时,常规的排序算法(如快速排序、归并排序等)通常是比较排序,即通过比较元素之间的大小关系来进行排序。但有时候我们需要对一组数据按照它们的“数字位”进行排序,此时比较排序并不是最优的选择,这时候基数排序就显得非常有效了。基数排序是一种非比......
  • 【逆向】x64程序逆向基础——调用约定和栈使用
    【逆向】x64程序逆向基础 主要区别1.所有地址指针都是64位。2.增加和扩展新的寄存器,并兼容原32位版本的通用寄存器。3.原指令指针寄存器EIP扩展为RIP。寄存器1.64位寄存器兼容原32位寄存器。2.新增加8个XMM寄存器(XMM8-XMM15)。3.扩展原32位寄存器的64位版本,并......
  • 基于RL(Q-Learning)的迷宫寻路算法
    强化学习是一种机器学习方法,旨在通过智能体在与环境交互的过程中不断优化其行动策略来实现特定目标。与其他机器学习方法不同,强化学习涉及到智能体对环境的观测、选择行动并接收奖励或惩罚。因此,强化学习适用于那些需要自主决策的复杂问题,比如游戏、机器人控制、自动驾驶等。强化......
  • Redis 为何使用Nearly LRU 算法淘汰数据
    Redis使用该LRU算法淘汰过期数据吗?不是的。由于LRU算法需要用链表管理所有的数据,会造成大量额外的空间消耗。大量的节点被访问就会带来频繁的链表节点移动操作,从而降低了Redis性能。Redis的内存空间是很宝贵的,而维护LRU的双向链表需要使用比较多的额外空间,至少需要一......
  • k3s 基础 —— 配置 traefik ingress 跨命名空间访问
    新增配置文件/var/lib/rancher/k3s/server/manifests/traefik-config.yaml参考apiVersion:helm.cattle.io/v1kind:HelmChartConfigmetadata:name:traefiknamespace:kube-systemspec:valuesContent:|-globalArguments:-"--providers.kubernetescrd......
  • JVM垃圾回收机制之对象回收算法
    在前面的文章中,介绍了JVM内存模型分为:堆区、虚拟机栈、方法区、本地方法区和程序计数器,其中堆区是JVM中最大的一块内存区域,在Java中的所有对象实例都保存在此区域,它能被所有线程共享。在Java中还有一个重要的机制:GC(垃圾收集器),堆是GC管理的主要区域,本文会带大家了解GC机制。GC......
  • CSS 基础拾遗(核心知识、常见需求)
    本篇文章围绕了CSS的核心知识点和项目中常见的需求来展开。虽然行文偏长,但较基础,适合初级中级前端阅读,阅读的时候请适当跳过已经掌握的部分。这篇文章断断续续写了比较久,也参考了许多优秀的文章,但或许文章里还是存在不好或不对的地方,请多多指教,可以评论里直接提出来哈。核心......
  • Python用哈希算法查找相似图片(包括不同分辨率,不同大小,不同格式的图片)
    #-*-coding:utf-8-*-'''Python用哈希算法查找相似图片并放入[_df]的文件夹中相似图片包括不同分辨率,不同大小,不同格式,只要图片相似就会算重复文件安装cv2pipinstallopencv-python'''importosimportcv2importnumpyasnpimportshutilimportrandomclas......
  • Java基础语法(一):Java程序的结构
    前言Java是一种流行的面向对象编程语言,可以用于开发各种应用程序,从桌面应用程序到企业级Web应用程序和移动应用程序。编写Java程序时,良好的程序结构是至关重要的,因为它可以帮助程序员更好地组织代码并使其易于维护和扩展。本文将介绍Java程序的结构,包括程序组成部分、代码结构和组......
  • 若依RuoYi框架浅析 基础篇①——日志logs本地保存
    文章目录日志保存位置在/home/ruoyi/logs/[root@iZ2ze30dygwd6yh7gu6lskZlogs]#cd/home/ruoyi/logs/[root@iZ2ze30dygwd6yh7gu6lskZlogs]#lssys-error.2021-02-28.logsys-error.logsys-info.2021-02-28.logsys-info.2021-03-01.logsys-info.logsys-user.2021-0......