数据结构之常见排序算法

文章目录

    • 1.排序概念
    • 2.10种排序比较
    • 3.排序算法
    • 3.1直接插入排序(元素越有序,越高效)
    • 3.2希尔排序序( 缩小增量排序 )
    • 3.3直接选择排序
    • 3.5堆排序
    • 3.6冒泡排序
    • 3.8快速排序 递归实现(无序使用最好)
      • 3.8.1挖坑法 (建议用这个 找基准)
      • 3.8.2Hoare法
      • 3.8.3三数取中法 优化排序算法
    • 3.9 快速排序 非递归实现
    • 4.0归并排序
    • 4.1计数排序
    • 4.2基数排序
    • 4.3桶排序

1.排序概念

排序:就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。

稳定性:假设一组序列中,有两个相同的元素,在排序之后,两个相同元素的前后顺序颠倒了,说明这个排序算法是不稳定的,反之。

在这里插入图片描述

2.10种排序比较

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

3.排序算法

3.1直接插入排序(元素越有序,越高效)

思路:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到
一个新的有序序列 。

在这里插入图片描述

 /** 时间复杂度:最坏:o(N^2)*            最好:O(N)   数据都有序,j不会往前走,直接break*               当数据趋于有序的时候,排序速度非常快*               一般场景就是数据基本有序,建议使用直接插入排序* 空间复杂度:O(1)* 稳定性:稳定*    如果一个排序是稳定的,那么就可以实现为不稳定的,*     但是如果一个排序本身就是不稳定的,那么不能变成稳定的** */
 //插入排序public static void insertSort(int[] array) {for (int i = 1; i < array.length; i++) {int temp = array[i];int j = i - 1;for (; j >= 0; j--) {if (array[j] > temp) {array[j + 1] = array[j];} else {break;}}array[j + 1] = temp;}}

3.2希尔排序序( 缩小增量排序 )

基本思想:希尔排序是选的一个增量值分组,然后对每组使用直接插入排序算法排序,随着增量值减小,每组包含的值也越来越多,当增量值减为1时,整个序列在一组内排序

在这里插入图片描述

上代码:

 //希尔排序   每组进行插入排序/*时间复杂度:O(N^1.3) - O(N ^ 1.5)*空间复杂度:o(1)* 稳定性:不稳定的排序**/public static void shellSort(int[] array) {int gap = array.length;while (gap > 1) {gap /= 2;shell(array, gap);}}//插入排序 ------->gappublic static void shell(int[] array, int gap) {for (int i = gap; i < array.length; i++) {int temp = array[i];int j = i - gap;for (; j >= 0; j -= gap) {if (array[j] > temp) {array[j + gap] = array[j];} else {break;}}array[j + gap] = temp;}}

3.3直接选择排序

基本思想:在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
在这里插入图片描述

(1)第一种

/*选择排序*时间复杂度:O(N^2)* 空间复杂度:o(1)* 稳定性:不稳定** */public static void selectSort(int[] arrays) {for (int i = 0; i < arrays.length; i++) {int minIndex = i;for (int j = i + 1; j < arrays.length; j++) {if (arrays[j] < arrays[minIndex]) {minIndex = j;}}swap(arrays, i, minIndex);}}private static void swap(int[] array, int i, int j) {int tmp = array[i];array[i] = array[j];array[j] = tmp;}

(2)前面是用了一个minIndex去找最小值然后交换,最后排序

那么如果用两个一个minIndex去找最小值,一个maxIndex去找最大值,然后用left指向前,right指向后,比如说是升序那就minIndex和left交换,maxIndex和right交换,这样效率应该是比第一种方法要快

在这里插入图片描述

3.5堆排序

基本思想:堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。

在这里插入图片描述

/*堆排序*时间复杂度:O(Nlogn)* 空间复杂度:O(1)* 稳定性:不稳定* */public static void heapSort(int[] array) {createBigHeap(array);int end = array.length - 1;while (end > 0) {swap(array, 0, end);shiftDown(array, 0, end);end--;}}private static void createBigHeap(int[] array) {for (int parent = (array.length - 1 - 1) / 2; parent >= 0; parent--) {shiftDown(array, parent, array.length);}}private static void shiftDown(int[] array, int parent, int len) {int child = parent * 2 + 1;//至少有左孩子while (child < len) {if (child + 1 < len && array[child] < array[child + 1]) {//有右孩子,且右孩子最大child++;}if (array[child] > array[parent]) {swap(array, child, parent);parent = child;child = 2 * parent + 1;} else {//child比parent小,不需要调整break;}}}

3.6冒泡排序

基本思想:冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。重复的进行上面的步骤,直到没有再需要交换的
在这里插入图片描述

 /** 冒泡排序* 时间复杂度:不考虑优化 O(N^2),优化后,o(N)*空间复杂度o(1)* 稳定性:稳定* */public static void bubbleSort(int[] array) {for (int i = 0; i < array.length; 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 == false) {return;}}}private static void swap(int[] array, int i, int j) {int tmp = array[i];array[i] = array[j];array[j] = tmp;}

3.8快速排序 递归实现(无序使用最好)

基本思想:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,
左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,
然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
在写递归的过程中可联想 二叉树的前序遍历 找基准值 进行区间划分

 //快速排序/** 时间复杂度:N*logN*            最好情况:N*logN*            最坏情况:N^2   有序   倒序   (只有左树或者只有右树)* 空间复杂度:*            最好情况:logN*            最坏情况:N* 稳定性:不稳定** *///1.递归实现快速排序public static void quickSort(int[] array) {quick(array, 0, array.length - 1);}private static void quick(int[] array, int start, int end) {//递归结束条件//为什么取大于号  :1,2,3,4   越界if (start >= end) {return;}//基准int pivot = partition(array, start, end);//划分quick(array, start, pivot - 1);quick(array, pivot + 1, end);}//插入排序public static void insertSort2(int[] array, int start, int end) {for (int i = start + 1; i <= end; i++) {int temp = array[i];int j = i - 1;for (; j >= 0; j--) {if (array[j] > temp) {array[j + 1] = array[j];} else {break;}}array[j + 1] = temp;}}

3.8.1挖坑法 (建议用这个 找基准)

必须先走右边,再走左边,这样就不会出现将大的放在前面的情况
在这里插入图片描述

//基准  挖坑法private static int partition(int[] array, int left, int right) {int tmp = array[left];while (left < right) {//left<right   不然会越界//“ = ”必须取,不然会发生死循环:6  23 1  3 6while (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;}

3.8.2Hoare法

在这里插入图片描述

//Hoare法private static int partition2(int[] array, int left, int right) {int tmp = array[left];int i = left;while (left < right) {//为什么要先从右边走,因为左边先停下来就会比tmp大,与i位置交换就会把比tmp大的换到前面while (left < right && array[right] >= tmp) {right--;}while (left < right && array[left] <= tmp) {left++;}swap(array, left, right);}swap(array, left, i);return left;}

3.8.3三数取中法 优化排序算法

在这里插入图片描述

在这里插入图片描述

 //1.递归实现快速排序public static void quickSort(int[] array) {quick(array, 0, array.length - 1);}private static void quick(int[] array, int start, int end) {//递归结束条件//为什么取大于号  :1,2,3,4   越界if (start >= end) {return;}//优化2:解决减少递归的次数if (end - start + 1 <= 14) {//假设有十万条数据,还剩十几条,那么数据基本有序,用插入排序更高效insertSort2(array, start, end);return;}// 三数取中法优化1:不用每一次都用start,而是从start ,mid  ,end中取一个中间数来作为基准然后将它与start位置的数交换位置即可,// 后续不用改变int i = minThree(array, start, end);swap(array, i, start);//基准int pivot = partition2(array, start, end);//划分quick(array, start, pivot - 1);quick(array, pivot + 1, end);}//三数取中法private static int minThree(int[] array, int start, int end) {int mid = (start + end) / 2;if (start < end) {if (mid < start) {return start;} else if (mid > end) {return end;} else {return mid;}} else {if (mid < end) {return end;} else if (mid > start) {return start;} else {return mid;}}}

3.9 快速排序 非递归实现

第一个分析:
在这里插入图片描述
第二个分析:
在这里插入图片描述
上代码:

//2.非递归实现快速排序/** 时间复杂度:O(Nlog n)* 空间复杂度:O(log n)* 稳定性:不稳定* */public static void quickSort2(int[] array) {Deque<Integer> stack = new LinkedList<>();int left = 0;int right = array.length - 1;int pivot = partition(array, left, right);//保证左边与右边都大于两个元素,才可以排序,只有一个元素就不用排序了if (pivot > left + 1) {stack.push(left);stack.push(pivot - 1);}if (pivot < right - 1) {stack.push(pivot + 1);stack.push(right);}while (!stack.isEmpty()) {right = stack.pop();left = stack.pop();pivot = partition(array, left, right);//保证左边与右边都大于两个元素,才可以排序,只有一个元素就不用排序了if (pivot > left + 1) {stack.push(left);stack.push(pivot - 1);}if (pivot < right - 1) {stack.push(pivot + 1);stack.push(right);}}}

4.0归并排序

基本思想:归并排序是建立在归并操作上的一种有效的排序算法,将已有序的子序列合并,得到完全有序的序列;也就是先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

递归实现:
在这里插入图片描述

//归并排序public static void mergeSort(int[] array) {mergeSortFunc(array, 0, array.length - 1);}private static void mergeSortFunc(int[] array, int start, int end) {//和快速排序原因一样if (start >= end) {return;}int mid = (end + start) / 2;//分mergeSortFunc(array, start, mid);mergeSortFunc(array, mid + 1, end);//并merge(array, start, end, mid);}private static void merge(int[] array, int start, int end, int mid) {int s1 = start;int s2 = mid + 1;//tmp数组下标int k = 0;//合并成一个临时数组int[] tmp = new int[end - start + 1];while (s1 <= mid && s2 <= end) {if (array[s1] <= array[s2]) {tmp[k++] = array[s1++];} else {tmp[k++] = array[s2++];}}//s2数组为空时,s1还有数据while (s1 <= mid) {tmp[k++] = array[s1++];}//同样的while (s2 <= end) {tmp[k++] = array[s2++];}//将tmp临时数组数据给原数组for (int i = 0; i < tmp.length; i++) {//这里start+i意思是,不一定start都是0下标开始的array[i + start] = tmp[i];}}

非递归实现归并排序:

  //非递归实现归并排序public static void mergeSort2(int[] array) {int gap = 1;while (gap < array.length) {//i += gap * 2,当前组拍序好后,i往后移动,排序下一组for (int i = 0; i < array.length; i += gap * 2) {int left = i;int mid = i + gap - 1;if (mid >= array.length) {避免越界异常mid = array.length - 1;}int right = mid + gap;//if (right >= array.length) {//避免越界异常right = array.length - 1;}merge(array, left, right, mid);}//间隙为1的每组排序好后,改为间隙为2的每组,重复上述步骤,接着排序下一组,直到gap//超过array.lengh,就排序好了gap *= 2;}}

4.1计数排序

在这里插入图片描述

 //计数排序/** 时间复杂度:O(N+范围)* 空间复杂度:** */public static void countSort(int[] array) {//1遍历数组,找到数组最大值和最小值int min = array[0];int max = array[0];for (int i = 1; i < array.length; i++) {if (array[i] < min) {min = array[i];}if (array[i] > max) {max = array[i];}}//2确定计数数组的长度int len = max - min + 1;int[] count = new int[len];//3遍历array数组,在计数数组中记录数字出现的个数for (int i = 0; i < array.length; i++) {count[array[i] - min]++;}//根据计数数组,将数字按照数组顺序重新返回array数组int index = 0;for (int i = 0; i < count.length; i++) {while (count[i] > 0) {//这里i+min:在array中加上min,反映真实数据array[index] = i + min;index++;count[i]--;}}}

4.2基数排序

基本思想:基数排序是利用分配和收集两种基本操作。
基数排序是一种按记录关键字的各位值逐步进行排序的方法。
此种排序一般适用于记录的关键字为整数类型的情况。所有对于字符串和文字排序不适合。

在这里插入图片描述

  private static void radixSort(int[] arr) {//待排序列最大值int max = arr[0];int exp;//指数//计算最大值for (int anArr : arr) {if (anArr > max) {max = anArr;}}//从个位开始,对数组进行排序for (exp = 1; max / exp > 0; exp *= 10) {//存储待排元素的临时数组int[] temp = new int[arr.length];//分桶个数int[] buckets = new int[10];//将数据出现的次数存储在buckets中for (int value : arr) {//(value / exp) % 10 :value的最底位(个位)buckets[(value / exp) % 10]++;}//更改buckets[i],for (int i = 1; i < 10; i++) {buckets[i] += buckets[i - 1];}//将数据存储到临时数组temp中for (int i = arr.length - 1; i >= 0; i--) {temp[buckets[(arr[i] / exp) % 10] - 1] = arr[i];buckets[(arr[i] / exp) % 10]--;}//将有序元素temp赋给arrSystem.arraycopy(temp, 0, arr, 0, arr.length);}}

4.3桶排序

基本思想:将要排序的数据分到几个有序的桶里,每个桶里的数据再单独进行排序。桶内排完序之后,再把每个桶里的数据按照顺序依次取出,组成的序列就是有序的了

在这里插入图片描述

public static void bucketSort(int[] arr){// 计算最大值与最小值int max = Integer.MIN_VALUE;int min = Integer.MAX_VALUE;for(int i = 0; i < arr.length; i++){max = Math.max(max, arr[i]);min = Math.min(min, arr[i]);}// 计算桶的数量int bucketNum = (max - min) / arr.length + 1;ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum);for(int i = 0; i < bucketNum; i++){bucketArr.add(new ArrayList<Integer>());}// 将每个元素放入桶for(int i = 0; i < arr.length; i++){int num = (arr[i] - min) / (arr.length);bucketArr.get(num).add(arr[i]);}// 对每个桶进行排序for(int i = 0; i < bucketArr.size(); i++){Collections.sort(bucketArr.get(i));}// 将桶中的元素赋值到原序列int index = 0;for(int i = 0; i < bucketArr.size(); i++){for(int j = 0; j < bucketArr.get(i).size(); j++){arr[index++] = bucketArr.get(i).get(j);}}  
}

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

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

相关文章

我的第一个后端项目(环境搭建,Springboot项目,运行,接口验证)

一. 安装Java开发工具包&#xff08;JDK&#xff09;&#xff1a; 访问Java Software | OracleOracle官方网站&#xff0c;下载适合你操作系统的最新版本的JDK。安装JDK并设置好JAVA_HOME环境变量。 二. 安装集成开发环境&#xff08;IDE&#xff09;&#xff1a; 推荐使用In…

全方位对比 Postgres 和 MongoDB (2023 版)

本文为「数据库全方位对比系列」第二篇&#xff0c;该系列的首部作品为「全方位对比 Postgres 和 MySQL (2023 版)」 为何对比 Postgres 和 MongoDB 根据 2023 年 Stack Overflow 调研&#xff0c;Postgres 已经成为最受欢迎和渴望的数据库了。 MongoDB 曾连续 4 年 (2017 - …

【高级数据结构】树状数组

目录 树状数组1 &#xff08;单点修改&#xff0c;区间查询&#xff09; 树状数组1 &#xff08;单点修改&#xff0c;区间查询&#xff09; 洛谷&#xff1a;树状数组1https://www.luogu.com.cn/problem/P3374 题目描述 如题&#xff0c;已知一个数列&#xff0c;你需要进行…

MySQL案例——多表查询以及嵌套查询

系列文章目录 MySQL笔记——表的修改查询相关的命令操作 MySQL笔记——MySQL数据库介绍以及在Linux里面安装MySQL数据库&#xff0c;对MySQL数据库的简单操作&#xff0c;MySQL的外接应用程序使用说明 文章目录 系列文章目录 前言 一 创建数据库 1.1 创建一个部门表 1.…

C++网络编程 TCP套接字基础知识,利用TCP套接字实现客户端-服务端通信

1. TCP 套接字编程流程 1.1 概念 流式套接字编程针对TCP协议通信&#xff0c;即是面向对象的通信&#xff0c;分为服务端和客户端两部分。 1.2 服务端编程流程&#xff1a; 1&#xff09;加载套接字库&#xff08;使用函数WSAStartup()&#xff09;&#xff0c;创建套接字&…

【Python机器学习】实验04(2) 机器学习应用实践--手动调参

文章目录 机器学习应用实践1.1 准备数据此处进行的调整为&#xff1a;要所有数据进行拆分 1.2 定义假设函数Sigmoid 函数 1.3 定义代价函数1.4 定义梯度下降算法gradient descent(梯度下降) 此处进行的调整为&#xff1a;采用train_x, train_y进行训练 1.5 绘制决策边界1.6 计算…

Spring AOP 中,切点有多少种定义方式?

在 Spring AOP 中&#xff0c;我们最常用的切点定义方式主要是两种&#xff1a; 使用 execution 进行无侵入拦截。使用注解进行拦截。 这应该是是小伙伴们日常工作中使用最多的两种切点定义方式了。但是除了这两种还有没有其他的呢&#xff1f;今天松哥就来和大家聊一聊这个话…

Linux-Shell

1.什么是Bash shell(壳) Bash Shell是一个命令解释器&#xff0c;它在操作系统的最外层&#xff0c;负责用户程序与内核进行交互操作的一种接口&#xff0c;将用户输入的命令翻译给操作系统&#xff0c;并将处理后的结果输出至屏幕。 通过xshell连接&#xff0c;就是打开了一…

uniAPP 视频图片预览组件

效果图 思路&#xff1a;处理文件列表&#xff0c;根据文件类型归类 已兼容 H5 ios 设备&#xff0c;测试已通过 浙政钉&#xff0c;微信小程序 视频资源因为&#xff0c;没有预览图&#xff0c;用灰色图层加播放按钮代替 <template><!--视频图片预览组件 -->&l…

《面试1v1》Kafka与传统消息系统区别

&#x1f345; 作者简介&#xff1a;王哥&#xff0c;CSDN2022博客总榜Top100&#x1f3c6;、博客专家&#x1f4aa; &#x1f345; 技术交流&#xff1a;定期更新Java硬核干货&#xff0c;不定期送书活动 &#x1f345; 王哥多年工作总结&#xff1a;Java学习路线总结&#xf…

React AntDesign写一个导出数据的提示语 上面有跳转的路径,或者点击知道了,关闭该弹层

效果如下&#xff1a; 代码如下&#xff1a; ForwardDataCenterModal(_blank);export const ForwardDataCenterModal (target?: string) > {let contentBefore React.createElement(span, null, 数据正在处理中&#xff0c;请稍后前往);let contentAfter React.creat…

lightGBM实例——特征筛选和评分卡模型构建

数据还是采用这个例子里的数据&#xff0c;具体背景也同上。 添模型构建——使用逻辑回归构建模型&#xff0c;lightGBM进行特征筛选 lightGBM模型介绍请看这个链接&#xff1a;集成学习——Boosting算法&#xff1a;Adaboost、GBDT、XGBOOST和lightGBM的简要原理和区别 具体代…

Docker复习

目录 1. Docker的理解1.1 Docker三要素 2 安装Docker2.1 安装命令2.2 配置阿里云加速器 3 Docker命令3.1 启动类命令3.2 镜像类命令 4 实战4.1 启动容器&#xff0c;自动创建实例4.2 查看Docker内启动的容器4.3 退出容器4.4 其他4.5 导入导出文件4.6 commit 5 Dockerfile5.1 理…

uniapp:H5定位当前省市区街道信息

高德地图api&#xff0c;H5定位省市区街道信息。 由于uniapp的uni.getLocation在H5不能获取到省市区街道信息&#xff0c;所以这里使用高德的逆地理编码接口地址接口&#xff0c;通过传key和当前经纬度&#xff0c;获取到省市区街道数据。 这里需要注意的是&#xff1a;**高德…

0基础学习VR全景平台篇 第69篇:VR直播-如何设置广告

直播间可以插入轮播广告&#xff0c;并且支持外链跳转&#xff0c;能够有效地提升VR直播活动的转化率。 1、点击&#xff0c;添加广告 2、广告图展现形式分为两种&#xff1a;普通广告和全屏广告&#xff0c;普通广告在非全屏播放的直播间显示&#xff0c;全屏广告在全屏播放的…

Raki的读paper小记:LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS

Abstract&Introduction&Related Work 研究任务 对大模型进行部分微调 已有方法和相关工作 现有技术通常通过扩展模型深度引入推理延迟&#xff08;Houlsby 等人&#xff0c;2019&#xff1b;Rebuffi 等人&#xff0c;2017&#xff09;&#xff0c;或通过减少模型可用序…

redis的四种模式优缺点

redis简介 Redis是一个完全开源的内存数据结构存储工具&#xff0c;它支持多种数据结构&#xff0c;以及多种功能。Redis还提供了持久化功能&#xff0c;可以将数据存储到磁盘上&#xff0c;以便在重启后恢复数据。由于其高性能、可靠性和灵活性&#xff0c;Redis被广泛应用于…

什么是 MyBatis?

经过前几篇博客的学习 Spring 系列的基本操作已经实现的差不多了&#xff0c;接下来&#xff0c;我们来学习更重要的知识&#xff0c;将前端传递的数据存储起来&#xff0c;或者查询数据库里面的数据。 一、MyBatis 是什么&#xff1f; MyBatis 是一款优秀的持久层框架&…

东南大学轴承故障诊断(Python代码,CNN模型,适合复合故障诊断研究)

运行代码要求&#xff1a; 代码运行环境要求&#xff1a;Keras版本>2.4.0&#xff0c;python版本>3.6.0 本次实验主要是在两种不同工况数据下&#xff0c;进行带有复合故障的诊断实验&#xff0c;没有复合故障的诊断实验。 实验结果证明&#xff0c;针对具有复合故障的…

面试—Redis相关

文章目录 一、概述二、缓存1、缓存穿透2、缓存击穿3、缓存雪崩4、双写一致性5、持久化6、数据过期策略7、数据淘汰策略 三、分布式锁四、其它面试题1、主从复制2、哨兵3、分片集群结构4、I/O多路复用 一、概述 使用场景&#xff1a; Redis的数据持久化策略有哪些什么是缓存穿透…