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

排序算法详解

 💎所属专栏:数据结构与算法学习 

💎 欢迎大家互三:2的n次方_

在这里插入图片描述

 

🍁1. 插入排序

🍁1.1 直接插入排序

插入排序是一种简单直观的排序算法,它的原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

以从小到大排序为例

1.从第一个元素开始,可以看作是有序的元素

2.取出下一个元素,与前面已经排好序的元素比较,如果前面的元素大于此元素,就把前面的元素往后移,继续往前找,找到小于或等于的位置进行插入,直到找到最前面

public class InsertSort {public static void main(String[] args) {int[] arr = {5, 1, 2, 4, 3};insertSort(arr);System.out.println(Arrays.toString(arr));}public static void insertSort(int[] arr) {for (int i = 1; i < arr.length; i++) {int tmp = arr[i];int j = i - 1;//比tmp大的元素往后移for (; j >= 0 && arr[j] > tmp; j--) {arr[j + 1] = arr[j];}arr[j + 1] = tmp;}}
}

🍁1.2 希尔排序

希尔排序的基本思想是:

先选定一个整数,把待排序的数据分为多个组,对每一个组内进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。

假设有一个数组arr = [9, 8, 3, 7, 5, 6, 4, 1],我们采用最简单的增量序列(每次减半)来进行希尔排序:

1. 初始增量d = 4,将数组分为4组,每组进行插入排序:[9, 7], [8, 5], [3, 6], [4, 1]

2. 增量d = 2,将数组分为2组,每组进行插入排序:[7, 5, 6, 1], [9, 8, 3, 4]

3. 增量d = 1,整个数组作为一组进行插入排序,得到最终结果:[1, 3, 4, 5, 6, 7, 8, 9]

    public static void shellSort(int[] arr) {int gap = arr.length;//增量每次减半while (gap > 1) {gap /= 2;shell(arr, gap);}}private static void shell(int[] arr, int gap) {for (int i = gap; i < arr.length; i++) {int tmp = arr[i];int j = i - gap;//比tmp大的元素往后移for (; j >= 0 && arr[j] > tmp; j -= gap) {arr[j + gap] = arr[j];}arr[j + gap] = tmp;}}

🍁2. 选择排序

🍁2.1 直接选择排序

直接选择排序的思想:

每次从待排序的数据中选择一个最小(最大)的元素放在序列起始位置,直到整个序列元素排序完毕

    public static void selectSort(int[] arr) {for (int i = 0; i < arr.length; i++) {int minIndex = i;for (int j = i + 1; j < arr.length; j++) {if (arr[j] < arr[minIndex]) {minIndex = j;}}swap(arr, i, minIndex);}}

🍁2.2 堆排序

堆排序在上一节中已经有过介绍,这里再简单回顾下,还是以从小到大排序为例,这时我们创建一个大根堆,堆顶元素也就是最大的,把最顶元素和堆尾元素进行交换,接着向下调整,再把堆顶元素和堆尾元素进行交换,也就是排在了上一个最大元素的前面,重复此过程,就实现了从小到大排序。

 上一篇回顾

    public static void heapSort(int[] arr) {createHeap(arr);int end = arr.length - 1;while (end > 0) {//堆顶和end元素互换swap(arr, 0, end);//向下调整siftDown(arr, 0, end);end--;}}public static void createHeap(int[] arr) {for (int parent = (arr.length - 1 - 1) / 2; parent >= 0; parent--) {siftDown(arr, parent, arr.length);}}private static void siftDown(int[] arr, int parent, int length) {int child = 2 * parent + 1;while (child < length) {if (child + 1 < length && arr[child] < arr[child + 1]) {child += 1;}if (arr[child] > arr[parent]) {swap(arr, child, parent);child = 2 * child + 1;} else {break;}}}

🍁3. 交换排序

🍁3.1 冒泡排序

冒泡排序是一种简单的排序算法,它重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。

比较每一对相邻的元素:如果第一个比第二个大(升序排序),就交换它们两个,这步做完后,最后的元素会是最大的数。

持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

    public static void bubbleSort(int[] arr) {for (int i = 0; i < arr.length - 1; i++) {boolean flag = false;for (int j = 0; j < arr.length - 1 - i; j++) {if (arr[j] > arr[j + 1]) {swap(arr, j, j + 1);flag = true;}}//如果这一趟没有交换任何一对元素,表示已经排好序了if(!flag){break;}}}

 这里做了一个优化,如果给出的数据只有一对数据不符合顺序,那么交换这对数据之后就不用再重复后续的程序了,所以直接结束循环即可,在一些情况下,通过这种优化,冒泡排序的时间复杂度可以达到O(n)

🍁3.2 快速排序

首先把0索引的位置当作基准数,定义两个指针,先将右指针从数组末尾开始往前找,遇到比基准数小的停下来,左指针从1索引开始往后找,遇到比基准数大的停下来,交换左右指针的数,重复此过程,直到左右指针相遇,此时和基准数交换,就可以把基准数排到正确的位置,此时左边都是比基准数小的,右边都是比基准数大的,再依次递归基准数左边部分和右边部分,就实现了排序

注意:一定要先从右边开始找比基准数小的,先从左边开始就无法达到效果

    public static void quickSort(int[] arr, int start, int end) {if (start >= end) {return;}int mid = part(arr, start, end);quickSort(arr, start, mid - 1);quickSort(arr, mid + 1, end);}public static int part(int[] arr, int i, int j) {int tmp = arr[i];int tmpStart = i;while (i < j) {while (i < j && arr[j] >= tmp) {j--;}while (i < j && arr[i] <= tmp) {i++;}swap(arr, i, j);}swap(arr, tmpStart, i);return i;}

 关于基准数归位的过程还有一种优化的方法,由于上面使用了大量的交换,也会浪费一些时间

    public static int part1(int[] arr, int i, int j) {int tmp = arr[i];while (i < j) {while (i < j && arr[j] >= tmp) {j--;}arr[i] = arr[j];while (i < j && arr[i] <= tmp) {i++;}arr[j] = arr[i];}arr[i] = tmp;return i;}

也就是先把基准数拿出来,这时就留出来一个空位,接着从右边找比基准数小的,填上空位,再从左边找比基准数大的,再填上右边的空位,以此类推

还有一个优化的点是,输入数组已经是有序(升序或降序)的,或者每次划分(partition)操作都选择到最小(或最大)的元素作为基准(pivot),导致每次划分只将一个元素移到它最终的位置上,而其他所有元素都留在原数组的另一侧。这种情况下,每次划分后的递归调用处理的子数组大小几乎相同,递归的深度接近n,导致总的比较和交换次数接近n^2。

 下面通过三数取中法来更换基准数进行优化

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

 获取中位数之后进行交换:

 另一个可以优化的是,由于快速排序的递归是一棵二叉树,每一层都是指数级的增长,所以最后两层会有很多递归需要走,但此时元素也趋于有序,就可以调用插入排序

        if(end - start + 1 <= 3){insertSort(arr,start,end);return;}

非递归实现

递归需要一直在栈上开辟空间,容易造成栈溢出,这里我们直接通过栈来进行非递归的实现

    public static void yquickSort(int[] arr,int start,int end){Stack<Integer> stack = new Stack<>();int pivot = part1(arr,start,end);if(pivot > start + 1){stack.push(start);stack.push(pivot - 1);}if(pivot < end - 1){stack.push(pivot + 1);stack.push(end);}while(!stack.isEmpty()){end = stack.pop();start = stack.pop();pivot = part1(arr,start,end);if(pivot > start + 1){stack.push(start);stack.push(pivot - 1);}if(pivot < end - 1){stack.push(pivot + 1);stack.push(end);}}}

 🍁4. 归并排序

归并排序主要利用了分治法,先使每个子序列有序,再使子序列段间有序,组后合并分解的子序列

 首先把要排序的数组依次分解,直到两两一组,之后开始合并,合并的过程也就是把两个有序数组再合并为一个有序数组的过程,每次取出两个数组的较小者存入合并的数组中,最终合并为一整个数组

    public static void mergeSort(int[] arr, int left, int right) {if (left >= right) return;int mid = (left + right) / 2;mergeSort(arr, left, mid);mergeSort(arr, mid + 1, right);merge(arr, left, mid, right);}//也就是合并两个有序数组public static void merge(int[] arr, int left, int mid, int right) {int[] tmp = new int[right - left + 1];int k = 0;int s1 = left;int s2 = mid + 1;//将分解后的两个数组每次取出最小值放在tmp数组中while (s1 <= mid && s2 <= right) {if (arr[s1] >= arr[s2]) {tmp[k++] = arr[s2++];} else {tmp[k++] = arr[s1++];}}while (s1 <= mid) {tmp[k++] = arr[s1++];}while (s2 <= right) {tmp[k++] = arr[s2++];}for (int i = 0; i < k; i++) {arr[left + i] = tmp[i];}}

 非递归实现归并排序

 非递归实现也就是通过循环来模拟递归,依次合并数组,gap取

    public static void mergeSortNor(int[] arr) {int gap = 1;while (gap < arr.length) {for (int i = 0; i < arr.length; i += gap * 2) {int left = i;int mid = i + gap - 1;if (mid >= arr.length) {mid = arr.length - 1;}int right = mid + gap;if (right >= arr.length) {right = arr.length - 1;}merge(arr, left, mid, right);}gap *= 2;}}

🍁5. 计数排序

计数排序是一种基于非比较的排序算法,计数排序的主要特点是通过统计每个元素出现的次数,来确定每个元素在排序后数组中的位置,从而实现排序。

 计算出以上数据出现的次数之后,再根据次数进行遍历,就可以达到排序的效果

    public static void countSort(int[] arr) {int maxVal = arr[0];int minVal = arr[0];//找到最大值和最小值for (int i = 0; i < arr.length; i++) {if (arr[i] > maxVal) {maxVal = arr[i];}if (arr[i] < minVal) {minVal = arr[i];}}int[] tmp = new int[maxVal - minVal + 1];//开始计数for (int i = 0; i < arr.length; i++) {tmp[arr[i] - minVal]++;}int index = 0;//将数据赋值给数组for (int i = 0; i < tmp.length; i++) {while (tmp[i]-- != 0) {arr[index] = i + minVal;index++;}}}

在这里插入图片描述

相关文章:

排序算法详解

​ &#x1f48e;所属专栏&#xff1a;数据结构与算法学习 &#x1f48e; 欢迎大家互三&#xff1a;2的n次方_ &#x1f341;1. 插入排序 &#x1f341;1.1 直接插入排序 插入排序是一种简单直观的排序算法&#xff0c;它的原理是通过构建有序序列&#xff0c;对于未排序数…...

vxe-table树形结构使用setCheckboxRow卡顿--已解决

项目场景&#xff1a; vxe-table树形结构使用setCheckboxRow进行部分节点选中 问题描述 vxe-table树形结构使用setCheckboxRow&#xff0c;在数据较多时卡顿 原因分析&#xff1a; setCheckboxRow内部进行了多次的循环遍历&#xff0c;导致速度慢 解决方案&#xff1a; 设…...

配置错误和 IAM 弱点是云安全的主要隐患

根据云安全联盟发布的《2024 年云计算最大威胁》报告&#xff0c;通常与云服务提供商 (CSP) 相关的传统云安全问题的重要性正在持续下降。 配置错误、IAM 弱点和 API 风险仍然至关重要 这些发现延续了 2022 年报告中首次发现的轨迹&#xff0c;同时&#xff0c;诸如错误配置的…...

Redis系列之Redis Cluster

概述 Redis 2.8版本发布稳定版Redis Sentinel&#xff0c;不过Sentinel集群版存在一些问题&#xff1a; 高可用性&#xff1a;Sentinel集群对Redis既有的主从集群提供有限的高可用保障&#xff1b;在线扩容&#xff1a;节点下线&#xff0c;触发选举&#xff0c;选举涉及两个…...

网站证书过期导致WordPress后台无法登录问题解决,页面样式丢失

1、首先打开网站目录文件\wp-includes\functions.php&#xff0c;找到代码&#xff0c;应该就是就在在第8行。 require( ABSPATH . WPINC . /option.php ); 在下面添加以下代码&#xff0c;作用就是把http替换为https add_filter(script_loader_src, agnostic_script_loader…...

LeetCode刷题笔记第191题:位1的个数

LeetCode刷题笔记第191题&#xff1a;位1的个数 题目&#xff1a; 想法&#xff1a; 通过位运算判断二级制形式中有多少个1&#xff0c;代码及解释如下&#xff1a; class Solution:def hammingWeight(self, n: int) -> int:return sum(1 for i in range(32) if n & …...

C语言—函数栈帧

函数&#xff0c;一般都有返回值&#xff0c;函数名&#xff0c;参数&#xff0c;再下来还有什么mian函数&#xff0c;函数写出来就是要被调用的&#xff0c;上面图片上的代码&#xff0c;main函数和myadd函数&#xff0c;都要在自己的栈结构什么形成自己的栈&#xff0c;可以帮…...

IDEA 2022.1.4用前需知

目录 一、配置国内源 二、正确再次创建新项目方式 IDEA 2022.1.4下载地址 一、配置国内源 1、查看本地仓库地址 2、设置国内源-添加Setting.xml文件内容 3、修改目录&#xff08;考虑到当前硬盘空间大小&#xff0c;英文目录名&#xff09; 1&#xff09;创建你要移动过去…...

Python数据可视化案例——折线图

目录 json介绍&#xff1a; Pyecharts介绍 安装pyecharts包 构建一个基础的折线图 配置全局配置项 综合案例&#xff1a; 使用工具对数据进行查看 &#xff1a; 数据处理 json介绍&#xff1a; json是一种轻量级的数据交互格式&#xff0c;采用完全独立于编程语言的文…...

Ubuntu虚拟机安装及汉化

一、安装 1.勾选典型(推荐)(T)——点击下一步 2.点击浏览找到光盘映像文件打开&#xff08;此文件很重要安装好后安装包不要卸载&#xff0c;放在不容易被删除的地方&#xff09;——点击下一步 3.将信息补充完整——点击下一步 4.点击浏览选择要将虚拟机安装在哪个路径&…...

记2024-08原生微信小程序开发

继2024.08 最近需要开发一个微信小程序的一个功能模块&#xff0c;但是之前在学的时候都是好几年前的东东了&#xff0c;然后重新快速过了一遍b站大学的教程&#xff0c;这篇文章就是基于教程进行的一些总结&#xff0c;和自己开发过程当中使用到的一些点和一些技巧什么的吧。 …...

嵌入式linux系统镜像制作day1

点击上方"蓝字"关注我们 01、前言 嵌入式设备&#xff08;例如心电图检测仪&#xff0c;售票系统等&#xff09;。尽管&#xff0c;嵌入式设备像那些智能手机一样&#xff0c;绝大多数都使用同样的硬件和软件&#xff0c;包括系统芯片SoC、储存、连接和多媒体接口、…...

【相机与图像】2. 相机内外参的标定的代码示例

1 摄像头内参的标定 【相机标定具体操作】 使用将要标定的摄像头&#xff0c;以不同的角度采集棋盘格&#xff0c;要保证视野内出现完整的棋盘格。采集图片数量约15张左右即可。 以11*8的棋盘格为例&#xff0c;具体流程如下&#xff1a; step 1. 设置棋盘格3D点&#xff1b;通…...

重启人生计划-拒绝内耗

&#x1f973;&#x1f973;&#x1f973; 茫茫人海千千万万&#xff0c;感谢这一刻你看到了我的文章&#xff0c;感谢观赏&#xff0c;大家好呀&#xff0c;我是最爱吃鱼罐头&#xff0c;大家可以叫鱼罐头呦~&#x1f973;&#x1f973;&#x1f973; 如果你觉得这个【重启人生…...

盘点电脑开机慢的几大高频原因

常规的话一台电脑正常我们都要用个2年以上的时间,有的可能更长,5年的都有,而电脑目前占多数的主流操作系统就是微软的Windows。那么随着使用年限的增加,无论是系统还是电脑硬件,都会随着使用次数和使用的时间的增加而有损耗,系统软件上就是文件越来越臃肿,空间越来越小,…...

2-64 基于matlab的Consensus-Based Bundle Algorithm (CBBA)算法

基于matlab的Consensus-Based Bundle Algorithm (CBBA)算法&#xff0c;可为异构代理网络上的多代理多任务分配问题提供良好的解决方案。支持具有有效时间窗口的任务、异构代理-任务兼容性要求&#xff0c;以及平衡任务奖励和燃料成本的得分函数。奖励和燃料成本的分数函数。程…...

Win10 去掉桌面右上角 了解有关此图片的信息

1. 进入注册表 Win R运行regedit 2. 找到以下路径 计算机\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel 3. 新建 DWORD&#xff08;32位&#xff09;值&#xff08;D&#xff09; 右击 NewStartPanel新建 DWORD…...

tcpdump入门——抓取三次握手数据包

1. 使用docker启动一个tcp应用 参考&#xff1a;https://blog.csdn.net/LONG_Yi_1994/article/details/141175526 2. 获取容器id docker ps |grep gochat 3. 获取容器的 PID 首先&#xff0c;你需要获得容器的进程 ID&#xff08;PID&#xff09;。可以使用 docker inspect…...

漏洞复现-GitLab任意读取文件(CVE-2023-2825)

1.漏洞描述 GitLab是一个用于仓库管理系统的开源项目&#xff0c;其使用Git作为代码管理工具&#xff0c;可通过Web界面访问公开或私人项目。据悉,该漏洞影响 GitLab社区版(CE)和企业版(EE)的 16.0.0 版本,其它更早的版本几乎都不受影响。 该漏洞存在于GitLab CE/EE版本16.0.0…...

二叉树——9.找树左下角的值

力扣题目链接 给定一个二叉树&#xff0c;在树的最后一行找到最左边的值。 示例&#xff1a; 输出&#xff1a;7 题干很简单&#xff0c;找到树的最后一行&#xff0c;在该行找到最左边的值&#xff0c;结合完整代码进行分析。 完整代码如下&#xff1a; class Solution:d…...

如何用github制作个人网站

这里整理了一些参考资料。总结来说&#xff0c;如果系统学过html网页制作的话&#xff0c;可以不用看这篇博客了&#xff1b;这里适合于小白&#xff0c;就是那种 没有做过网页、打算以别人优秀的个人主页为框架做网页的小白。 一、简单说明 这是利用github.io来制作网页的&a…...

二.PhotoKit - 相册权限(彻底读懂权限管理)

引言 用户的照片和视频算是用户最私密的数据之一&#xff0c;由于内置的隐私保护功能&#xff0c;APP只有在用户明确授权的前提下才能访问用户的照片库。从iOS14 开始&#xff0c;PhotoKit进一步增强了用户的隐私控制&#xff0c;用户可以选择指定的照片或者视频资源的访问权限…...

二叉树------最小堆,最大堆。

什么是最小堆&#xff1a; 堆是一种二叉树&#xff0c;最小堆中所有父亲节点的值都要比自己的子节点的值要小。而根节点称为堆顶。根据定义我们可以得到堆中最小元素就在堆顶。&#xff08;节点左上角是编号&#xff0c;内部是元素值&#xff09; 假设该图中的堆顶元素是24呢&a…...

预约功能的知识整理

前置知识 如果项目为小程序的开发项目中&#xff1a; 我们确定数据库中有的字段有: 预约人姓名、手机号、家人名称、预约时间 根据我们的经定一表必须要有的6个字段&#xff1a; 主键、创建时间、修改时间、创建人、修改人、备注 使用我们现在有的字段为&#xff1a; 主键…...

Linux的常用操作-02

一&#xff1a;Linux的系统目录结构 /bin bin是ary的缩写&#xff0c;这个目录存放着最经常用的命令 /boot&#xff1a;这里存放的是启动Linux时使用的一些核心文件&#xff0c;包括一些连接文件以及镜像文件。 /dev&#xff1a;dev是Device(设备)的缩写,该目录下存放的是Lin…...

Android Studio 连接手机进行调试

总所周知&#xff0c;Android Studio里的虚拟手机下载后又大又难用。不如直接连手机用。本篇文章主要内容为Android Studio怎么连接手机进行程序调试。 1. 在AndroidSDK中下载google USB Driver: 2. 连接手机&#xff1a; 进入电脑设备管理器界面。并点开便携设备&#xff0c…...

Vue3项目创建及相关配置

Vue是一种用于构建用户界面的JavaScript框架。它采用了一种称为MVVM&#xff08;Model-View-ViewModel&#xff09;的架构模式。 MVVM是一种将用户界面与业务逻辑和数据分离的设计模式。它包括三个部分&#xff1a; Model&#xff08;模型&#xff09;&#xff1a;表示应用程序…...

【Python】Python中一些有趣的用法

Python是一种非常灵活和强大的编程语言&#xff0c;它有很多有趣的用法&#xff0c;以下是一些例子&#xff1a; 一行代码实现FizzBuzz&#xff1a; print(\n.join([FizzBuzz[i%3*4:i%5*8:-1] or str(i) for i in range(1, 101)]))使用列表推导式生成斐波那契数列&#xff1a; …...

RCE复现问题和研究

目录 先了解一些常见的知识点 PHP常见命令执行函数 call_user_func eval&#xff08;&#xff09; call_user_func_array array_filter 实战演练&#xff08;RCE&#xff09;PHP Eval函数参数限制在16个字符的情况下 &#xff0c;如何拿到Webshell&#xff1f; 1、长度…...

MySQL中的索引——适合创建索引的情况

1.适合创建索引的情况 1、字段的数值有唯一性的限制 2、频繁作为 WHERE 查询条件的字段 某个字段在 SELECT 语句的 WHERE 条件中经常被使用到&#xff0c;那么就需要给这个字段创建索引了。尤其是在数据量大的情况下&#xff0c;创建普通索引就可以大幅提升数据查询的效率。 …...