数据结构-8.Java. 七大排序算法(中篇)

数据结构-8.Java. 七大排序算法(中篇)
本篇博客给大家带来的是排序的知识点, 由于时间有限, 分两天来写, 中篇主要实现后三种排序算法: 冒泡排序,快速排序,下一篇讲 归并排序.
文章专栏: Java-数据结构
若有问题 评论区见
欢迎大家点赞 评论 收藏 分享
如果你不知道分享给谁,那就分享给薯条.
你们的支持是我不断创作的动力 .

1. 冒泡排序

1.1 算法思路

1. 将数组中相邻元素从前往后依次进行比较,如果前一个元素比后一个元素大,则交换,一趟下来后最大元素就在数组的末尾
2. 依次进行上述过程,直到数组中所有的元素都排列好
如下模拟动图:

请添加图片描述

1.2 具体步骤:

1. 定义i = 0, j = 0, i 表示冒泡的趟数, j为起始位置的下标值.
2. 每一趟中遍历未排序数组, 比较[ j ] 与 [ j + 1 ] 的大小, 若[ j ] 较大, 则值交换, 否则 j++.

1.3 代码实现:

// 交换下标分别为 i 和 j 的两个元素publicstaticvoidswap(int[] array,int i,int j){int tmp = array[i]; array[i]= array[j]; array[j]= tmp;}//冒泡排序publicstaticvoidbubbleSort(int[] array){for(int i =0; i < array.length-1; i++){for(int j =0; j < array.length-1-i; j++){if(array[j]> array[j+1]){swap(array,j,j+1);}}}}

1.4 优化冒泡排序:

在这里插入图片描述

如上图, 进行完第i = 2 趟之后,发现数组已经有序了,没有必要再进行下去了.
于是我们可以定义一个标记变量flag = false,如果发生交换就将flag = true.
优化代码如下:

/** * 冒泡排序: * 时间复杂度: * 最坏情况: 没有优化的 O(N^2) * 最好情况: 优化过的O(N) * 空间复杂度: O(1) * 稳定性: 稳定 * @param array */publicstaticvoidbubbleSort(int[] array){for(int i =0; i < array.length-1; i++){boolean flag =false;for(int j =0; j < array.length-1-i; j++){if(array[j]> array[j+1]){swap(array,j,j+1); flag =true;}}if(!flag){//没交换,说明已经有序return;}}}publicstaticvoidswap(int[] array,int i,int j){int tmp = array[i]; array[i]= array[j]; array[j]= tmp;}

1.5 冒泡排序的特性总结:

1. 冒泡排序是一种非常容易理解的排序
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1)
4. 稳定性:稳定

2. 快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法.

2.1 基本思想:

任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

[start,end]为待排序区间.privatestaticvoidquick(int[] array,int start,int end){if(start >= end)return;//start下标 与 end相遇说明基准值已经排好序了.//拿到基准值下标,将原数组划分为两个子序列.int pivot =partition(array,start,end);//递归排左序列quick(array,start,pivot-1);//递归排右序列quick(array,pivot+1,end);}

上述为快速排序递归实现的主框架,发现与二叉树前序遍历规则非常像,在写快速排序递归框架时可想想二叉树前序遍历规则即可快速写出来,后序只需分析如何按照基准值来对区间中数据进行划分的方式即可。

将区间按照基准值划分为左右两半部分的常见方式有:

2.2 具体实现:

2.2.1 Hoare版:

1. 将首元素作为关键字key, 先从数组最右边right开始,向左找到比key小的数 i .再从左left开始向右找比key大的数 j . i 和 j 交换.
2. 重复第一步直到left >= right.
3. 当left = right 时 交换 [left] 和 key. 此时left 和 right所在的位置即为基准值下标.
具体过程如下图:

代码如下:

publicstaticvoidswap(int[] array,int i,int j){int tmp = array[i]; array[i]= array[j]; array[j]= tmp;}privatestaticintpartition(int[] array,int left,int right){int key = array[left];int i = left;while(left < right){//1. 为什么要先走右边而不是先走左边?//如果先走右边,最终pivot下标处的值一定比key(头元素)小,自己画图便知.//如果先走左边,最终pivot下标处的值一定比key(头元素)大,自己画图便知.//2. 为什么要 >= 和 <= 而不能是>和< 等号必须取,因为left一开始就在key下标,// 判断的时候,a[left]<key为false,a[right]>key为也为false,所以等号必取,否则left和right一开始就不动.while(left < right && array[right]>= key){//如果所有元素确实都小于key,那么会越界,所以加上left<right条件 right--;}while(left < right && array[left]<= key){ left++;}//走到这里说明right的元素值小于key,left的元素大于key,那么交换即可swap(array,left,right);}//走到这里,left=right,key与pivot交换,但是原先的key位置left已经改变了,//所以再left变之前先将其保存下来.swap(array,i,left);//返回基准值的下标.return left;}

2.2.2 挖坑法:

1. 定义变量key=[left], 从右边right开始往左,找到比key小的第一个数 tmpR,tmpR的下标为 i , [left] = tmpR, 将 left位置覆盖, 此时 i 位置可看作一个空出来的坑位.
2. 再从左边left开始往右, 找到一个比key大的元素 tmpL, tmpL的位置为 j , [right] = tmpL, 此时 j 也空出来了.
3. 重复以上两步, 直到left >= right.
具体过程如下图:

在这里插入图片描述


代码如下:

publicstaticvoidquickSort(int[] array){quick2(array,0,array.length-1);}privatestaticvoidquick2(int[] array,int start,int end){if(start >= end)return;int pivot =partition2(array,start,end);quick2(array,start,pivot-1);quick2(array,pivot+1,end);}privatestaticintpartition2(int[] array,int left,int right){int key = array[left];while(left < right){while(left < right && array[right]>= key){ right--;}//从右边开始第一个比key小的元素覆盖[left] array[left]= array[right];while(left < right && array[left]<= key){ left++;}//从左边开始第一个比key大的元素覆盖空位. array[right]= array[left];}//key填补最终的空位,此时left=right array[left]= key;return left;}

2.2.3 前后指针法(了解):

自行画图即可:

在这里插入图片描述


代码如下:

publicstaticvoidswap(int[] array,int i,int j){int tmp = array[i]; array[i]= array[j]; array[j]= tmp;}privatestaticintpartition(int[] array,int left,int right){int prev = left ;int cur = left+1;while(cur <= right){if(array[cur]< array[left]&& array[++prev]!= array[cur]){swap(array,cur,prev);} cur++;}swap(array,prev,left);return prev;}

递归快排模拟动图

在这里插入图片描述

2.2.4 快速排序的递归优化:

1. 当数据量很大的待排序数组本身是有序的时候, 递归快排会出现单分支的情况, 此时递归的次数最多, 所需的空间也最多, 怎么减小空间消耗呢?
在递归快排之前, 先对待排序数组进行三数取中操作
三数取中: 拿出数组首尾中三个元素值, 取这三个数的中间值与0下标的值进行交换.

在这里插入图片描述


在这里插入图片描述


三数取中代码实现:

//三数取中.privatestaticintmidOfThree(int[] array,int left,int right){int mid =(left+right)/2;if(array[right]> array[left]){if(array[mid]< array[left]){return left;}elseif(array[mid]> array[right]){return right;}else{return mid;}}else{if(array[mid]> array[left]){return left;}elseif(array[mid]< array[right]){return right;}else{return mid;}}}publicstaticvoidquickSort(int[] array){quick2(array,0,array.length-1);}privatestaticvoidquick2(int[] array,int start,int end){if(start >= end)return;//三数取中,优化空间复杂度int index =midOfThree(array,start,end);//三数取中,swap(array,index,start);//start下标数与中间数交换,确保start下标 是中间大的数.int pivot =partition2(array,start,end);quick2(array,start,pivot-1);quick2(array,pivot+1,end);}privatestaticintpartition2(int[] array,int left,int right){int key = array[left];while(left < right){while(left < right && array[right]>= key){ right--;}//从右边开始第一个比key小的元素覆盖[left] array[left]= array[right];while(left < right && array[left]<= key){ left++;}//从左边开始第一个比key大的元素覆盖空位. array[right]= array[left];}//key填补最终的空位,此时left=right array[left]= key;return left;}

2.递归快排的第二次优化:

在这里插入图片描述
//第二次优化快速排序.//递归得差不多了之后,使用直接插入排序效率可能会更高.publicstaticvoidinsertSortRange(int[] array,int begin,int end){for(int i = begin+1; i <= end; i++){int tmp = array[i];int j = i-1;for(; j >= begin; j--){if(array[j]> tmp){// >= 会变成不稳定的 array[j+1]= array[j];}else{//array[j+1] = tmp; 执行了一次break;}}//当j=-1时,a[j+1]=tmp这条语句没被执行,所以再写一次 array[j+1]= tmp;//执行了两次, 故把第一次屏蔽.}}publicstaticvoidquickSort(int[] array){quick2(array,0,array.length-1);}privatestaticvoidquick2(int[] array,int start,int end){if(start >= end)return;//第二次优化快速排序法.减少递归的次数,但是不一定是优化,可能反而会变慢.if(end - start+1<=15){//直接插入排序insertSortRange(array,start,end);return;}//三数取中,优化空间复杂度int index =midOfThree(array,start,end);//三数取中,swap(array,index,start);//start下标数与中间数交换,确保start下标 是中间大的数.int pivot =partition2(array,start,end);quick2(array,start,pivot-1);quick2(array,pivot+1,end);}

以递归快排的整体代码如下(以Hoare版为例):

在这里插入图片描述
publicstaticvoidquickSort(int[] array){quick2(array,0,array.length-1);}privatestaticvoidquick2(int[] array,int start,int end){if(start >= end)return;//第二次优化快速排序法.减少递归的次数,但是不一定是优化,可能反而会变慢.if(end - start+1<=15){//直接插入排序insertSortRange(array,start,end);return;}//三数取中,优化空间复杂度int index =midOfThree(array,start,end);//三数取中,swap(array,index,start);//start下标数与中间数交换,确保start下标 是中间大的数.int pivot =partition2(array,start,end);quick2(array,start,pivot-1);quick2(array,pivot+1,end);}privatestaticintpartition2(int[] array,int left,int right){int key = array[left];while(left < right){while(left < right && array[right]>= key){ right--;}//从右边开始第一个比key小的元素覆盖[left] array[left]= array[right];while(left < right && array[left]<= key){ left++;}//从左边开始第一个比key大的元素覆盖空位. array[right]= array[left];}//key填补最终的空位,此时left=right array[left]= key;return left;}//递归快排的第一次优化: 三数取中.privatestaticintmidOfThree(int[] array,int left,int right){int mid =(left+right)/2;if(array[right]> array[left]){if(array[mid]< array[left]){return left;}elseif(array[mid]> array[right]){return right;}else{return mid;}}else{if(array[mid]> array[left]){return left;}elseif(array[mid]< array[right]){return right;}else{return mid;}}}//第二次优化快速排序.//递归得差不多了之后,使用直接插入排序效率更高.publicstaticvoidinsertSortRange(int[] array,int begin,int end){for(int i = begin+1; i <= end; i++){int tmp = array[i];int j = i-1;for(; j >= begin; j--){if(array[j]> tmp){// >= 会变成不稳定的 array[j+1]= array[j];}else{//array[j+1] = tmp; 执行了一次break;}}//当j=-1时,a[j+1]=tmp这条语句没被执行,所以再写一次 array[j+1]= tmp;//执行了两次, 故把第一次屏蔽.}}
快速排序的非递归实现在下篇 ↓
以上就是本篇文章的全部内容啦! 七大排序的难点在于理解, 只要理解了并且自己画图,写代码, 那么相信这一块就没什么太大的问题啦,
感谢观看, 希望我们都能在排序的学习之路上 秒杀"OJ排序题" .

Read more

【CS创世SD NAND征文】为无人机打造可靠数据仓:工业级存储芯片CSNP32GCR01-AOW在飞控系统中的应用实践

【CS创世SD NAND征文】为无人机打造可靠数据仓:工业级存储芯片CSNP32GCR01-AOW在飞控系统中的应用实践

一、引言:无人机时代的数据存储挑战 在无人机(UAV)技术飞速发展的今天,其应用范畴早已突破消费级航拍的界限,深度渗透至测绘勘察、基础设施巡检、精准农业、安防监控乃至国防军事等工业级领域。每一次精准的自动巡航、每一帧高清图像的实时图传、每一条飞行轨迹的忠实记录,都离不开飞控系统这颗"大脑"的精密运算。然而,大脑的决策依赖于记忆与学习,而承担这一"记忆"任务的存储单元,其可靠性直接决定了飞行任务的成败与数据的价值。一次意外的数据丢失或存储故障,不仅可能导致珍贵的测绘数据付诸东流,造成重大的经济损失,甚至可能引发严重的飞行安全事故。因此,为无人机飞控系统选择一款高性能、高可靠的存储芯片,已成为行业设计中不可或缺的关键一环。 本文将围绕基于全志MR100主控平台与CS创世SD NAND(具体型号:CSNP32GCR01-AOW)构建的新一代无人机飞控存储方案,深入探讨工业级存储芯片如何为高端无人机赋予稳定、可靠的"数据生命线",助力无人机技术在各个领域发挥更大的价值。 二、应用产品介绍:无人机飞控系统——空中机器人的智能核心

By Ne0inhk
用OpenClaw做飞书ai办公机器人(含本地ollama模型接入+自动安装skills+数据可视化)

用OpenClaw做飞书ai办公机器人(含本地ollama模型接入+自动安装skills+数据可视化)

执行git clone https://github.com/openclaw/openclaw克隆项目,执行cd openclaw进入项目 执行node --version看看node的版本是否大于等于22(没有node.js需自行安装),再执行npm install -g pnpm安装作为包管理器,并执行pnpm install安装依赖 首次执行pnpm ui:build构建 Web UI(会先安装 ui/ 目录的依赖) 执行pnpm build构建主程序 执行pnpm openclaw onboard --install-daemon运行配置向导(安装守护进程),完成初始化 按键盘右箭头选择Yes,同样Yes 任选一个模型提供商都行,没有对应的提供商的密钥可以跳过,如果是本地模型选vLLM(需用vLLM框架启动模型,有性能优势,但原生vLLM仅完全支持Linux的cuda)、Custom Provider(可以连接任何 OpenAI 或 Anthropic 兼容的端点,

By Ne0inhk
低成本开源!ESP32轮腿机器人实战

低成本开源!ESP32轮腿机器人实战

低成本开源!ESP32-S3轮腿机器人实战:自平衡+身高调节,语音控制在路上 作为机器人爱好者,你是否想亲手打造一款兼具灵活性与功能性的轮腿机器人,却担心成本过高、技术门槛难跨越?今天给大家分享一个超实用的开源项目——L在这里插入代码片eTian-robot2,一款基于ESP32-S3的低成本轮腿机器人,不仅实现了自平衡、身高调节、无线控制等核心功能,还开源了全部PCB、原理图和代码,新手也能跟着复刻! 一、项目初衷:从模仿到创新,解锁轮腿机器人的更多可能 这款机器人的灵感来源于大名鼎鼎的Ascento机器人,最初的设计目标是通过实践学习控制算法,最终实现酷炫的跳跃功能。虽然受限于理论知识储备,跳跃功能的建模仿真与实物落地预计要到明年6月才能完成,但目前已成功实现自平衡、身高调节、无线控制三大核心功能,后续还将迭代离线语音控制,性价比直接拉满! 更值得一提的是,项目完全开源,从PCB设计图、原理图、三维模型到BOM清单,所有资源都能免费获取,大大降低了制作门槛,让更多爱好者能参与到轮腿机器人的研发与优化中。 二、硬件篇:低成本选材,兼顾性能与性价比 1. 核心主控与P

By Ne0inhk
Flutter 三方库 whatsapp_bot_flutter 自动化社交矩阵鸿蒙多维协同适配指引:横向打通设备生态通信拦截管道、打造多模态实体机器人事件分发-适配鸿蒙 HarmonyOS ohos

Flutter 三方库 whatsapp_bot_flutter 自动化社交矩阵鸿蒙多维协同适配指引:横向打通设备生态通信拦截管道、打造多模态实体机器人事件分发-适配鸿蒙 HarmonyOS ohos

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 whatsapp_bot_flutter 自动化社交矩阵鸿蒙多维协同适配指引:横向打通设备生态通信拦截管道、打造多模态实体机器人事件分发极限制化与消息群发堡垒 前言 在 OpenHarmony 的企业级服务助理、自动化通知分发系统或者是个人智能机器人应用中,如何打通全球主流的即时通讯链路是开发者必须跨越的门槛。whatsapp_bot_flutter 库为 Flutter 开发者提供了一套基于协议或 Web 端桥接的自动化社交机器人方案。本文将带大家在鸿蒙端实战适配该库,探索社交自动化的无限可能。 一、原直线性 / 概念介绍 1.1 基础原理/概念介绍 whatsapp_bot_flutter 的核心逻辑是基于 基于流的会话状态机与加密协议握手 (Encryption Protocol Handshake)。它模拟官方客户端的连接逻辑,通过与指定网关建立受保护的 WebSocket 链路,并实时监听业务事件流(消息、

By Ne0inhk