Java堆排序和代码实现详解

news2024/10/6 16:25:15

堆的定义

堆是计算机科学中一类特殊的数据结构的统称,堆通常可以被看做是一棵完全二叉树的数组对象。

堆的特性

1.它是完全二叉树,除了树的最后一层结点不需要是满的,其它的每一层从左到右都是满的,如果最后一层结点不是满的,那么要求左满右不满。

2.它通常用数组来实现。
具体方法就是将二叉树的结点按照层级顺序放入数组中,根结点在位置1,它的子结点在位置2和3,而子结点的子结点则分别在位置4,5,6和7,以此类推。

在这里插入图片描述
如果一个结点的位置为k,则它的父结点的位置为[k/2],而它的两个子结点的位置则分别为2k和2k+1。这样,在不使用指针的情况下,我们也可以通过计算数组的索引在树中上下移动:从a[k]向上一层,就令k等于k/2,向下一层就令k等于2k或2k+1

3.每个结点都大于等于它的两个子结点。这里要注意堆中仅仅规定了每个结点大于等于它的两个子结点,但这两个子结点的顺序并没有做规定,跟我们之前学习的二叉查找树是有区别的。

堆的API设计

在这里插入图片描述

insert插入方法的实现

堆是用数组完成数据元素的存储的,由于数组的底层是一串连续的内存地址,所以我们要往堆中插入数据,我们只能往数组中从索引0处开始,依次往后存放数据,但是堆中对元素的顺序是有要求的,每一个结点的数据要大于等于它的两个子结点的数据,所以每次插入一个元素,都会使得堆中的数据顺序变乱,这个时候我们就需要通过一些方法让刚才插入的这个数据放入到合适的位置。
在这里插入图片描述
在这里插入图片描述

delMax删除最大元素方法的实现

由堆的特性我们可以知道,索引1处的元素,也就是根结点就是最大的元素,当我们把根结点的元素删除后,需要有一个新的根结点出现,这时我们可以暂时把堆中最后一个元素放到索引1处,充当根结点,但是它有可能不满足堆的有序性需求,这个时候我们就需要通过一些方法,让这个新的根结点放入到合适的位置。
在这里插入图片描述
在这里插入图片描述
所以,当删除掉最大元素后,只需要将最后一个元素放到索引1处,并不断的拿着当前结点a[k]与它的子结点a[2k]
和a[2k+1]中的较大者交换位置,即可完成堆的有序调整。

堆的代码实现和运行结果

public class Heap <T extends Comparable<T>>{

    //测试代码
    public static void main(String[] args) throws Exception {
            Heap<String> heap = new Heap<String>(20);
            heap.insert("S");
            heap.insert("G");
            heap.insert("I");
            heap.insert("E");
            heap.insert("N");
            heap.insert("H");
            heap.insert("O");
            heap.insert("A");
            heap.insert("T");
            heap.insert("P");
            heap.insert("R");
            String del;
            while((del=heap.delMax())!=null){
                System.out.print(del+",");
            }
    }
    //堆代码
    //存储堆中的元素
    private  T[] items;
    //记录堆中元素的个数
    private int N;
    //判断堆中索引i处的元素是否小于索引j处的元素
    public Heap(int capacity){
        items= (T[]) new Comparable[capacity+1];
        N=0;
    }
    //交换堆中i索引和j索引处的值
    private boolean less(int i,int j){
        return items[i].compareTo(items[j])<0;
    }

    private void  exch(int i,int j){
        T temp = items[i];
        items[i]=items[j];
        items[j]= temp;
    }
    //往堆中插入一个元素
    public void insert(T t){
        items[++N]=t;
        swim(N);
    }
    private void swim(int k){ //使用上浮算法,使索引k处的元素能在堆中处于一个正确的位置
        while(k>1){//如果已经到了根结点,就不需要循环了
            if(less(k/2,k)){//比较当前结点和其父结点
                exch(k/2,k);//父结点小于当前结点,需要交换
            }
            k=k/2;
        }
    }
    //删除堆中最大的元素,并返回这个最大元素
    public T delMax(){
        T max =items[1];
        //交换索引1处和索引N处的值
        exch(1,N);
        //删除最后位置上的元素
        items[N]=null;
        N--;
        sink(1);
        return max;
    }
    private void sink(int k){ //使用下沉算法,使索引k处的元素能在堆中处于一个正确的位置
        while(2*k<N){//如果当前已经是最底层了,就不需要循环了
            int max;//找到子结点中的较大者
            if(2*k+1<=N){//存在右子结点
                if(less(2*k,2*k+1)){
                    max=2*k+1;
                }else{//不存在右子结点
                    max=2*k;
                }
            }else{
                max= 2*k;
            }
//比较当前结点和子结点中的较大者,如果当前结点不小,则结束循环
            if(!less(k,max)){
                break;
            }
            exch(k,max);//当前结点小,则交换,
            k=max;
        }
    }
}

运行结果:

T,S,R,P,O,N,I,H,G,A,E,

堆的排序

问题:
给定一个数组:
String[] arr = {“S”,“O”,“R”,“T”,“E”,“X”,“A”,“M”,“P”,“L”,“E”}
请对数组中的字符按从小到大排序。
实现步骤:
1.构造堆;
2.得到堆顶元素,这个值就是最大值;
3.交换堆顶元素和数组中的最后一个元素,此时所有元素中的最大元素已经放到合适的位置;
4.对堆进行调整,重新让除了最后一个元素的剩余元素中的最大值放到堆顶;
5.重复2~4这个步骤,直到堆中剩一个元素为止。
API设计:

在这里插入图片描述

堆构造过程

堆的构造,最直观的想法就是另外再创建一个和新数组数组,然后从左往右遍历原数组,每得到一个元素后,添加到新数组中,并通过上浮,对堆进行调整,最后新的数组就是一个堆。
上述的方式虽然很直观,也很简单,但是我们可以用更聪明一点的办法完成它。创建一个新数组,把原数组0length-1的数据拷贝到新数组的1length处,再从新数组长度的一半处开始往1索引处扫描(从右往左),然后对扫描到的每一个元素做下沉调整即可。
在这里插入图片描述
在这里插入图片描述

堆的排序过程

对构造好的堆,我们只需要做类似于堆的删除操作,就可以完成排序。
1.将堆顶元素和堆中最后一个元素交换位置;
2.通过对堆顶元素下沉调整堆,把最大的元素放到堆顶(此时最后一个元素不参与堆的调整,因为最大的数据已经到
了数组的最右边)
3.重复1~2步骤,直到堆中剩最后一个元素。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现:

package practise7;

import java.util.Arrays;

public class HeapSort {
    public static void main(String[] args) throws Exception{
        String[] arr = {"S", "O", "R", "T", "E", "X", "A", "M", "P", "L", "E"};
        HeapSort.sort(arr);
        System.out.println(Arrays.toString(arr));
    }
    //对排序代码
    public static void sort(Comparable[] source){ //对source数组中的数据从小到大排序
        Comparable[] heap = new Comparable[source.length+1]; //1.创建一个比原数组大1的数组
        createHeap(source,heap);//2.构造堆
        //3.堆排序
            //3.1定义一个变量,记录heap中未排序的所有元素中最大的索引
        int N = heap.length-1;
        while(N!=1){
            //3.2交换heap中索引1处的元素和N处的元素
            exch(heap,1,N);
            N--;
            //3.3对索引1处的元素在0~N范围内做下沉操作
            sink(heap,1,N);
        }
        //4.heap中的数据已经有序,拷贝到source中
        System.arraycopy(heap,1,source,0,source.length);
    }
    //根据原数组source,构造出堆heap
    private  static void createHeap(Comparable[] source, Comparable[] heap){
        //1.把source中的数据拷贝到heap中,从heap的1索引处开始填充
        System.arraycopy(source,0,heap,1,source.length);
        //2.从heap索引的一半处开始倒叙遍历,对得到的每一个元素做下沉操作
        for(int i=(heap.length-1)/2;i>0;i--){
            sink(heap,i,heap.length-1);
        }
    }
    //判断heap堆中索引i处的元素是否小于索引j处的元素
    private static boolean less(Comparable[]heap,int i,int j){
        return heap[i].compareTo(heap[j])<0;
    }
    //交换heap堆中i索引和j索引处的值

    private static void exch(Comparable[]heap,int i,int j) {
        Comparable temp = heap[i];
        heap[i] = heap[j];
        heap[j] = temp;
    }
    //在heap堆中,对target处的元素做下沉,范围是0~range
    private static void sink(Comparable[]heap,int target, int range){
        //没有子结点了
        while(2*target<=range){
            //1.找出target结点的两个子结点中的较大值
            int max = 2*target;
            if(2*target+1<=range){
                //存在右子结点
                if(less(heap,2*target,2*target+1)){
                    max= 2*target+1;

                }
            }
            //2.如果当前结点的值小于子结点中的较大值,则交换
            if(less(heap,target,max)){
                exch(heap,target,max);
            }
            //3.更新target的值
            target=max;
        }
    }

}

运行结果:

[A, E, E, L, M, O, P, R, S, T, X]

参考:黑马程序员Java数据结构与java算法全套教程

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

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

相关文章

Hadoop(入门)

一、Hadoop概述 1.1 Hadoop是什么 1&#xff09;Hadoop是一个由Apache基金会所开发的分布式系统基础架构。 2&#xff09;主要解决&#xff0c;海量数据的存储和海量数据的分析计算问题。 3&#xff09;广义上来说&#xff0c;Hadoop通常是指一个更广泛的概念—Hadoop生态圈。…

[Vue3]自定义指令实现组件元素可拖拽移动

实现思路&#xff1a; 元素移动设计思路 1.在光标按下的时刻记录下光标的绝对位置坐标&#xff08;以视窗左上角为原点&#xff09;&#xff08;const {clientX, clientY} evt&#xff09; clientX / clientY 事件属性返回当事件被触发时光标指针相对于浏览器页面当前 body …

flutter系列之:移动端手势的具体使用

文章目录简介赋予widget可以点击的功能会动的组件可删除的组件总结简介 之前我们介绍了GestureDetector的定义和其提供的一些基本的方法&#xff0c;GestureDetector的好处就是可以把任何一个widget都赋予类似button的功能。 今天将会通过几个具体的例子来讲解一下GestureDet…

用ChatGPT写一段嵌入式代码

已剪辑自: https://mp.weixin.qq.com/s/uKkUwXx32LPkUYQK44z1lw 废话不多说&#xff0c;开整&#xff01; ChatGPT: Optimizing Language Models for Dialogue&#xff0c;即优化对话的语言模型&#xff0c;它以对话的方式进行交互。对话形式使ChatGPT能够回答后续问题&#…

性能测试---LoadRunner

目录 1.LoadRunner对比Jmeter的优势 2.LoadRunner三个组件之间的关系 3.学习VUG的使用 3.1创建性能测试脚本并进行录制 第一步:打开VUG,创建一个新的性能测试的脚本 第二步:对新建的脚本进行设置 第三步:启动WebTours服务 第四步:回到VUG中,点击录制按钮并设置录制选项…

学习编程的五个关键点!你需要get它,并运用!

总体来说&#xff0c;学习如何编程是一件较难的事情。我最近发现大学里的计算机课程和各种编程训练营错过了编程的一些重要因素&#xff0c;对新手的教学用了不太恰当的方法。于是&#xff0c;我准备分享一个成功的编程课程应该具备的五大基本支柱。 菜鸟的目标是掌握编程的基…

form表单发送put、delete、patch请求的实现过程

关于发送put、delete、patch请求底层实现过程 对于put这些请求&#xff0c;我们无法直接通过form表单发送&#xff0c;form表单仅支持get和post请求&#xff1b; 虽然我们无法直接通过form表单发送这些请求&#xff0c;但我们可以以form表单为载体做二次请求&#xff1a;使用f…

[附源码]计算机毕业设计Node.js宠物商店管理系统(程序+LW)

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

48数据流中的中位数 49表达式 50两数之和

48数据流中的中位数 第一次没看到要求排序&#xff0c;还以为题目答案写错了&#xff0c;用排序的内置函数也正好是nlogn import java.util.ArrayList;public class Solution {ArrayList<Integer> list new ArrayList<>();public void Insert(Integer num) {list…

小满nestjs(第二十七章 nestjs typeOrm关系)

在我们开始的过程中&#xff0c;肯定不会把数据存在一个表里面&#xff0c;我们会进行分表&#xff0c;把数据分开存&#xff0c;然后通过关联关系&#xff0c;联合查询。 typeOrm 文档 一对一 | TypeORM 中文文档 前端代码还是复用上一章的 增加了一个添加Tag <template…

腾讯安全联合发布《2022游戏安全白皮书》:外挂对抗仍然激烈

2022年以来&#xff0c;各类游戏安全事件的发生给不断影响着游戏生态的健康发展。同时&#xff0c;随着游戏行业数字化进程的加快&#xff0c;以及游戏全球化布局的不断推进&#xff0c;游戏厂商对于游戏安全的投入越来越大&#xff0c;掌握最新的行业安全态势有利于其安全防护…

外汇天眼:WiKiEXPO亮相香港亚洲博览馆,史上最强大咖阵容坐镇

凛冬已至&#xff0c;在这个寒冷的冬天&#xff0c;WikiGlobal将于2022年12月16日至17日早9:00--晚18:00在香港的亚洲国际博览馆举办为期两天的“Wiki Finance EXPO Asia 2022”。目前展会已拉开帷幕。  此次展会展厅面积高达5000多平方米&#xff0c;经过WiKiEXPO科学的规划和…

【数据结构】线性表之单链表

目录 一、链表的概念 1、概念 2、分类 3、重点 二、单链表模拟实现 1、准备 2、头插法 3、尾插法 4、指定下标插入 5、遍历 6、删除第一次出现的元素key 7、删除所有的key 8、双指针删除所有的key 一、链表的概念 1、概念 是一种物理存储结构上非连续的存储结构&a…

PS-历史记录

目录 哪里能找到【历史记录】面板 1、窗口→历史记录 2、编辑→清理→历史记录 还原 1、点击【历史记录】面板 快捷键 【ctrlz】 【shiftctrlz】 从当前状态创建新文档 创建新快照 给快照起名 1、右击你要创建快照的步骤 2、点击面板菜单 3、先按住alt不动&#…

Java 对象和类

Java作为一种面向对象语言。支持以下基本概念&#xff1a; 多态继承封装抽象类对象实例方法重载 本节我们重点研究对象和类的概念。 对象&#xff1a;对象是类的一个实例&#xff08;对象不是找个女朋友&#xff09;&#xff0c;有状态和行为。例如&#xff0c;一条狗是一个对…

猿如意|chat GPT测评

文章目录猿如意猿如意传送门猿如意个人使用感受好的一面&#xff1a;可以改进的一面:什么是猿如意chat GPT测评chat GPT使用过程使用场景描述&#xff1a;问题1问题2问题3问题4&#xff1a;问题5&#xff1a;主观感受&#xff1a;认为此功能不足的地方&#xff1a;对此功能的期…

学习编程的过程中可能会走哪些弯路,有哪些经验可以参考?

很多人学习编程, 走的弯路可以总结为以下几点: 一言不合找视频&#xff0c;几十集视频刷半年。 很多人学习编程的时候&#xff0c;喜欢看视频学&#xff0c;我这里总结一下看视频学习编程的弊端。 1. 完善的视频资源往往稍稍过时&#xff0c;比如你会发现很多java的教学视频…

产品设计市场调研有哪些特点?

产品市场种类繁多&#xff0c;变化无常&#xff0c;消费者需求各异。在工业设计之初&#xff0c;需要对行业和区域环境进行调查分析&#xff0c;深入了解市场情况、市场供求关系、客户引导、趋势等&#xff0c;客观合理地对新产品进行适当定位。只有有了正确的新产品概念规划方…

三方接口签名验签简易设计与实现

本人水平有限&#xff0c;对密码学的理解相当浅显。错误与疏漏&#xff0c;欢迎各位指正。 〇、写在前面 接口安全防护是个永恒的话题&#xff0c;提供给前端的接口需要登录&#xff0c;提供给服务的接口(下文简称"三方接口")也需要鉴权&#xff1b;当前大环境下,ht…

chatgpt教我内存对齐,对齐了但没完全对齐?

文章目录内存对齐关于chatgpt的回答总结内存对齐 关于chatgpt的回答 我与chatgpt的对话如下&#xff1a; 我现在来描述与总结上述对话都干了啥以及我为什么要问这个。 我本来是在学习rapidjson源码里面的内存池实现&#xff0c;然后 RAPIDJSON_ALIGN 没有看懂&#xff0c;所…