当前位置: 首页 > news >正文

Java数据结构第二十期:解构排序算法的艺术与科学(二)

专栏:Java数据结构秘籍

个人主页:手握风云

目录

一、常见排序算法的实现

1.1. 直接选择排序

1.2. 堆排序

1.3. 冒泡排序

1.4. 快速排序


一、常见排序算法的实现

1.1. 直接选择排序

        每⼀次从待排序的数据元素中选出最小的⼀个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。第一轮,先让j下标遍历出数组中最小的元素,再利用MinIndex存放最小的下标,利用最小值与i下标的元素进行交换;第二轮,依然让j下标遍历出待排序中最小的元素,再利用MinIndex存放最小的下标,利用最小值与i下标的元素进行交换……知道i走到最后一个元素,这样就能保证i之前的元素全部是有序的。

import java.util.Random;public class Sort {public void SelectSort(int[] array){for (int i = 0; i < array.length; i++) {int MinIndex = i;for (int j = i+1; j < array.length; j++) {if(array[MinIndex] > array[j]){MinIndex = j;}}swap(array,MinIndex,i);}}private void swap(int[] array,int i,int j){int tmp = array[i];array[i] = array[j];array[j] = tmp;}public void DisOrder(int[] array) {Random ran = new Random();for (int i = 0; i < array.length; i++) {array[i] = ran.nextInt(100);}}
}
import java.util.Arrays;public class Test {public static void main(String[] args) {int[] array = new int[6];Sort sort = new Sort();sort.DisOrder(array);System.out.println("排序前:" + Arrays.toString(array));sort.SelectSort(array);System.out.println("排序后:"+Arrays.toString(array));}
}

        直接选择排序是不稳定的。空间上,没有使用额外的空间,空间复杂度为O(n) =1。时间上,

        直接选择排序还有另外一种思路。与上面的方法不同的是,我们需要两个值MinIndex接收最小值下标和MaxIndex来接收最大值下标,再额外定义两个指针left和right。这种选择排序的思路是从首尾找,起始两个值接收下标都为0,利用i去遍历数组,找出最大值与最小值下标,再让left下标的值与MinIndex下标的值交换,right下标的值与MaxIndex下标的值交换。接着再让left向左移动,right向右移动,直到相遇,循环结束

        完整代码实现:

import java.util.Random;public class Sort {public void DisOrder(int[] array) {Random ran = new Random();for (int i = 0; i < array.length; i++) {array[i] = ran.nextInt(100);}}public void SelectSort(int[] array) {int left = 0, right = array.length - 1;while (left < right) {int MinIndex = left, MaxIndex = left;for (int i = left + 1; i <= right; i++) {if (array[i] < array[MinIndex]) {MinIndex = i;}if (array[i] > array[MaxIndex]) {MaxIndex = i;}}swap(array, MinIndex, left);swap(array, MaxIndex, right);left++;right--;}}private void swap(int[] array, int i, int j) {int tmp = array[i];array[i] = array[j];array[j] = tmp;}
}
import java.util.Arrays;public class Test {public static void main(String[] args) {Sort sort = new Sort();int[] array = new int[6];sort.DisOrder(array);System.out.println("排序前:" + Arrays.toString(array));sort.SelectSort(array);System.out.println("排序后:" + Arrays.toString(array));}
}

        但我们一运行,就会发现,排序出现了问题,这是因为,如果最大值或最小值本身就在首尾,那么一交换,最大值或最小值就会跑掉,,所以我们还需要判断一下。

import java.util.Random;public class Sort {public void DisOrder(int[] array) {Random ran = new Random();for (int i = 0; i < array.length; i++) {array[i] = ran.nextInt(100);}}public void SelectSort(int[] array) {int left = 0, right = array.length - 1;while (left < right) {int MinIndex = left, MaxIndex = left;for (int i = left + 1; i <= right; i++) {if (array[i] < array[MinIndex]) {MinIndex = i;}if (array[i] > array[MaxIndex]) {MaxIndex = i;}}swap(array, MinIndex, left);/*if (left == MaxIndex) {MaxIndex = MinIndex;}*/swap(array, MaxIndex, right);left++;right--;}}private void swap(int[] array, int i, int j) {int tmp = array[i];array[i] = array[j];array[j] = tmp;}
}
import java.util.Arrays;public class Test {public static void main(String[] args) {Sort sort = new Sort();int[] array = new int[6];sort.DisOrder(array);System.out.println("排序前:" + Arrays.toString(array));sort.SelectSort(array);System.out.println("排序后:" + Arrays.toString(array));}
}

1.2. 堆排序

        堆排序是指利⽤堆积树这种数据结构所设计的⼀种排序算法,它是选择排序的⼀ 种,通过堆来进⾏选择数据。需要注意的是排升序要建大堆,排降序建小堆。

import java.util.Random;public class Sort {public void HeapSort(int[] array) {CreateHeap(array);int end = array.length - 1;while(end > 0){swap(array,0,end);ShiftDown(array,0,end);end--;}}private void CreateHeap(int[] array) {for (int parent = (array.length - 1 - 1) / 2; parent >= 0; parent--) {ShiftDown(array, parent, array.length);}}private void ShiftDown(int[] array, int parent, int length) {int child = 2 * parent + 1;while (child < length) {if (child + 1 < length && array[child] < array[child + 1]) {child++;}if (array[child] > array[parent]) {swap(array,parent,child);parent = child;child = 2 * parent + 1;} else {break;}}}private void swap(int[] array,int i,int j){int tmp = array[i];array[i] = array[j];array[j] = tmp;}public void DisOrder(int[] array){Random ran = new Random();for (int i = 0; i < array.length; i++) {array[i] = ran.nextInt(1,100);}}
}
import java.util.Arrays;public class Solution {public static void main(String[] args) {Sort sort = new Sort();int[] array = new int[6];sort.DisOrder(array);System.out.println("排序前:"+ Arrays.toString(array));sort.HeapSort(array);System.out.println("排序前:"+ Arrays.toString(array));}
}

        堆排序使⽤堆来选数,效率就⾼了很多。但堆排序是不稳定的,时间复杂度为O(n)=nlogn,空间复杂度为O(n)=1

1.3. 冒泡排序

        定义下标j,比较array[j]与array[j+1]的值,如果array[j] > array[j+1],则交换两数的位置。我们还可以进行一个优化,如果数组本身就是有序,或者没有走完所有的趟数就已经有序,那么后面就不用再比较了。

import java.util.Random;public class Sort {public void DisOrder(int[] array) {Random ran = new Random();for (int i = 0; i < array.length; i++) {array[i] = ran.nextInt(1, 100);}}public void BubbleSort(int[] array) {//表示交换的趟数for (int i = 0; i < array.length-1; i++) {boolean flg = false;for (int j = 0; j < array.length-1-i; j++) {if(array[j] > array[j+1]){swap(array,j,j+1);flg = true;}}if(!flg){break;}}}private void swap(int[] array,int i,int j){int tmp = array[i];array[i] = array[j];array[j] = tmp;}
}
import java.util.Arrays;public class Test {public static void main(String[] args) {Sort sort = new Sort();int[] array = new int[6];sort.DisOrder(array);System.out.println("排序前:"+Arrays.toString(array));sort.BubbleSort(array);System.out.println("排序后:"+Arrays.toString(array));}
}

        冒泡排序是稳定的。时间复杂度:O(n)=n^{2},空间复杂度:O(n)=1

1.4. 快速排序

        快速排序是Hoare于1962年提出的⼀种⼆叉树结构的交换排序⽅法,其基本思想为:任取待排序元素 序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列。

        我们需要定义两个指针left和right,假设以6作为基准值,left向左移动,遇到比6大的数停下;right向右移动,遇到比6小的数停下,然后交换两个元素。当两个指针相遇时,再与基准值进行交换。这样就能保证6左边都是比6小的数,6右边都是比6大的数。按照这个过程再次进行,,构成递归的条件,直到分离出只含一个值的子序列。这样就构成了如下图所示的二叉树结构。

public class Sort {public void QuickSort(int[] array){}public void Quick(int[] array,int start,int end){if(start >= end){//如果结点的右子树为空,就不用遍历右边return;}int par = partition(array,start,end);Quick(array,start,par-1);Quick(array,par+1,end);//当start==end时,递归条件结束}private int partition(int[] array, int left, int right) {return -1;}
}

        我们接下来要思考的问题是如何写partition这个方法。无论是递归左边还是右边,与上面的过程都是一样的。只要left下标的值比基准值小,left右移;只要right下标的值比基准值大,right右移。这里我们还需要注意里层的while循环,指针不能越界。

    private int partition(int[] array, int left, int right) {int i = left;int tmp = array[left];while(left < right){while(left < right && array[right] >= tmp){right--;}while(left < right && array[left] <= tmp){left++;}swap(array,left,right);}swap(array,left,i);return left;}

        完整代码实现:

import java.util.Random;public class Sort {public void DisOrder(int[] array){Random ran = new Random();for (int i = 0; i < array.length; i++) {array[i] = ran.nextInt(1,40);}}public void QuickSort(int[] array){Quick(array,0,array.length-1);}public void Quick(int[] array,int start,int end){if(start >= end){//如果结点的右子树为空,就不用遍历右边return;}int par = partition(array,start,end);Quick(array,start,par-1);Quick(array,par+1,end);//当start==end时,递归条件结束}private int partition(int[] array, int left, int right) {int i = left;int tmp = array[left];while(left < right){while(left < right && array[right] >= tmp){right--;}while(left < right && array[left] <= tmp){left++;}swap(array,left,right);}swap(array,left,i);return left;}private void swap(int[] array,int i,int j){int tmp = array[i];array[i] = array[j];array[j] = tmp;}
}
import java.util.Arrays;public class Test {public static void main(String[] args) {Sort sort = new Sort();int[] array = new int[6];sort.DisOrder(array);System.out.println("排序前:"+Arrays.toString(array));sort.QuickSort(array);System.out.println("排序后:"+Arrays.toString(array));}
}

        这里解释一下为什么先让right移动,防止越过某些值。这里我们还需要注意,>=或<=的等号不能省略,如果left和right的值与基准值相等,那么就不会进入内层的while循环,导致外层的while循环陷入死循环。

        快速排序是不稳定的。快速排序的时间复杂度通常是指最好情况下,因为我们经常对快速排序进行优化。

        快速排序还有一种做法——挖坑法。与上面的方法类似,我们依然是以6为基准值,把6存进tmp中,right向左移动,遇到比6小的数,把6之前的位置填上;left向右移动,遇到比6大的数,把5之前的位置填上……我们只需要对上面的代码进行修改就可以。

import java.util.Random;public class Sort {public void DisOrder(int[] array){Random ran = new Random();for (int i = 0; i < array.length; i++) {array[i] = ran.nextInt(1,40);}}public void QuickSort(int[] array){Quick(array,0,array.length-1);}public void Quick(int[] array,int start,int end){if(start >= end){//如果结点的右子树为空,就不用遍历右边return;}int par = partition(array,start,end);Quick(array,start,par-1);Quick(array,par+1,end);//当start==end时,递归条件结束}private int partition(int[] array, int left, int right) {int i = left;int tmp = array[left];while(left < right){while(left < right && array[right] >= tmp){right--;}array[left] = array[right];while(left < right && array[left] <= tmp){left++;}array[right] = array[left];}array[left] = tmp;return left;}}
import java.util.Arrays;public class Test {public static void main(String[] args) {Sort sort = new Sort();int[] array = new int[6];sort.DisOrder(array);System.out.println("排序前:"+ Arrays.toString(array));sort.QuickSort(array);System.out.println("排序后:"+ Arrays.toString(array));}
}

        我们接下来思考一下快排的优化。我们先来看第一种三数取中法。我们找出start和end的中位数,让二叉树的形状尽量不要出现单分支的情况。那我们怎么再这段区间里面去找中位数呢?我们可以通过下标来找

    private static int midNum(int[] array, int left, int right) {int mid = (left+right)/2;if(array[left] < array[right]) {if(array[mid] < array[left]) {return left;}else if(array[mid] > array[right]) {return right;}else {return mid;}}else {if(array[mid] > array[left]) {return left;}else if(array[mid] < array[right]) {return right;}else {return mid;}}}

        第二种,递归到⼩的⼦区间时,可以考虑使⽤插⼊排序。

相关文章:

Java数据结构第二十期:解构排序算法的艺术与科学(二)

专栏&#xff1a;Java数据结构秘籍 个人主页&#xff1a;手握风云 目录 一、常见排序算法的实现 1.1. 直接选择排序 1.2. 堆排序 1.3. 冒泡排序 1.4. 快速排序 一、常见排序算法的实现 1.1. 直接选择排序 每⼀次从待排序的数据元素中选出最小的⼀个元素&#xff0c;存放在…...

inkscape裁剪svg

参考https://blog.csdn.net/qq_46049113/article/details/123824888&#xff0c;但是上个博主没有图片&#xff0c;不太直观&#xff0c;我补上。 使用inkscape打开需要编辑的图片。 在左边导航栏&#xff0c;点击矩形工具&#xff0c;创建一个矩形包围你想要保留的内容。 如果…...

类加载器加载过程

今天我们就来深入了解一下Java中的类加载器以及它的加载过程。 一、什么是类加载器&#xff1f; 在Java中&#xff0c;类加载器&#xff08;Class Loader&#xff09;是一个非常重要的概念。它负责将类的字节码文件&#xff08;.class文件&#xff09;加载到Java虚拟机&#x…...

Git基础之基本操作

文件的四种状态 Untracked&#xff1a;未追踪&#xff0c;如新建的文件&#xff0c;在文件夹中&#xff0c;没有添加到git库&#xff0c;不参与版本控制&#xff0c;通过git add将状态变为staged Unmodify&#xff1a;文件已入库&#xff0c;未修改&#xff0c;即版本库中的文件…...

简单的 Python 示例,用于生成电影解说视频的第一人称独白解说文案

以下是一个简单的 Python 示例&#xff0c;用于生成电影解说视频的第一人称独白解说文案。这个示例使用了 OpenAI 的 GPT 模型&#xff0c;因为它在自然语言生成方面表现出色。 实现思路 安装必要的库&#xff1a;使用 openai 库与 OpenAI API 进行交互。设置 API 密钥&#…...

[Pycharm]创建解释器

仅以此文章来记录自己经常脑子抽忘记的地方 有时候我们在建好了一个项目以后&#xff0c;想要更换解释器。以新建conda解释器为例。 一、conda解释器 1.选择setting 2.选择Add Local Interpreter 3.type选则conda。如果你之前已经有了conda环境&#xff0c;和我一样选择了Gen…...

在 k8s中查看最大 CPU 和内存的极限

在 Kubernetes&#xff08;k8s&#xff09;中&#xff0c;你可以从不同层面查看最大 CPU 和内存的极限&#xff0c;下面为你详细介绍从节点和集群层面查看的方法。 查看节点的 CPU 和内存极限 节点的 CPU 和内存极限是指单个节点上可分配的最大资源量&#xff0c;可通过以下几…...

【Python】为什么要写__init__.py

文章目录 PackageA(__init__特性)应该往__init__.py里放什么东西&#xff1f;1、包的初始化2、管理包的公共接口3、包的信息 正常我们直接导入就可以执行&#xff0c;但是在package的时候&#xff0c;有一种__init__.py的特殊存在 引入moduleA.py&#xff0c;执行main.py&…...

【IPFS应用开发】IPFS播放器-上传助手

本系列文章是针对 https://blog.csdn.net/weixin_43668031/article/details/83962959 内容的实现所编写的。开发经历包括思考过程、重构和推翻重来。 基于IPFS的视频播上传助手发布 起源一、优势:二、劣势:三、未来展望:上传助手Demo版本发布公告欢迎体验!立即体验:http:/…...

单细胞多数据集整合和去除批次效应教程,代做各领域生信分析

单细胞多数据集整合和去除批次效应教程 每个数据集的数据分别单独进行读取单细胞数据构建Seurat分析对象 读取各种来源的单细胞数据构建Seurat分析对象的教程 做这一步的时候可以查看我这篇写的非常详细的教程文章&#xff1a; 【腾讯文档】单细胞分析步骤1读取各种来源格式…...

Windows控制台函数:移动光标位置函数SetConsoleCursorPosition()

目录 什么是 SetConsoleCursorPosition&#xff1f; 它长什么样&#xff1f; 什么是 COORD&#xff1f; 怎么用它&#xff1f; 它有什么用&#xff1f; 跟 C 标准库有什么不一样&#xff1f; 注意事项 再试一个有趣的例子 什么是 SetConsoleCursorPosition&#xff1f;…...

MyBatis-Plus 注解大全

精心整理了最新的面试资料和简历模板&#xff0c;有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 MyBatis-Plus 注解大全 MyBatis-Plus 是基于 MyBatis 的增强工具&#xff0c;通过注解简化了单表 CRUD 操作和复杂查询的配置。以下是常用注解的分类及详细说…...

Redis基础之基础概念

NoSQL数据库的优点 1.直接减少CPU与IO压力&#xff0c;是直接通过内存来读取的 2.可以直接作为缓存使用&#xff0c;减少IO操作 如果我们在请求中需要来传递数据&#xff0c;使用NoSQL可以来进行数据的直接存储和读取&#xff0c;从而来减少CPU与IO压力 或者是如果一些数据较为…...

Django小白级开发入门

1、Django概述 Django是一个开放源代码的Web应用框架&#xff0c;由Python写成。采用了MTV的框架模式&#xff0c;即模型M&#xff0c;视图V和模版T。 Django 框架的核心组件有&#xff1a; 用于创建模型的对象关系映射为最终用户设计较好的管理界面URL 设计设计者友好的模板…...

热图回归(Heatmap Regression)

热图回归(Heatmap Regression)是一种常用于关键点估计任务的方法,特别是在人体姿态估计中。它的基本思想是通过生成热图来表示某个关键点在图像中出现的概率或强度。以下是热图回归的主要特点和工作原理: 主要特点 热图表示: 每个关键点对应一个热图,热图中的每个像素值…...

SpringSecurity认证授权完整流程

SpringSecurity认证流程&#xff1a;loadUserByUsername&#xff08;&#xff09;方法内部实现。 实现步骤&#xff1a; 构建一个自定义的service接口&#xff0c;实现SpringSecurity的UserDetailService接口。建一个service实现类&#xff0c;实现此loadUserByUsername方法。…...

MongoDB用户管理和复制组

用户管理 1、建用户时&#xff0c;use到的库就是此用户的验证库 2、登录时必须明确指定验证库才能登录 3、通常管理员用的验证库是admin&#xff0c;普通用户的验证库一般是所管理的库设置为验证库 4、如果直接登录到数据库&#xff0c;不进行use&#xff08;示例&#xff…...

【Android】setText调用导致的悬浮窗抖动问题

在Android13中&#xff0c;有这么一个bug&#xff0c;写一个可以拖到的悬浮窗&#xff0c;这个悬浮窗里有TextView&#xff0c;在拖到某个位置后&#xff0c;再调用TextView的setText方法&#xff0c;会发现出现了一个窗口动画&#xff0c;悬浮窗跳到了起始位置&#xff0c;从开…...

【从零开始学习计算机科学】数字逻辑(四)数字系统设计

【从零开始学习计算机科学】数字逻辑(四)数字系统设计 数字系统设计硬件描述语言 HDL(Hardware Description Language)Verilog HDL 的起源与发展HDL 软核、固核和硬核的重用HDL 的应用数字系统设计实现数字系统设计 一个数字集成电路的可以从不同的层次(系统级、算法级、…...

QT 作业 C++ day5

作业 代码 MyQThread.h class MyThread : public QThread {Q_OBJECT public:MyThread(QObject *parent nullptr); protected:void run() override; signals://向ui界面发送的 "复制进度" 的信号void copy_process_signal(int index); public slots:// "复…...

UE5 BaseEditorSettings.ini加载原理与配置生效机制

1. 为什么你改了BaseEditorSettings.ini却没生效&#xff1f;——从UE5编辑器启动流程讲起很多人在UE5项目里折腾半天&#xff0c;把BaseEditorSettings.ini文件翻来覆去改了十几遍&#xff0c;重启编辑器后发现&#xff1a;缩放比例还是不对、网格间距没变、甚至“启用实时预览…...

用Python和MNE库玩转BCI Competition IV 2a脑电数据集:从数据加载到可视化全流程

用Python和MNE库玩转BCI Competition IV 2a脑电数据集&#xff1a;从数据加载到可视化全流程当你第一次接触脑电信号处理时&#xff0c;面对原始数据文件可能会感到无从下手。BCI Competition IV 2a数据集作为脑机接口领域的经典基准数据&#xff0c;包含了9名受试者四种运动想…...

雪球网md5__1038参数逆向解析与Node.js复现

1. 这不是“破解”&#xff0c;而是对前端加密逻辑的常规逆向还原你打开雪球网任意一只股票详情页&#xff0c;F12 打开开发者工具&#xff0c;切到 Network 面板&#xff0c;刷新页面——很快就能在 XHR 请求里捕获到类似这样的接口&#xff1a;https://xueqiu.com/stock/cube…...

配置OpenClaw Agent使用Taotoken作为后端模型提供商

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 配置OpenClaw Agent使用Taotoken作为后端模型提供商 基础教程类&#xff0c;指导希望使用OpenClaw等Agent工具的开发者&#xff0c…...

基于ATtiny84的智能冰箱监控器:低功耗温度与门状态监测方案

1. 项目概述&#xff1a;一个装在树莓派盒子里的智能冰箱管家如果你家里有台老冰箱&#xff0c;或者对食物储存温度特别在意&#xff0c;总担心冰箱门没关严或者突然断电导致内部升温&#xff0c;那么这个自己动手做的“冰箱看门狗”项目就太适合你了。它本质上是一个高度定制化…...

从开题到定稿零焦虑:okbiye AI 论文写作,帮你把毕业季的 “大山” 变成坦途

okbiye-免费查重复率aigc检测/开题报告/毕业论文/智能排版/文献综述/AI PPT毕业论文 - Okbiye智能写作https://www.okbiye.com/ai/bylw 毕业季的深夜&#xff0c;宿舍台灯下的屏幕亮着刺眼的光&#xff0c;文档里的字数停留在三位数&#xff0c;而 deadline 正一天天逼近。你是…...

从零到上机:我的第一个Quest 3空间锚点应用是如何跑起来的(附完整Unity工程)

从零到上机&#xff1a;我的第一个Quest 3空间锚点应用是如何跑起来的&#xff08;附完整Unity工程&#xff09;第一次戴上Meta Quest 3时&#xff0c;那种虚拟与现实交织的震撼感至今难忘。但作为开发者&#xff0c;更让我着迷的是如何让虚拟物体在真实空间中"记住"…...

ComfyUI-Manager完整指南:如何轻松管理你的AI工作流扩展库

ComfyUI-Manager完整指南&#xff1a;如何轻松管理你的AI工作流扩展库 【免费下载链接】ComfyUI-Manager ComfyUI-Manager is an extension designed to enhance the usability of ComfyUI. It offers management functions to install, remove, disable, and enable various c…...

OpenIPC开源固件:5分钟解锁网络摄像头的终极控制权

OpenIPC开源固件&#xff1a;5分钟解锁网络摄像头的终极控制权 【免费下载链接】firmware Alternative IP Camera firmware from an open community 项目地址: https://gitcode.com/gh_mirrors/fir/firmware 还在为网络摄像头的封闭系统而烦恼吗&#xff1f;想要完全掌控…...

模拟电路实现自主循线机器人:无MCU的硬件逻辑设计

1. 项目概述&#xff1a;用最纯粹的模拟电路&#xff0c;造一台会“思考”的机器人每次看到那些在赛道上灵巧穿梭的循线小车&#xff0c;你是不是也手痒&#xff0c;想自己动手做一个&#xff1f;但一听到“单片机”、“编程”、“Arduino”这些词&#xff0c;又觉得门槛太高&a…...