3.堆排序和比较器

news2024/9/29 5:35:59

1. 堆

堆结构就是用数组实现的完全二叉树结构,对于结点i,左孩子2*i+1、右孩子2*i+2、父节点(i-1)/ 2

  1. 完全二叉树中如果每棵子树的最大值都在顶部就是大根堆
    在这里插入图片描述
  2. 完全二叉树中如果每棵子树的最小值都在顶部就是小根堆
  3. 堆结构的heapInsert与heapify操作
  4. 堆结构的增大和减少
  5. 优先级队列结构,就是堆结构

1.1 堆的构建(大顶堆)

方法:
先给出一个序列:[4,6,5,8,9]
图解流程:
在这里插入图片描述在这里插入图片描述

这个过程我们也叫做堆结构的heapInsert(一个新结点加入到堆中依次向上比对的过程)看具体的代码:

for(int i = 0; i < arr.length; i++) {
	heapInsert(arr, i);
}

public static void heapInsert(int[] arr, int index) {
	//当前子节点和父节点值比较,满足条件进行交换
	while(arr[index] > arr[(index - 1) / 2]) {
		//这里swap就是一个交换函数
		swap(arr, index, (index - 1) / 2);
		index = (index - 1) / 2;
	}
}

堆结构的heapify的过程:
当我们形成一个大顶堆时,当堆中一个数据突然发生变化(变小)时,会有可能使得大顶堆不存在,得做下沉操作,heapify的过程就是使得该完全二叉树重新变成大顶堆。
在这里插入图片描述
就是将当前的结点与其左右子节点进行比较,大的进行交换

public static void heapify(int[] arr, int index, int size) {
        //左子节点
        int left = index * 2 + 1;
        while (left < size) {
            //两个孩子中,谁的值大,把下标给largest
            int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
            //父和孩子之间,谁的值大,把小标给largest
            largest = arr[largest] > arr[index] ? largest : index;
            if (largest == index) {
                break;
            }
            //将两个数进行交换
            swap(arr, largest, index);
            index = largest;
            left = index * 2 + 1;
        }
    }

1.2 堆排序

第一步:这就是堆排序的主要代码,先将无序的序列通过heapInsert的过程变成大顶堆;
第二步:把最后一个数与堆顶的数进行交换,size-1;
第三步:通过heapify的过程重新构建大根堆;
接下来的过程就是最后一个数与堆顶的数进行比较,size-1,再次重新构成大根堆,以此类推最终数组的顺序就是从小 到大的一个排序。
所有代码如下:

public class HeapSortV2 {
    public static void main(String[] args) {
        int[] arr = {10,9,40,30,28,-5,29};
        System.out.println("排序前:" + Arrays.toString(arr));
        heapSort(arr);
        System.out.println("排序后:" + Arrays.toString(arr));


    }

    public static void heapSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        //给定一个数组,我们先要实现的就是根据数组形成大根堆,我们用heapInsert
        for (int i = 0; i < arr.length; i++) {
            heapInsert(arr, i);
        }

        //我们是如何根据大根堆来完成排序的呢,我们分析大根堆的根节点是当前数组中的最大的值
        //我们将根节点的数字与叶子节点的最后一个值进行交换,并将size-1
        int size = arr.length;
        swap(arr, 0, --size);
        while (size > 0) {
            heapify(arr, 0, size);
            swap(arr, 0, --size);
        }
    }

    public static void heapInsert(int[] arr, int index) {
        //当前子节点和父节点值比较,满足条件进行交换
        while (arr[index] > arr[(index - 1) / 2]) {
            swap(arr, index, (index - 1) / 2);
            index = (index - 1) / 2;
        }
    }

    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public static void heapify(int[] arr, int index, int size) {
        //左子节点
        int left = index * 2 + 1;
        while (left < size) {
            //两个孩子中,谁的值大,把下标给largest
            int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
            //父和孩子之间,谁的值大,把小标给largest
            largest = arr[largest] > arr[index] ? largest : index;
            if (largest == index) {
                break;
            }
            //将两个数进行交换
            swap(arr, largest, index);
            index = largest;
            left = index * 2 + 1;
        }
    }
}

运行结果:
在这里插入图片描述

1.3 题目练习

题目描述:
已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离可以不超过k,并且k相对于数组来说比较小。请选择一个合适的排序算法针对这个数据进行排序。
题解
由于数组是几乎有序的,假如在第k+1个数之后存在最小值,那么元素移动到第一个位置需要移动超过k次,不符合,所以最小值必在前k+1个数里。

可以选择k+1大小的小根堆进行排序,先扫描数组的前k+1个数构建小根堆。然后取出堆顶元素放在数组的第1个位置,并把数组的第k+2个数插入小根堆。再取出堆顶元素放在数组的第2个位置,并把数组的第k+3个数插入小根堆……依次类推,扫描完数组后,把小根堆依次出堆即可。

代码:

public class SortArrayDistanceLessK {

    public void sortedArrDistanceLessK(int[] arr, int k) {
        //优先队列默认小根堆
        PriorityQueue<Integer> heap = new PriorityQueue<>();
        int index = 0;
        for (int i = 0; i < arr.length; i++) {
            heap.add(arr[i]); // 入堆
            if (i > k) { //当扫描到完前k+1个数后,堆顶元素出堆,并放入数组的对应位置
                arr[index++] = heap.poll();  
            }
        }
        //扫描完数组,依次出堆即可把剩余元素排完
        while (!heap.isEmpty()) {
            arr[index++] = heap.poll();
        }
    }
}

2. 比较器

比较器的使用
1)比较器的实质就是重载比较运算符
2)比较器可以很好的应用在特殊标准的排序上
3)比较器可以很好的应用在根据特殊标准排序的结构上

代码:

import java.util.Arrays;
import java.util.Comparator;

public class Code03_Comparator {

    public static class Student {
        public String name;
        public int id;
        public int age;

        public Student(String name, int id, int age) {
            this.name = name;
            this.id = id;
            this.age = age;
        }

        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", id=" + id +
                    ", age=" + age +
                    '}';
        }
    }

    public static class IdAAscendingComparator implements Comparator<Student> {

        // 返回负数的时候,第一个参数排在前面
        // 返回正数的时候,第二个参数排在前面
        // 返回0的时候, 谁在前面无所谓
        @Override
        public int compare(Student o1, Student o2) {
            return o1.id - o2.id;
//            return 0;
        }
    }

    public static class IdDescendingComparator implements Comparator<Student> {

        @Override
        public int compare(Student o1, Student o2) {
            return o2.id - o1.id;
        }
    }

    public static class AgeAscendingComparator implements Comparator<Student> {

        @Override
        public int compare(Student o1, Student o2) {
            return o1.age - o2.age;
        }
    }

    public static class AgeDescendingComparator implements Comparator<Student> {

        @Override
        public int compare(Student o1, Student o2) {
            return o2.age - o1.age;
        }
    }

    public static void main(String[] args) {
        int[] arr = {5,4,3,2,7,9,1,0};
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));

        System.out.println("=======================");
        Student studnet1 = new Student("A", 2, 20);
        Student studnet2 = new Student("B", 3, 21);
        Student studnet3 = new Student("C", 1, 22);

        Student[] students = {studnet1, studnet2, studnet3};
        System.out.println("第一条打印");

        System.out.println("==========id升序排序==========");
        Arrays.sort(students, new IdAAscendingComparator());
        System.out.println(Arrays.toString(students));


        System.out.println("==========id降序排序==========");
        Arrays.sort(students, new IdDescendingComparator());
        System.out.println(Arrays.toString(students));

        System.out.println("==========Age升序排序==========");
        Arrays.sort(students, new AgeAscendingComparator());
        System.out.println(Arrays.toString(students));

        System.out.println("==========Age降序排序==========");
        Arrays.sort(students, new AgeDescendingComparator());
        System.out.println(Arrays.toString(students));

    }
}

运行结果:
在这里插入图片描述

import java.util.Comparator;
import java.util.PriorityQueue;

public class HeapTest {
    public static void main(String[] args) {
        PriorityQueue<Integer> heap = new PriorityQueue<>(new ACom());
        heap.add(6);
        heap.add(9);
        heap.add(3);
        heap.add(2);
        heap.add(10);
        while (!heap.isEmpty()) {
            System.out.println(heap.poll());
        }
    }

    public static class ACom implements Comparator<Integer> {

        // 如果返回负数,认为第一个参数应该放在上面
        // 如果返回正数,认为第二个参数放在上面
        // 如果返回0, 认为谁放前面都行
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1 - o2;
        }

    }
}

运行结果:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/186394.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++算法图解专栏】一篇文章带你入门二分算法

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4e3;专栏定位&#xff1a;为 0 基础刚入门数据结构与算法的小伙伴提供详细的讲解&#xff0c;也欢迎大佬们一起交流~ &#x1f4da;专栏地址&#xff1a;https://blog.csdn.net/Newin…

步进式PID控制算法及仿真

在较大阶跃响应时&#xff0c;很容易产生超调。采用步进式积分分离PID控制&#xff0c;该方法不直接对阶跃信号进行响应&#xff0c;而是使输入指令信号一步一步地逼近所要求的阶跃信号&#xff0c;可使对象运行平稳&#xff0c;适用于高精度伺服系统的位置跟踪。在步进式PID控…

【数据手册】CH340G芯片使用介绍

1.概述 CH340是一系列USB总线适配器&#xff0c;它通过USB总线提供串行、并行或IrDA接口。CH340G集成电路提供通用的MODEM信号&#xff0c;允许将UART添加到计算机上&#xff0c;或将现有的UART设备转换为USB接口。 2.特征 全速USB接口&#xff0c;兼容USB 2.0接口。使用最小…

Android核心技术【SystemServer加载AMS】

启动流程 Init 初始化Linux 层&#xff0c;处理部分服务 挂载和创建系统文件 解析rc文件&#xff1a; rc 文件中有很多action 进入无限循环 执行action&#xff1a;zygote 进程就在这里启动 for循环去解析参数&#xff0c;根据rc 文件中的action 执行相应操作 检测并重启需要…

细谈文件操作

该文章将详细的介绍文件操作这方面的知识&#xff0c;文件的打开&#xff0c;关闭&#xff0c;读取&#xff0c;写入&#xff0c;以及相关的函数都会在本文一一介绍&#xff0c;干货满满喔&#xff01;1.为什么使用文件2.什么是文件2.1程序文件2.2数据文件2.3文件名3.文件的打开…

SpringBoot(java)操作elasticsearch

elasticsearch我已经装了ik&#xff0c;中文分词器。已经使用容器搭建了集群。之前在我的博客-elasticsearch入门中&#xff0c;已经介绍了http请求操纵es的基本功能&#xff0c;java API功能和他一样&#xff0c;只是从http请求换成了javaApi操作。springBoot里继承了elastics…

蓝桥杯算法训练合集八 1.数的划分2.求先序排列3.平方计算4.三角形高5.单词复数

目录 1.数的划分 2.求先序排列 3.平方计算 4.三角形高 5.单词复数 1.数的划分 问题描述 将整数n分成k份&#xff0c;且每份不能为空&#xff0c;任意两份不能相同(不考虑顺序)。 例如&#xff1a;n7&#xff0c;k3&#xff0c;下面三种分法被认为是相同的。 1&#xff0c…

关于宏文档开启宏后还是不能正常使用问题

1.问题 2.开启宏 (62条消息) [Win10Excel365]尽管已启用VBA宏&#xff0c;Excel还是无法运行宏_逍遥猴哥的博客-CSDN博客 3. 问题还是没解决 发现可能是字体显示乱码&#xff0c;导致vba运行找不到争取路径 VBA编辑器中中文乱码的解决办法&#xff1a;1、依次点击【工具→选项…

如何写一个命令行解释器(SHELL)

文章目录前言什么是命令行解释器 ——SHELLSHELL的结构void print_info(char ** env) //打印命令行信息函数void read_comand(char **buffer) //读取指令函数char **split_line(char *buffer, int *flag) //分割字符串函数int excute_line(char **buffer, int flag) // 执行指令…

Redis 安全汇总小结

Redis redis 是一个C语言编写的 key-value 存储系统&#xff0c;可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。它通常被称为数据结构服务器&#xff0c;因为值&#xff08;value&#xff09;可以是 字符串(String), 哈希(Hash), 列表(list…

电子技术——基本MOS放大器配置

电子技术——基本MOS放大器配置 上一节我们探究了一种MOS管的放大器实现&#xff0c;其实MOS放大器还有许多变种配置&#xff0c;在本节我们学习最基本的三大MOS放大器配置&#xff0c;分别是共栅极&#xff08;CG&#xff09;、共漏极&#xff08;CD&#xff09;、共源极&…

【MSSQL】分析数据库日志文件无法收缩的问题

一、问题描述 在SQL Server 2008R2数据库中&#xff0c;无法对数据库日志进行收缩&#xff0c;导致日志不断膨胀。 二、问题分析 由于是日志文件不断增大且无法收缩&#xff0c;所以初步判断为存在未提交的事务。检查可能阻止日志阶段的活动事务&#xff0c;执行&#xff1a…

使用 JMX 连接远程服务进行监测

使用 JMX 连接远程服务进行监测1.JVM参数2.启动脚本3.演示使用相关JMX工具连接部署在服务器上的Java应用&#xff0c;可以对应用的内存使用量&#xff0c;CPU占用率和线程等信息进行监测。相关监测工具有jconsole&#xff0c;jprofiler&#xff0c;jvisualvm等。1.JVM参数 监测…

本地镜像发布到阿里云

1、找到阿里云控制台中的容器镜像服务&#xff0c;进入个人版 2、先创建命名空间&#xff0c;再创建镜像仓库 记住创建时设置的密码&#xff0c;选择创建本地的镜像仓库 建完之后&#xff0c;选择管理 进入后的界面如下 内容如下&#xff1a; 1. 登录阿里云Docker Registry $…

547、RocketMQ详细入门教程系列 -【消息队列之 RocketMQ(一)】 2023.01.30

目录一、RocketMQ 特点二、基本概念2.1 生产者2.2 消费者2.3 消息服务器2.4 名称服务器三、参考链接一、RocketMQ 特点 RocketMQ 是阿里巴巴在2012年开源的分布式消息中间件&#xff0c;目前已经捐赠给 Apache 软件基金会&#xff0c;并于2017年9月25日成为 Apache 的顶级项目…

【自然语言处理】【大模型】PaLM:基于Pathways的大语言模型

PaLM&#xff1a;基于Pathways的大语言模型《PaLM: Scaling Language Modeling with Pathways》论文地址&#xff1a;https://arxiv.org/pdf/2204.02311.pdf 相关博客 【自然语言处理】【大模型】PaLM&#xff1a;基于Pathways的大语言模型 【自然语言处理】【chatGPT系列】大语…

电脑重装系统后找不到硬盘怎么办

有网友的win10系统电脑出了系统故障进行了重装&#xff0c;但是又发现了重装系统后找不到硬盘的新问题&#xff0c;那么重装系统后找不到硬盘怎么办呢? 工具/原料&#xff1a; 系统版本&#xff1a;win10专业版 品牌型号&#xff1a;戴尔成就5880 方法/步骤&#xff1a; …

使用FFmpeg工具进行推流、拉流、截图、变速、转换,及常见问题处理

下载安装 FFmpeg下载官网&#xff1a;FFmpeg &#xff0c;这里提供了官网下载的windows环境 4.1.3版本&#xff1a;https://download.csdn.net/download/qq_43474959/12311422 下载后&#xff0c;配置环境变量&#xff0c;将bin文件地址加入到path中&#xff1a; 测试 在cmd…

数据结构 | 图结构 | 最小生成树 | Kruskal Prim算法讲解

文章目录前言Kruskal算法Prim算法前言 讲解之前&#xff0c;我们需要先明白连通图是指什么&#xff1f;连通图具有以一个顶点为起点可以到达该图中的任意一个顶点的特性&#xff0c;就算它们不直接相连&#xff0c;但是它们之间至少有一条可以递达的路径。并且连通图是针对无向…

Mysql 中的日期时间函数汇总

日期和时间函数MySQL中内置了大量的日期和时间函数&#xff0c;能够灵活、方便地处理日期和时间数据&#xff0c;本节就简单介绍一下MySQL中内置的日期和时间函数。1 CURDATE()函数CURDATE()函数用于返回当前日期&#xff0c;只包含年、月、日部分&#xff0c;格式为YYYY-MM-D…