heap堆结构以及堆排序

news2024/11/25 10:39:18

堆的定义

堆(heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:

  • 堆中某个结点的值总是不大于或不小于其父结点的值;

  • 堆总是一棵完全二叉树。

将根结点最大的堆叫做最大堆或大根堆,根结点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等。

堆是非线性数据结构,相当于一维数组,有两个直接后继。

堆使用数组保存

使用一个一维数组保存堆数据

堆顶位于index0位置,i位置的左孩子位于 2*i+1位置,右孩子位于2*i+2位置,父位置位于(i-1)/2位置

堆操作

pop:弹出堆顶元素并删除

push:加入一个元素

peek:查看堆顶元素

heapify:从index位置,往下看,不断的下沉,直到到达或者堆最后位置

heapInsert:新加进来的数,现在停在了index位置,依次往上移动直到适合位置结束

code

/**
 * title:Heap
 * description: 堆介绍及其相关操作
 * date: 2023/9/8
 * author: fooleryang
 * version: 1.0
 */

/**
 *  堆 也叫优先级队列,是一颗 完全二叉树,其次,堆分为大根堆和小根堆
 *      完全二叉树
 *
 *      大根堆:
 *          子树的根节点是子树的最大值
 *      小根堆:
 *          子树的根节点是子数的最小值
 *  使用一个数组来表示一个堆
 *
 *  堆操作:
 *      heapInsert:新加进来的数,现在停在了index位置,依次往上移动直到适合位置结束
 *      heapify:从index位置,往下看,不断的下沉,直到到达或者堆最后位置
 *      peek:查看堆顶元素
 *      pop:弹出堆顶元素并删除
 *      push:加入一个元素
 *  使用一个数组记录堆
 *  用heapSize控制堆大小,heapSize =0说明堆为空
 *
 */
public  class Heap {
    //大根堆
    //数组存放
   private int [] heap;
   //堆数据最大存储量
   private  final int limit;
   //堆大小
   private int heapSize;

   public Heap(int limit){
       this.limit  = limit;
       heap = new int[limit];
       heapSize = 0;
   }

   public boolean isEmpty(){
       return heapSize == 0;
   }
   public boolean isFull(){
       return heapSize == limit;
   }
   private void swap(int [] arr,int l,int r){
       int temp = arr[l];
       arr[l] = arr[r];
       arr[r] = temp;
   }
   /**
    * 功能:
    *   堆插入时调整
    *   即: 在arr数组中,插入元素的位置为index,现需要进行调整
    * 参数:
    *   arr:待调整的堆
    *   index:待调整位置
    * 步骤:
    *   如果当前index位置和父位置 (index-1)/2 比较
    *   大于则交换,继续比较,直到小于等于或者到达堆顶位置即0位置
    *   小于等于则停止
    * 关于 (index -1) /2
    *   完全二叉树的求父节点位置应该是 index/2
    *   但是这是建立在index从1开始时
    *   如果index从0开始,那么就是 (index -1) /2
    *   1,2,3,4,5,6,7 => index=7的父节点index => 7/2 = 3
    *   0,1,2,3,4,5,6 => index=5的父节点index => (6-1)/2 = 2
    */
   private void heapInsert(int [] arr,int index){

       //结束条件包含:
       //arr[index] == 父节点value
       //index 达到0位置  arr[0] arr[(0-1)/2 = 0]
       while (arr[index] > arr[(index -1) / 2]){
           swap(arr,index,(index-1)/2);
           index = (index -1) /2;
       }
   }
   /**
    * @Description: 从index开始节点下沉,直到index位置的孩子都比index小,或者已经没有孩子
    * @Param arr:堆
    * @Param index:开始下沉节点
    * @Param heapSize:堆大小
    * 关于孩子节点:
    *   完全二叉树,左孩子 2*i +1,右孩子 2*i+2
    */
   private void heapify(int [] arr,int index,int heapSize){
       //左孩子index
       int leftChildIndex = 2 * index + 1;
       //遍历条件:index还有孩子
       while (leftChildIndex < heapSize){
           //比较index的左右孩子,记录得到最大左右孩子的index
           //右孩子存在 并且 右孩子大于左孩子,返回右孩子index,否则返回左孩子index
           //右孩子不存在 返回左孩子index
           int largestIndex = leftChildIndex+1<heapSize && arr[leftChildIndex] < arr[leftChildIndex+1]?leftChildIndex+1:leftChildIndex;
           //如果孩子中最大的孩子小于等于index,则结束
           if(arr[largestIndex] <= arr[index])break;
           //交换
           swap(arr,largestIndex,index);
           //index节点下沉
           index = largestIndex;
           leftChildIndex = 2 * index +1;
       }
   }

   //新加入一个元素
   public void push(int value){
       //查看是否已经堆满
       if(heapSize == limit)
           throw new RuntimeException("heap is full");
       //将新加入的元素放到最后一个位置
       heap[heapSize] = value;
       //向上调整堆
       heapInsert(heap,heapSize);
       //堆增加
       heapSize++;
   }

   /**
    * @Description: 弹出堆顶元素
    * 思路:
    *   堆顶元素位置 0
    *   将堆顶元素与堆最后一个位置交换,堆长减一
    *   从新堆顶元素0进行下沉调整堆
    */
   public int pop(){
       int ans = heap[0];
       swap(heap,0,heapSize-1);
       heapSize--;
       heapify(heap,0,heapSize);
       return ans;
   }

   public int peek(){
       if(heapSize==0)return -1;
       return heap[0];
   }

    //test
    public static void main(String[] args) {
        Heap heap1 = new Heap(10);
        heap1.push(3);
        heap1.push(9);
        heap1.push(1);
        heap1.push(3);
        heap1.push(5);
        heap1.push(0);
        heap1.push(6);

        while (!heap1.isEmpty()){
            System.out.print(heap1.pop() + " ");
        }
    }


}

构建堆图示

1. 已经构建好的堆

2. 新加入元素

3. 弹出堆顶元素

堆排序

时间复杂度 O(N*logN)

思路

待排序数组arr,将arr视作一个待排序的堆

先将arr构建成一个大根堆

再将大根堆arr调整为从小到大的顺序

构建大根堆过程
思路一

遍历arr数组

index = 0,arr 数组 0到index=0范围视为一个大根堆

index = 1,视为在0-0的大根堆加入一个元素,进行heapInsert操作

index = i,视为在0-(index-1)的大根堆加入元素

直到数组全部加入

for (int i = 0; i < arr.length; i++) {
    hearInsert(arr,i);
}
思路二

arr数组视为一个待排序堆,叶子结点分别构成的子树只有一个元素,天然为一个大根堆

依次往上构建的堆,则是需要调整,即新子树除了根节点其余子树都是大根堆,需要调整新子树的根节点,进行下沉调整

直到所有子树构建成一颗子树

如下图过程

1. 叶子结点分别为一个大根堆

2. 加入上层节点,需要进行下沉调整

3. 下沉调整

4. 继续步骤二 ,直到所有节点进入堆中

将大根堆数组调整到大小顺序
思路

将堆顶元素和最后一个元素交换,此时最大元素到达数组最后位置

将最后一个元素剔除堆

此时堆顶元素为待调整的元素,进行下沉调整

反复直到堆元素为空

int heapSize = arr.length;
do{
  swap(arr,0,--heapSize);// 等价与 swap(arr,0,heapSize-1);heapSize--;
  heapify(arr,0,heapSize);
}while (heapSize > 0);
code
    public static void heapSort(int [] arr){
        if(arr == null || arr.length < 2)return;

        //构建大根堆

        //从index = 0 开始
        //index = 0 ,则0-0为一个大根堆
        //inedx = 1,则将index=1插入到0-0堆中
        //index = i,则将index=i插入到0 到 (i-1)的堆中
        // O(N*logN)
//        for (int i = 0; i < arr.length; i++) {
//            hearInsert(arr,i);
//        }

        //或者这样调整,该步骤时间复杂度降低到O(N)
        //给定的数组是一个堆,只是顺序不对
        //从最低层即叶子节点开始下沉调整
        //最开始,叶子节点构成的子树为大根堆
        //上一层时,叶子节点构成的子树新加入一个节点并且位于子数的根节点,需要进行下沉调整
        //依次进行
        //O(N)
        for (int i = arr.length-1;i >=0;i--){
            heapify(arr,i,arr.length);
        }

        //将大根堆数组排序

        //将堆顶元素与数组最后一个元素交换
        //堆大小减一
        //交换完成后将堆顶元素下沉调整堆
//        int heapSize = arr.length;
//        swap(arr,0,heapSize-1);
//        heapSize--;
//        while (heapSize >0){
//            //调整
//            heapify(arr,0,heapSize);
//            swap(arr,0,heapSize-1);
//            heapSize--;
//        }
        int heapSize = arr.length;
        do{
            swap(arr,0,--heapSize);// 等价与 swap(arr,0,heapSize-1);heapSize--;
            heapify(arr,0,heapSize);
        }while (heapSize > 0);
    }
    private static void swap(int [] arr,int l,int r){
        int temp = arr[l];
        arr[l] = arr[r];
        arr[r] = temp;
    }
    //向下调整
    public static void heapify(int [] arr,int index,int heapSize){
        int left = index * 2 + 1;
        while (left < heapSize){
            int largest = left+1 < heapSize && arr[left+1] > arr[left]?left+1:left;
            if(arr[largest] < arr[index])break;
            swap(arr,index,largest);
            index = largest;
            left = index * 2 + 1;
        }
    }

    //插入一个新元素,向上调整
    public static void hearInsert(int [] arr,int index){
        int fatherIndex = (index - 1) / 2;
        while (arr[index] > arr[fatherIndex]){
            swap(arr,index,fatherIndex);
            index = fatherIndex;
            fatherIndex = (index - 1) / 2;
        }
    }

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

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

相关文章

YOLO目标检测——复杂场景人员行人数据集+已标注voc格式标签下载分享

实际项目应用&#xff1a;安防监控、人群管理、自动驾驶、城市规划、人机交互等等数据集说明&#xff1a;YOLO目标检测数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富&#xff0c;图片格式为jpg&#xff0c;分为训练集和验证集。标注说明&#xff1a;使用…

kubernetes(K8S)笔记

文章目录 大佬博客简介K8SDocker VS DockerDockerK8S简介K8S配合docker相比较单纯使用docker 大佬博客 Kubernetes&#xff08;通常缩写为K8s&#xff09;是一个用于自动化容器化应用程序部署、管理和扩展的开源容器编排平台。它的构造非常复杂&#xff0c;由多个核心组件和附加…

【Java基础篇 | 类和对象】--- 聊聊什么是内部类

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【JavaSE_primary】 本专栏旨在分享学习Java的一点学习心得&#xff0c;欢迎大家在评论区讨论&#x1f48c; 前言 当一个事物的内部&…

分享日常电脑遇到msvcr110.dll丢失的解决方法

最近&#xff0c;我在尝试运行一款新的软件时&#xff0c;突然遇到了一个错误提示&#xff0c;提示说缺少msvcr110.dll文件&#xff0c;导致软件无法启动。在使用电脑过程中&#xff0c;我们常常会遇到一些系统文件丢失的问题。其中&#xff0c;msvcr110.dll是Windows操作系统中…

10分钟从实现和使用场景聊聊并发包下的阻塞队列

上篇文章12分钟从Executor自顶向下彻底搞懂线程池中我们聊到线程池&#xff0c;而线程池中包含阻塞队列 这篇文章我们主要聊聊并发包下的阻塞队列 阻塞队列 什么是队列&#xff1f; 队列的实现可以是数组、也可以是链表&#xff0c;可以实现先进先出的顺序队列&#xff0c;…

【矩阵分解】PCA - 主成分分析中的数学原理

前言 本文主要对PCA主成分分析中的数学原理进行介绍&#xff0c;将不涉及或很少涉及代码实现或应用&#xff0c;阅读前请确保已了解基本的机器学习相关知识。 文章概述 PCA主成分分析属于矩阵分解算法中的入门算法&#xff0c;通过分解特征矩阵来实现降维。 本文主要内容&a…

【PowerQuery】Excel 一分钟以内刷新PowerQuery数据

当需要进行刷新的周期如果小于一分钟,采用数据自动刷新就无法实现自动刷新的目标。那就没有办法了吗?当然不是,这里就是使用VBA来实现自动刷新。这里实现VBA刷新的第一步就是将当前的Excel 保存为带有宏的Excel 文件,如果不带宏则无法运行带有宏代码的Excel文件,保存过程如…

JAVA中的String类中的一些常用方法

目录 字符串比较方法&#xff1a; boolean equals(Object anObject)&#xff1a; int compareTo(String s)&#xff1a; int compareToIgnoreCase(String str) 字符串查找方法&#xff1a; char charAt(int index)&#xff1a; int indexOf(int ch)&#xff1a; int inde…

sqlserver2012性能优化配置:设置性能相关的服务器参数

前言 sqlserver2012 长时间运行的话会将服务器的内存占满 解决办法 通过界面设置 下图中设置最大服务器内存 通过执行脚本设置 需要先开发开启高级选项配置才能设置成功 设置完成之后将高级选择配置关闭&#xff0c;还原成跟之前一样 --可以配置高级选项 EXEC sp_conf…

MySQL--数据库基础

数据库分类 数据库大体可以分为 关系型数据库 和 非关系型数据库 常用数据类型 数值类型&#xff1a; 分为整型和浮点型&#xff1a; 字符串类型 日期类型

【SpringMVC】一行代码完成文件上传JRebel的使用

目录 引言 一、JRebel的使用 1.1.安装JReble 1.2.反向代理工具 1.3.离线使用 二、文件上传 2.1.公共文件跳转 2.2.添加依赖 2.3.配置文件上传解析器 2.4.图片路径配置Tomcat 2.5.前端代码 2.6.文件上传实现 三、文件下载 3.1.Controller层 3.2.前端代码 四、多文…

Jetsonnano B01 笔记4:UART 通信配置及编程

今日继续我的Jetsonnano学习之路&#xff0c;今日学习使用Jetson硬件驱动之UART串口通信&#xff1a; 目录 简议串口通信&#xff1a; 硬件连接&#xff1a; 串口配置&#xff1a; 安装串口函数库&#xff1a; 设置权限&#xff1a; Python代码配置&#xff1a; 下载测试…

机器学习实战-系列教程5:手撕线性回归4之非线性回归(项目实战、原理解读、源码解读)

11、非线性模型 当得到一个回归方程会&#xff0c;得到一条直线来拟合这个数据的统计规律&#xff0c;但是实际中用这样的简单直线很显然并不能拟合出统计规律&#xff0c;所谓线性回归比如两个变量之间关系就直接用一条直线来拟合&#xff0c;2个变量和一个1个变量的关系就用…

PDF 工具箱

PDF 工具箱 V9.0.0.1 程序&#xff1a;VB.net 运行库&#xff1a;NET Framework 4.5 功能简介&#xff1a; 1、PDF文件多文件合并&#xff0c;可调整顺序。 2、PDF文件拆分&#xff0c;将每页拆分成独立的PDF文件。 3、PDF文件添加水印&#xff0c;文字或图片水印&…

代码随想录 -- day46 --139.单词拆分

139.单词拆分 dp[i] : 字符串长度为i的话&#xff0c;dp[i]为true&#xff0c;表示可以拆分为一个或多个在字典中出现的单词 递推公式是 if([j, i] 这个区间的子串出现在字典里 && dp[j]是true) 那么 dp[i] true。 本题一定是 先遍历 背包&#xff0c;再遍历物品 c…

【LeetCode题目详解】第九章 动态规划part09 198.打家劫舍 213.打家劫舍II 337.打家劫舍III(day48补)

本文章代码以c为例&#xff01; 一、力扣第198题&#xff1a;打家劫舍 题目&#xff1a; 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻…

【再识C进阶2(中)】详细介绍指针的进阶——函数指针数组、回调函数、qsort函数

前言 &#x1f493;作者简介&#xff1a; 加油&#xff0c;旭杏&#xff0c;目前大二&#xff0c;正在学习C&#xff0c;数据结构等&#x1f440; &#x1f493;作者主页&#xff1a;加油&#xff0c;旭杏的主页&#x1f440; ⏩本文收录在&#xff1a;再识C进阶的专栏&#x1…

开开心心带你学习MySQL数据库之第七篇

MySQL提供的约束 1.not null 2.unique 3.default 4.primary key 5.foreign key 表的设计 找到实体确定实体间的关系 一对一一对多多对多 聚合查询 ~~行之间的运算 ~~聚合函数 ~~分组group by 联合查询 ~~多表查询 ~~笛卡尔积: 把两个表放到一起进行排列组合 班级表 cla…

代码随想录 -- day45 -- 70. 爬楼梯 (进阶)、322. 零钱兑换 、279.完全平方数

70. 爬楼梯 &#xff08;进阶&#xff09; 这里要注意&#xff0c;这是一个排列组合的问题&#xff0c;所以要先遍历背包再遍历物品 dp[i]&#xff1a;爬到有i个台阶的楼顶&#xff0c;有dp[i]种方法 递推公式为&#xff1a;dp[i] dp[i - j] class Solution { public:int c…

基于51单片机万年历电压电流检测-proteus仿真-源程序

一、系统方案 本设计采用52单片机作为主控器&#xff0c;液晶1602显示&#xff0c;DS1302时钟检测&#xff0c;电流电压检测、按键设置报警&#xff0c;蜂鸣器报警。 二、硬件设计 原理图如下&#xff1a; 三、单片机软件设计 1、首先是系统初始化 /lcd1602初始化设置*/ vo…