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

Java-数构排序

1.排序的概念

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

稳定性:假如在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对序列不变,仍然在原序列中,即r[i]=r[j],且r[i]在r[j]之前,而在排序后之后两者顺序不变则称这种排序算法稳定。

内部排序:数据元素全部放在内存中排序

外部排序:数据元素过多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。

2.常见排序算法实现

2.1插入排序

2.1.1直接插入排序

把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到 一个新的有序序列
public static void insertSort(int[] array){for (int i = 1; i < array.length; i++) {int tmp=array[i];int j = i-1;for (; j >0 ; j--) {if(array[j]>tmp){array[j+1]=array[j];}else {break;}array[j+1]=tmp;}}}

代码思路:定义两个下标,i和j,i从数组第二个元素开始,j=i-1。将当前i的元素寄存tmp,与前面的元素进行比较,若数据元素大于tmp,将前序元素覆盖当前元素,并且继续往前遍历;若数据元素小于tmp则将当前空出的元素的地方用tmp补充。若j一直遍历到小于0,遍历完了那么也将空缺位置用tmp填充。

2.1.2希尔排序

先选定一个整数,把待排序文件中所有记录分成多个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达 =1时,所有记录在统一组内排好序

    public static void shellSort(int[] array){int gap= array.length;;while(gap>1){gap/=2;shell(array,gap);}}private static void shell(int[] array, int gap) {for (int i = gap; i < array.length; i++) {int tmp=array[i];int j = i-gap;for (; j >0 ; j-=gap) {if(array[j]>tmp){array[j+gap]=array[j];}else {break;}array[j+gap]=tmp;}}}

代码思路:直接排序可以看作gap==1的时候的情况,所以可以将直接排序的代码进行修改。j下标每次移动值的区间应该为gap。gap每次排序后不断缩小,可以让每次除以2.

希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算。

2.2选择排序

2.2.1直接选择排序

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。

public static 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[j]<array[minIndex]) {minIndex = j;}}swap(array,minIndex,i);}}private static void swap(int[] array, int minIndex, int i) {int tmp=array[i];array[i]=array[minIndex];array[minIndex]=tmp;}

代码思路:假定当前下标的值为最小值,将这个值与数组后面的值进行大小比较若发现还有更小的值,将两个值进行交换。

我们还可以进行优化一下,同时操作最小值和最大值。

private static void swap(int[] array, int minIndex, int i) {int tmp=array[i];array[i]=array[minIndex];array[minIndex]=tmp;}public static void selectSort2(int[] array){int left=0;int right= array.length-1;while(left<right){int minIndex=left;int 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(maxIndex==left){maxIndex=minIndex;}swap(array,maxIndex,right);left++;right--;}

代码思路:每次将最小值放在数组的最左边最大值放在最右边,需要注意的是,交换的时候minIndex和left先进行了交换,若此时maxIndex==left,那么minIndex才是原先maxIndex坐标。

 2.2.2堆排序

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。
public static void heapSort(int[] array) {createTree(array);int end = array.length - 1;while (end > 0) {swap(array, 0, end);shiftdown(array, 0, end);end--;}}private static void createTree(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 end) {int child = 2 * parent + 1;while (child < end) {if (child + 1 < end && array[child] < array[parent]) {child++;}if (array[child] > array[parent]) {swap(array, child, parent);parent = child;child = 2 * parent + 1;}else {break;}}}

2.3交换排序

所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

2.3.1冒泡排序

    public static 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){return;}}}

2.3.2快速排序

快速排序是Hoare1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止
2.3.2.1Hoare版
public static void quickSort(int[] array){quick(array,0,array.length-1);}private static void quick(int[] array, int start, int end) {if(start>end){return;}int pivot=partition(array,start,end);quick(array,start,pivot-1);quick(array,pivot+1,end);}private static int partition(int[] array, int left, int right) {int key=array[left];int i=left;while(left<right){while(left<right&&array[right]>=key){right--;//right去寻找比key小的值用于交换,比key大则跳过}while(left<right&&array[left]<=key){left++;//left去寻找比key大的值用于交换,比key小则跳过}swap(array,left,right);}swap(array,left,i);//相遇后当前位置为基准,将当前值与key进行对换。保证基准的左边全部小于基准,右边全部大于基准。return left;//返回基准下标}
}
循环内部一定要取等号,以防当前置与key相同的时候,陷入交换的死循环。之所以先走right是为了确保基准的左边都比基准要小。
2.3.2.2挖坑法
private static int partition(int[] array, int left, int right) {int key=array[left];//取基准值并挖坑while(left<right){while(left<right&&array[right]>=key){right--;}array[left]=array[right];//若当前值比key小则填坑while(left<right&&array[left]<=key){left++;}array[left]=array[right];//若当前值比key大则填坑}array[left]=key;//相遇return left;}
2.3.2.3前后指针法
    private static int partition(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.3.3快速排序的优化

1.三个数取中法选择key,使得二叉树的深度变小

2.递归到小的子区间的时候,可以考虑使用插入排序

private static void quick(int[] array, int start, int end) {if(start>end){return;}if(end-start+1<=7){insertSortRange(array,start,end);return;}int index=midOfThree(array,start,end);int pivot=partition(array,start,end);quick(array,start,pivot-1);quick(array,pivot+1,end);}private static int midOfThree(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;}}}
private static void insertSortRange(int[] array, int start, int end) {for (int i = start+1; i <=end ; i++) {int tmp=array[i];int j = i-1;for (; j >=start ; j--) {if(array[j]>tmp){array[j+1]=array[j];}else {break;}array[j+1]=tmp;}}}

2.3.4快速排序非递归

 public static  void quickSortNor(int[] array){Stack<Integer> stack=new Stack<>();int left=0;int right=array.length-1;int piovt=partition(array,left,right);if(piovt-1>left){//只有当左边不止一个元素的时候stack.push(left);stack.push(piovt-1);}if(piovt+1<right){stack.push(piovt+1);stack.push(right);//右边不止一个元素}while(!stack.isEmpty()){right=stack.pop();//后进先出left=stack.pop();piovt=partition(array,left,right);if(piovt-1>left){stack.push(left);stack.push(piovt-1);}if(piovt+1<right){stack.push(piovt+1);stack.push(right);}}}
快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

2.4归并排序

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
先分解后合并
    public static  void mergeSort(int[] array){mergeSortDe(array,0,array.length-1);}private static void mergeSortDe(int[] array,int left,int right) {if(left>=right) return;int mid=(left+right)/2;mergeSortDe(array,left,mid);mergeSortDe(array,mid+1,right);merge(array,left,right,mid);}private static void merge(int[] array, int left, int right, int mid) {int s1=left;int s2=mid+1;int[] tmpArr=new  int[right-left+1];int k=0;while(s1<mid&&s2<=right){if(array[s2]<=array[s1]){tmpArr[k++]=array[s2++];}else {tmpArr[k+=]=array[s1++];}}while(s1<=mid){tmpArr[k++]=array[s1++];}while (s2<=right){tmpArr[k++]=array[s2++];}for (int i = 0; i < tmpArr.length; i++) {array[i+left] = tmpArr[i];}}

2.5海量数据的排序问题

外部排序:排序过程需要在磁盘等外部存储进行的排序
前提:内存只有 1G,需要排序的数据有 100G
因为内存中因为无法把所有数据全部放下,所以需要外部排序,而归并排序是最常用的外部排序
1. 先把文件切分成 200 份,每个 512 M
2. 分别对 512 M 排序,因为内存已经可以放的下,所以任意排序方式都可以
3. 进行 2路归并,同时对 200 份有序文件做归并过程,最终结果就有序了

3.排序算法复杂度及稳定性分析

排序方法最好平均最坏空间复杂度稳定性
冒泡排序
O(n)
O(n^2)
O(n^2)O(1)
稳定
插入排序O(n)O(n^2)O(n^2)O(1)
稳定
选择排序O(n^2)O(n^2)O(n^2)O(1)
不稳定
希尔排序O(n)O(n^1.3)O(n^2)O(1)
不稳定
堆排序
O(n * log(n))
O(n * log(n))
O(n * log(n))
O(1)
不稳定
快速排序
O(n * log(n))
O(n * log(n))
O(n^2)
O(log(n)) ~ O(n)
不稳定
归并排序
O(n * log(n))
O(n * log(n))
O(n * log(n))
O(n)
稳定

4.其他排序

4.1计数排序

  1. 统计相同元素出现的次数
  2. 根据统计的结果将序列回收到原来的序列中
public static void countSort(int[] array){int minVal=array[0];int maxVal=array[0];//求当前数组的最大值和最小值for (int i = 1; i <array.length ; i++) {if(array[i]<minVal){minVal=array[i];}if(array[i]>maxVal){maxVal=array[i];}}//根据最大值和最小值来确定数组的大小int[] count=new int[maxVal-maxVal+1];//根据原来的数组进行计数for (int i = 0; i < array.length; i++) {count[array[i]-minVal]++;}//根据计数数组打印int index=0;for (int i = 0; i < array.length; i++) {while(count[i]>0){array[index]=i+minVal;index++;count[i]--;}}}

4.2基数排序

基数排序(Radix Sort)是一种非比较型的排序算法,它通过逐位比较元素的每一位(从最低位到最高位)来实现排序。基数排序的核心思想是将整数按位数切割成不同的数字,然后按每个位数分别进行排序。基数排序的时间复杂度为 O(n * k),其中 n 是列表长度,k 是最大数字的位数。

算法步骤:

  1. 确定最大位数:找到列表中最大数字的位数,确定需要排序的轮数。

  2. 按位排序:从最低位开始,依次对每一位进行排序(通常使用计数排序或桶排序作为子排序算法)。

  3. 合并结果:每一轮排序后,更新列表的顺序,直到所有位数排序完成。

4.3桶排序

一句话总结:划分多个范围相同的区间,每个子区间自排序,最后合并。

桶排序是计数排序的扩展版本,计数排序可以看成每个桶只存储相同元素,而桶排序每个桶存储一定范围的元素,通过映射函数,将待排序数组中的元素映射到各个对应的桶中,对每个桶中的元素进行排序,最后将非空桶中的元素逐个放入原序列中。

http://www.dtcms.com/a/302723.html

相关文章:

  • ATF 运行时服务
  • 【Web】京麒CTF 2025 决赛 wp
  • USRP-X440 2025年太空与导弹防御研讨会
  • 近屿智能正式发布AI得贤招聘官的AI面试官智能体6.3版本:交付替代人类面试官的打分结果
  • 1990-2024年上市公司财务指标/应计利润数据(30+指标)
  • MFC UI对话框
  • 基于Uniapp及Spring Boot的奢侈品二手交易平台的设计与实现/基于微信小程序的二手交易系统
  • 零基础学习性能测试第九章:全链路追踪-系统中间件节点监控
  • 【pytest高阶】源码的走读方法及插件hook
  • Ubuntu lamp
  • 商用车的自动驾驶应用场景主要包括七大领域
  • 十七、K8s 可观测性:全链路追踪
  • AI对服务器行业的冲击与启示:从挑战走向重构
  • vue3【组件封装】头像裁剪 S-avatar.vue
  • 谋先飞(Motphys)亮相 2025 世界人工智能大会:以物理仿真重构智能未来
  • Apache Commons VFS:Java内存虚拟文件系统,屏蔽不同IO细节
  • YOLOv11改进:添加SCConv空间和通道重构卷积二次创新C3k2
  • Error reading config file (/home/ansible.cfg): ‘ACTION_WARNINGS(default) = True
  • 如何理解有符号数在计算机中用​​补码​​存储
  • 网络安全第14集
  • C51:使用PWM波调节LED灯的亮度
  • GitLab 18.2 发布几十项与 DevSecOps 有关的功能,可升级体验【三】
  • 如何检测并修复服务器中的rootkit威胁
  • 中型企业如何用 RUM 技术破解地理分布式用户体验难题?从指标监测到优化实操
  • 暴雨服务器更懂人工智能+
  • Jetson Orin nx识别不到imx219 需要额外设置
  • [ The Missing Semester of Your CS Education ] 学习笔记 Vim篇
  • 4.DRF 认证--Authentication4.DRF 认证--Authentication
  • 从文件到文件描述符:理解程序与文件的交互本质
  • TapData 出席 TDBC 2025 可信数据库发展大会,分享“实时+信创”时代的数据基础设施演进路径