算法6.堆结构、堆排序、加强堆

news2024/11/28 6:29:53

算法|6.堆结构、堆排序、加强堆

1.比较器的定义

题意:定义一个学生类,分别实现对学生对象数组按年龄升序、按id降序、按名字的字典序、按id排序且id相同的年龄大的排在前边。

解题思路:

  • 定义一个学生类
  • 定义一个实现了Comparator接口的类A
  • 使用Arrays.sort对它们进行排序时,传一个类A的对象

补充:字符串比较

  • equals():比较是否相等,相等返回true,不相等返回false str1.equals(str2);
  • equalsIgnoreCase():功能与上边类似,但是忽略大小写
  • ==:比较对象引用是否引用相同的实例,一致返回true,不一致返回false
  • compareTo():按字典序比较(默认升序)str.compareTo(String otherstr);

这里的需求显然采取最后一种,并且使用默认的即可,倘若要降序,只需要将两个参数交换位置即可。

核心代码:

public static class Student{
    String name;
    int id;
    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 AgeShengOrder implements Comparator<Student>{

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

//id降序
public static class IdJiangOrder implements Comparator<Student>{

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

//按名字的字典序
public static class NameOrder implements Comparator<Student>{

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

//按id排序,id相同,按年龄的降序
public static class IdShengAgeJiangOrder implements Comparator<Student>{

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

测试代码:

// for test
public static Student[] copyArray(Student[] arr) {
    if (arr == null) {
        return null;
    }
    Student[] res = new Student[arr.length];
    for (int i = 0; i < arr.length; i++) {
        res[i] = arr[i];
    }
    return res;
}

//for test
public static void main(String[] args) {
    Student[] students=new Student[6];
    students[0]=new Student("zhangsan",1,16);
    students[1]=new Student("lisi",2,17);
    students[2]=new Student("wangwu",3,13);
    students[3]=new Student("zhaoliu",4,31);
    students[4]=new Student("liuqi",5,25);
    students[5]=new Student("yuanba",5,28);

    Student[] students1=copyArray(students);
    Student[] students2=copyArray(students);
    Student[] students3=copyArray(students);

    System.out.println("按age升序");
    Arrays.sort(students,new AgeShengOrder());
    System.out.println(Arrays.toString(students));

    System.out.println("按id降序");
    Arrays.sort(students,new IdJiangOrder());
    System.out.println(Arrays.toString(students));

    System.out.println("按名字字典序");
    Arrays.sort(students,new NameOrder());
    System.out.println(Arrays.toString(students));

    System.out.println("按ID升序age降序");
    Arrays.sort(students,new IdShengAgeJiangOrder());
    System.out.println(Arrays.toString(students));
}

测试结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kZhFurBc-1685191183079)(F:\typora插图\image-20230527095309509.png)]

2.堆结构

题意:构建堆结构,完成堆元素的插入、弹出、判空、判满

其中插入依赖于1.堆元素的插入方法,弹出依赖于 2.堆顶元素的删除方法。

另外,实验系统的大根堆、小跟堆怎么用

解题思路:

说明:知道什么是完全二叉树、父节点和子节点位置之间的关系(i,2i+1,2i+2)

  • 注意:顺序largest=heap[largest]>index?largest:index;``left=2*index+1;;比较的都是元素,得到的临时结果都是坐标
  • 堆实际是一颗完全二叉树,数据存放实际是在一个数组中,因此先提供一个数组
  • 插入,先判满
  • 弹出,先判空
  • 元素插入堆——是看元素是不是上浮
  • 元素从堆顶删除——使用了替换法,堆顶和堆尾交换,之后依次从顶向下考察

核心代码:

public static class MyMaxHeap{
    private int[] heap;
    private int heapSize;
    private final int limit;

    public MyMaxHeap(int limit) {
        heap=new int[limit];
        heapSize=0;
        this.limit = limit;
    }
    //判空
    public boolean isEmpty(){
        return heapSize==0;
    }
    //判满
    public boolean isFull(){
        return heapSize==limit;
    }
    //插入元素
    public void push(int value){
        if(isFull()){
            System.out.println("已满,误cue");
            return ;
        }
        heap[heapSize]=value;
        heapInsert(heap,heapSize++);
    }
    //元素上浮
    private void heapInsert(int[] heap, int index) {
        //孩子[index] 左右孩子父亲[(index-1)/2]
        //1.上到顶了没 2.不大于父亲
        while(heap[index]>heap[(index-1)/2]){
            swap(heap,index,(index-1)/2);
            index=(index-1)/2;
        }
    }
    private void swap(int[] arr,int i,int j){
        int tmp=arr[i];
        arr[i]=arr[j];
        arr[j]=tmp;
    }

    //弹出元素
    public  int pop(){
        if(isEmpty()){
            System.out.println("当前为空,别调");
            return -1;
        }
        int ans=heap[0];
        swap(heap,0,--heapSize);
        heapify(heap,0,heapSize);
        return ans;
    }
    //元素下沉
    private void heapify(int[] heap, int index, int heapSize) {
        int left=2*index+1;
        //以左孩子有没有为循环条件
        //右孩子有没有可能有可能没有
        while(left<heapSize){
            //孩子中最大的:右孩子有且大于左孩子
            int largest=left+1<heapSize&&heap[left+1]>heap[left]?left+1:left;
            largest=heap[largest]>heap[index]?largest:index;
            if(largest==index){
                break;
            }
            swap(heap,index,largest);
            index=largest;
            left=2*index+1;
        }
    }
}
public static class BigCom implements Comparator<Integer>{

    @Override
    public int compare(Integer o1, Integer o2) {
        return o2-o1;
    }
}

测试代码:

public static void main(String[] args) {
    System.out.println("系统大根堆");
    //默认是小根堆
    PriorityQueue<Integer> pq=new PriorityQueue<>(new BigCom());
    pq.add(57);
    pq.add(51);
    pq.add(52);
    pq.add(54);
    pq.add(56);
    pq.add(59);
    while(!pq.isEmpty()){
        System.out.println(pq.poll());
    }
    System.out.println("===========================");

    System.out.println("自定义大根堆");
    MyMaxHeap myMaxHeap=new MyMaxHeap(6);
    myMaxHeap.push(57);
    myMaxHeap.push(51);
    myMaxHeap.push(52);
    myMaxHeap.push(54);
    myMaxHeap.push(56);
    myMaxHeap.push(59);

    System.out.println(myMaxHeap.pop());
    System.out.println(myMaxHeap.pop());
    System.out.println(myMaxHeap.pop());
    System.out.println(myMaxHeap.pop());
    System.out.println(myMaxHeap.pop());
    System.out.println(myMaxHeap.pop());
}

测试结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vjoh1MsT-1685191183080)(F:\typora插图\image-20230527171622475.png)]

3.堆排序

题意:使用堆排序算法对数组进行排序

解题思路:

  • 堆排序就是在堆的基础上在进行调整(调用上浮下沉方法)的排序算法

  • 堆排序的堆建法有两种,一种是从下往上建,这样要求数据全部给出,另一种是从上往下建,也是经典建法,能够适应数据一个一个给出的情况

    这里把两种都给出,但是还是主要理解第二种

  • (以升序为例)构建方法主要是创建大根堆后,不断将堆顶“弹出”

复杂度、稳定性分析:

平均/最好时间复杂度:O(NlogN)

平均/最好空间复杂度:O(logN)

不稳定

核心代码:

    public static void heapSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        //从上往下O(N*logN)
        for (int i = 0; i < arr.length; i++) {
            heapInsert(arr,i);
        }
        //从下往上O(N)
//        for (int i = arr.length - 1; i >= 0; i--) {
//            heapify(arr, i, arr.length);
//        }

        int heapSize = arr.length;
        swap(arr, 0, --heapSize);
        while (heapSize > 0) {
            heapify(arr, 0, heapSize);
            swap(arr, 0, --heapSize);

        }
    }

    //元素上浮
    private static void heapInsert(int[] heap, int index) {
        //孩子[index] 左右孩子父亲[(index-1)/2]
        //1.上到顶了没 2.不大于父亲
        while (heap[index] > heap[(index - 1) / 2]) {
            swap(heap, index, (index - 1) / 2);
            index = (index - 1) / 2;
        }
    }

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

    //元素下沉
    private static void heapify(int[] heap, int index, int heapSize) {
        int left = 2 * index + 1;
        //以左孩子有没有为循环条件
        //右孩子有没有可能有可能没有
        while (left < heapSize) {
            //孩子中最大的:右孩子有且大于左孩子
            int largest = left + 1 < heapSize && heap[left + 1] > heap[left] ? left + 1 : left;
            largest = heap[largest] > heap[index] ? largest : index;
            if (largest == index) {
                break;
            }
            swap(heap, index, largest);
            index = largest;
            left = 2 * index + 1;
        }
    }

测试代码:

// for test
public static void comparator(int[] arr) {
    Arrays.sort(arr);
}

测试结果:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VRouNdkh-1685191183081)(F:\typora插图\image-20230527123314276.png)]

4.加强堆

题意:给定堆结构,保证删除指定元素时间复杂度为O(logN)。

说明:

  1. 已经入堆的元素,如果参与排序的指标方法变化,针对每个元素,系统提供的堆无法做到时间复杂度为O(logN),都是O(N)

  2. 系统提供的堆只能弹出堆顶,做不到自由删除任何一个堆中的元素/做不到在O(logN)的条件下完成弹出

解题思路:

  • 做不到自如弹出,是因为,元素进堆之后,我们不能确定它的位置
  • 如果知道具体位置,那么调整还是删除直接对当前位置进行heapInser或者heapify即可
  • 怎么知道具体位置——我们手动加一张反向索引表,即通过元素值得到它在堆中的位置

核心代码:

//堆中的元素一定要是引用类型,否则就包一层
public class HeapGreater<T> {
    private ArrayList<T> heap;
    //精华所在
    private HashMap<T, Integer> indexMap;
    private int heapSize;
    private Comparator<? super T> comp;

    public HeapGreater(Comparator<? super T> comp) {
        this.comp = comp;
        heap = new ArrayList<>();
        indexMap = new HashMap<>();
        heapSize = 0;
    }

    //判空
    public boolean isEmpty() {
        return heapSize == 0;
    }

    //判满——没必要,容器大小可以动态变化

    //看堆顶
    public T peek(){
        return heap.get(0);//这里使用的是ArrayList的方法
    }

    //压入元素
    public void push(T value){
        heap.add(value);
        indexMap.put(value,heapSize);//调整之后的坐标在swap中进行更改
        heapInsert(heapSize++);
    }

    //弹出元素
    public T pop() {
        T ans=heap.get(0);
        swap(0,heapSize-1);
        indexMap.remove(ans);
        heapify(0);
        return ans;
    }

    //删除值为obj的元素
    public void remove(T obj){
        T replace=heap.get(heapSize-1);

        int index=indexMap.get(obj);
        indexMap.remove(obj);
        heap.remove(--heapSize);
        //如果是堆底直接删,不影响,反之需要重新设置
        if(obj!=replace){
            heap.set(index,replace);
            indexMap.put(replace,index);
            resign(replace);
        }
    }

    public void resign(T obj) {
        heapInsert(indexMap.get(obj));
        heapify(indexMap.get(obj));
    }

    //返回堆上所有的元素
    public List<T> getAllElements(){
        List<T> ans = new ArrayList<>();
        for (T c : heap) {
            ans.add(c);
        }
        return ans;
    }

    //元素上浮
    private void heapInsert(int index) {
        //默认建立小跟堆
        //1.上到顶了没 2.不大于父亲
        while(comp.compare(heap.get(index),heap.get((index-1)/2))<0){
            swap(index,(index-1)/2);
            index=(index-1)/2;
        }
    }
    private void swap(int i,int j){
        T o1=heap.get(i);
        T o2 = heap.get(j);
        heap.set(i,o2);
        heap.set(j,o1);
        indexMap.put(o2,i);
        indexMap.put(o1,j);
    }

    //元素下沉
    private void heapify(int index) {
        int left=2*index+1;
        while(left<heapSize){
            int best=left+1<heapSize&&comp.compare(heap.get(left+1),heap.get(left))<0?left+1:left;
            best=comp.compare(heap.get(best),heap.get(index))<0?best:index;
            if(best==index){
                break;
            }
            swap(index,best);
            index=best;
            left=2*index+1;
        }
    }
    public static class MyCom implements Comparator<Integer>{

        @Override
        public int compare(Integer o1, Integer o2) {
            return o1-o2;
        }
    }

}

测试代码:

public static void main(String[] args) {
    HeapGreater<Integer> heapGreater=new HeapGreater<>(new MyCom());
    heapGreater.push(1);
    heapGreater.push(11111);
    heapGreater.push(1111);
    heapGreater.push(111);
    System.out.println(heapGreater.getAllElements());
    System.out.println(heapGreater.pop());
    heapGreater.remove(111);
    System.out.println(heapGreater.getAllElements());
}

测试结果:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oVUzsMo2-1685191183082)(F:\typora插图\image-20230527172349440.png)]

堆基本内容总结

堆是一颗完全二叉树。优先级队列底层是它。

逻辑结构是完全二叉树,存储结构是数组

性质:

  1. 堆中某个节点的值总是不大于或不小于其父节点的值;
  2. 堆总是一棵完全二叉树。

例题总结:

  • 比较器:非基础类型;类实现接口;传对象
  • 堆创建:数组;pop–下沉heapify,push——上浮heapInsert(while条件的两个作用)
  • 堆排序:从下往上建堆;从上往下建堆;比较的元素,赋的值是坐标heapSort(建堆(heapInsert)+改成有序(heapify)),升序建大根堆,降序建小跟堆

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

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

相关文章

前端包管理器的简介,pnpm的使用以及和npm的区别

随着前端的不断发展&#xff0c;包管理工具也不断的丰富&#xff0c;从最开始的npm到现在cnpm,pnpm,yarn&#xff0c;一个新的包管理工具的出现是为了弥补之前包管理工具的一个痛点&#xff0c;今天我就来说一下包管理器的出现的原因和pnpm的简单使用和一些包管理工具的一些区别…

【Vue】二:Vue核心处理---事件处理

文章目录 1. 事件修饰符1.1 prevent1.2 stop1.3 capture - 添加事件侦听器时使用 capture 模式。1.4 self1.5 one1.6 passive 2.按键修饰符3.系统修饰符 1. 事件修饰符 1.1 prevent 当我们点击后&#xff0c;回去先执行关联的事件&#xff0c;然后再去执行默认行为&#xff0c…

Arduino学习

物联网学习资料 《arduino程序设计基础》陈吕洲 北京航空航天大学出版社 半颗心脏博客导航一站式搜索(所有博客的汇总帖) Ai-Thinker 安信可科技 github 半颗心脏 | 徐宏 蓝牙技术 蓝牙网关【【智能家居】入门攻略二&#xff01;啥是网关&#xff1f;蓝牙、zigbee协议详…

隐藏在背后的真相——数据存储的方式(上)

数据存储的方式 1. 数据类型详细介绍1.1类型的基本归 2. 整形在内存中的存储2.1原码&#xff0c;反码&#xff0c;补码2.2有符号&#xff08;unsigned&#xff09;和无符号&#xff08;signed&#xff09;2.3 例题 3. 大小端字节序介绍及判断 所属专栏&#xff1a;C语言❤️ &a…

学网络安全可以参考什么方向?该怎么学?

在这个圈子技术门类中&#xff0c;工作岗位主要有以下三个方向&#xff1a; 安全研发安全研究&#xff1a;二进制 方向安全研究&#xff1a;网络渗透方向 下面逐一说明一下。 安全研发 安全行业的研发岗主要有两种分类&#xff1a; 与安全业务关系不大的研发岗位与安全业务…

英文论文(sci)解读复现【NO.13】基于YOLOv5的自然场景下苹果叶片病害实时检测

此前出了目标检测算法改进专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改进方法对应与自己的应用场景有效果&#xff0c;并且多少改进点能发什么水平的文章&#xff0c;为解决大家的困惑&#xff0c;此系列文章旨在给大家解读发表高水平学术期刊中的 SCI论文&a…

python 真正的密码,字符串排序

代码&#xff1a; from typing import List def turePassword(wordList:List[str]) ->str:wordList list(set(wordList))#排序先字符串长度&#xff0c;其次字典序wordList.sort(keylambda x :(len(x),x))ans for i in range(len(wordList)-1,-1,-1):flag Truefor j in …

蓝奥声核心技术分享——无线同步群控技术

1.技术背景 无线同步群控技术指基于对目标场景状态变化的协同感知而获得触发响应并进行智能决策&#xff0c;属于蓝奥声核心技术--边缘协同感知(EICS&#xff09;技术的关键支撑性技术之一。该项技术涉及无线物联网边缘域网络的无线通信与智能控制技术领域&#xff0c;具体主要…

浅谈人工智能怎么提升工作效率

一、引言 随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;逐渐成为各行各业提高工作效率的重要工具。本报告旨在通过分析人工智能在提高工作效率方面的作用&#xff0c;探讨其具体实现方法&#xff0c;并通过案例分析来深入了解其实际应用效果和优缺点&…

算法8.从暴力递归到动态规划1

算法|8.从暴力递归到动态规划1 目前感觉&#xff0c;背包问题和货币数组问题本质相同&#xff0c;货币的与dp相关的三种代码写完了&#xff0c;快复习不完了&#xff0c;背包暂时先不写了&#xff0c;回头再写&#xff0c;补充&#xff0c;再总结&#xff0c;结合那个C大神的文…

对KMP算法的一点碎碎念——上篇

对KMP算法的一点碎碎念——上篇 文章目录 对KMP算法的一点碎碎念——上篇1. KMP 算法 Next数组 求解问题1.1 前置知识-最长公共前后缀LCP1.1.1 前缀与后缀1.1.2 最长公共前后缀LCP 1.2 手算法求解 Next数组值(3种常见情况)1.2.1 情况1: next数组 正常存放匹配字符的长度情况1的…

前端面试-React专题

目录 一.React1. React 的主要特性是什么2.React 的核心思想是3. 什么是jsx4. render()函数5. 函数组件和类组件的区别6. HTML和React中事件处理的区别7. React 中的合成事件8. 虚拟Dom&#xff1f;为什么使用&#xff1f;如何实现&#xff1f;9. 在 constructor 中给 super 函…

Excel - 如何给单元格加上下拉框

当你使用下拉列表来限制人们在单元格中的输入时&#xff0c;数据输入会更快、更准确。当有人选择一个单元格时&#xff0c;下拉列表的向下箭头就会出现&#xff0c;可以点击它并进行选择。 创建一个下拉列表 / Create a drop-down list 你可以通过提供下拉列表使工作表更有效率…

基于微信小程序的教学质量评价系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

ubuntu下编译esp32 micropython固件编译(可自行增加模块)

目录 0. 前言1. 安装ESP-IDF2. 初始化Micropython仓库3. 选择ESP-IDF相应版本情况&#xff11;情况2 4. 开始编译5.烧录固件 0. 前言 为ESP32编译Micropython固件 操作系统&#xff1a;ubuntu22.04 1. 安装ESP-IDF 本节需要ESP-IDF环境&#xff0c;安装开发环境是必要的。 …

FPGA之手把手教你做多路信号发生器(STM32与FPGA数据互传控制波形生成)

文章目录 博主的念叨一、任务介绍1、本文目标2、设计思路3、设计注意事项 二、设计代码1.顶层文件代码2.波形生成模块3.ROM例化4.PLL例化5.引脚分配 总结 博主的念叨 博主建了一个技术资源分享的群&#xff0c;开源免费&#xff0c;欢迎进来唠嗑280730348 最近趁热打铁做了一…

pandas库的常用操作介绍

目录 1.1.Pandas概述2.Pandas索引结构3.groupby学习5.Pandas数值运算二元统计6.对象操作7.merge合并显示设置9.pivot操作10. 时间操作11.常用操作12.groupby操作13.字符串操作14.索引操作15.pandas绘图操作 1.1.Pandas概述 Python的pandas库是一个数据处理和数据分析库&#x…

javascript基础七:说说你对Javascript中作用域的理解?

一、作用域 作用域&#xff0c;即变量&#xff08;变量作用域又称上下文&#xff09;和函数生效&#xff08;能被访问&#xff09;的区域或集合 换句话说&#xff0c;作用域决定了代码区块中变量和其他资源的可见性 举个粟子 function myFunction(){let name小爱同学 } undef…

6.2:荷兰国旗问题

文章目录 实现key前面的数都小于等key&#xff0c;key后面的数都大于等于key1&#xff1a;前后指针法&#xff1a;2&#xff1a;挖坑法3&#xff1a;单指针法&#xff08;左神&#xff09; 辗转相除法求最大公约数 实现key前面的数都小于等key&#xff0c;key后面的数都大于等于…

【leetCode:剑指 Offer】20. 表示数值的字符串

1.题目描述 请实现一个函数用来判断字符串是否表示数值&#xff08;包括整数和小数&#xff09;。 数值&#xff08;按顺序&#xff09;可以分成以下几个部分&#xff1a; 若干空格 一个 小数 或者 整数 &#xff08;可选&#xff09;一个 e 或 E &#xff0c;后面跟着一个 …