【数据结构-JAVA】排序

news/2024/4/20 2:45:34/文章来源:https://blog.csdn.net/qq_41233305/article/details/128839958

排序在现实生活中的应用可谓相当广泛,比如电商平台中,选购商品时,使用价格排序或是综合排序、高考填报志愿的时候,会参考全国大学排名的情况。

下面介绍一些计算机中与排序相关的概念:

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

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

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

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

  1. 插入排序

1.1 直接插入排序

1.1.1 插入排序的基本思想

把待排序的记录按其关键码值的大小与已经排好序的序列逐个比较大小,插入到合适的位置,使整个序列重新有序。实际中我们玩扑克牌时,就用了插入排序的思想。

当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…array[0]的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移。

1.1.2 直接插入法的代码实现

 public class Test {public static void insertSort(int[] arr){for (int i = 1; i < arr.length; i++) {int j = i - 1;int temp =arr[i];for (; j >= 0 ; j--) {if(arr[j] > temp){arr[j+1] = arr[j];}else{break;}}arr[j + 1] = temp;}}public static void main(String[] args) {int[] arr = {4,2,5,3,134,74,36,7,8,76,32,17,38,54,38,87,54};insertSort(arr);System.out.println(Arrays.toString(arr));}
}

输出:

[2, 3, 4, 5, 7, 8, 17, 32, 36, 38, 38, 54, 54, 74, 76, 87, 134]

1.1.3 直接插入法的特性总结

直接插入排序的特性总结: 1. 元素集合越接近有序,直接插入排序算法的时间效率越高 2. 时间复杂度:O(N^2) 3. 空间复杂度:O(1) 4. 稳定性:稳定 ,它是一种稳定的排序算法

1.2 希尔排序

1.2.1 希尔排序的基本思想

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先想好间隔的设定方法,比如gap一开始为数组的长度,那么每个记录自成一组,无需排序,往后的间隔依次除以2(可以自己设定),把待排序文件中所有记录分成gap个组, 并对每一组内的记录进行直接插入排序。然后重复上述分组和排序的工作。当gap =1时,所有记录在统一组内最后插入排序。

1.2.2 希尔排序的代码实现

    //希尔排序:对直接插入法的优化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 j = i - gap;int temp = arr[i];for (; j >= 0 ; j-=gap) {if(arr[j] > temp){arr[j + gap] = arr[j];}else{break;}}arr[j + gap] = temp;}}public static void main(String[] args) {int[] arr = {5,23,2,51,4,5,642,14,56,82,1,73,59,42,77};shellSort(arr);System.out.println(Arrays.toString(arr));}

输出:

[1, 2, 4, 5, 5, 14, 23, 42, 51, 56, 59, 73, 77, 82, 642]

1.2.3 希尔排序的特性总结

希尔排序的特性总结: 1. 希尔排序是对直接插入排序的优化。 2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很 快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。 3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算。Knuth进行了大量的试验统计 ,当n很大时,关键码平均比较次数以及对象平均移动次数大约在范围内。所以对于希尔排序的时间复杂度,我们暂时就按照来计算。 4. 稳定性:不稳定

2. 选择排序

2.1 直接选择排序

2.1.1 直接先择排序的基本思想

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置。此时,就有1个元素有序,那么继续从剩下待排序元素中找到最小(或最大)的一个元素,放到有序数组后面,直到全部待排序的数据元 素排完 。

写代码时,如果是在原本数组进行操作,那么只能让序列的最小值与相应位置进行交换了。

2.1.2 直接选择排序的代码实现

    //选择排序//从一边查找最小元素public static void selectSort(int[] arr){//i下标是排序的位置//j则是遍历剩下数组元素,找到最小下标for (int i = 0; i < arr.length; i++) {int j = i + 1;int minIndex = i;for (; j < arr.length; j++) {if(arr[j] < arr[minIndex]){minIndex = j;}}swap(arr,i,minIndex);}}public static void main(String[] args) {int[] arr = {3,7,54,24,33,22,18,33,28,13,64,8,26};selectSort(arr);System.out.println(Arrays.toString(arr));}

输出:

[3, 7, 8, 13, 18, 22, 24, 26, 28, 33, 33, 54, 64]

为了让选择排序更高效,能不能在序列中查找最小值的同时,查找最大值,让最小值与最大值与各自相应位置进行交换?

    //从序列同时查找最大最小元素并与相应位置上的元素进行交换public static void selectSort(int[] arr){int left = 0;int right = arr.length - 1;while(left < right){int minIndex = left;int maxIndex = left;for (int j = left + 1; j <= right; j++) {if(arr[j] < arr[minIndex]){minIndex = j;}if(arr[j] > arr[maxIndex]){maxIndex = j;}}swap(arr,minIndex,left);if(left == maxIndex){maxIndex = minIndex;}swap(arr,maxIndex,right);left++;right--;}}public static void main(String[] args) {int[] arr = {4,3,22,56,77,23,53,7,13,75,224,6,27,69,49,88};selectSort(arr);System.out.println(Arrays.toString(arr));}

输出:

[3, 4, 6, 7, 13, 22, 23, 27, 49, 53, 56, 69, 75, 77, 88, 224]

2.1.3 直接排序的特定

1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用 2. 时间复杂度:O(N^2) 3. 空间复杂度:O(1) 4. 稳定性:不稳定

2.2 堆排序

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

2.2.1 堆排序的代码实现

   //堆排序,从小到大 -> 建立大堆 ; 从大到小 -> 建立小堆public static void heapSort(int[] arr){if(arr == null){return;}createHugeHeap(arr);int end = arr.length - 1 ;while(end > 0){swap(arr,0,end);shiftDown(arr,0,end);end--;}}//向下调整:private static void shiftDown(int[] arr,int parent,int len){int child = 2 * parent + 1;while(child < len){if(child + 1 < len && arr[child] < arr[child + 1]){child++;}if(arr[child] > arr[parent]){swap(arr,child,parent);parent = child;child = 2 * parent + 1;}else{break;}}}private static void createHugeHeap(int[] arr){for (int parent = (arr.length - 1 - 1)/2; parent >= 0 ; parent--) {shiftDown(arr,parent,arr.length );}}public static void main(String[] args) {int[] arr = {76,35,62,5,62,43,55,13,27,28,33,2,79,1};Sort.heapSort(arr);System.out.println(Arrays.toString(arr));}

输出:

[1, 2, 5, 13, 27, 28, 33, 35, 43, 55, 62, 62, 76, 79]

2.2.2堆排序的特性

【堆排序的特性总结】 1. 堆排序使用堆来选数,效率就高了很多。 2. 时间复杂度:O(N*logN) 3. 空间复杂度:O(1) 4. 稳定性:不稳定

3. 交换排序

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

3.1 快速排序

3.1.1 快速排序的基本思想

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元 素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有 元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

3.1.2 快速排序的代码实习

    public static void quickSort(int[] arr){quick(arr,0,arr.length - 1);}private static void quick(int[] arr, int start, int end) {if(start >= end){return;}//int pivot = partition(arr,start,end);int pivot = partition1(arr,start,end);quick(arr,start,pivot - 1);quick(arr,pivot + 1,end);}

填坑法

    //填坑法private static int partition(int[] arr,int left,int right){int temp = arr[left];while(left < right){//这里的等号不能省略,否则会陷入死循环//而有了等号,left < right 这个条件就不能省略while(left < right && arr[right] >= temp){right--;}swap(arr,right,left);while(left < right && arr[left] <= temp){left++;}swap(arr,left,right);}arr[left] = temp;return left;}

Hoare法

    //Hoare 法private static int partition1(int[] arr,int left,int right){int temp = arr[left];int i = left;while(left < right){while(left < right && arr[right] >= temp){right--;}while(left < right && arr[left] <= temp){left++;}swap(arr,left,right);}swap(arr,left,i);return left;}

上述的快速排序有一个问题,那就是如果数组已经是有序或是逆序的情况下,再次快排,每次都没有左树或是右数,比较费空间和时间,时间复杂度会达到O(N²),而且空间复杂度达到O(N),而只有每次划分都很平均的情况下,时间复杂度会达到O(NlogN),而且空间复杂度达到O(logN)。所以这里可以采用三数取中法来对快速排序进行优化。

三数取中法优化快排

    //三数取中法:优化快排private static int midThree(int[] arr,int head ,int tail ,int mid){if(arr[head] > arr[tail]){if(arr[mid] > arr[head]){return head;}else if(arr[tail] > arr[mid]){return tail;}else{return mid;}}else{if(arr[mid] > arr[tail]){return tail;}else if(arr[mid] < arr[head]){return head;}else{return mid;}}}//上述quick 方法修改如下:private static void quick(int[] arr, int start, int end) {if(start >= end){return;}int mid = (start + end)/2;int midIndex = midThree(arr,start,end,mid);swap(arr,mid,start);//int pivot = partition(arr,start,end);int pivot = partition1(arr,start,end);quick(arr,start,pivot - 1);quick(arr,pivot + 1,end);}public static void main(String[] args) {int[] arr = {9,8,7,6,5,4,3,2,1,0};Sort.quickSort(arr);System.out.println(Arrays.toString(arr));}

快排的非递归实现

public static void quickSortNor(int[] arr){int left = 0;int right = arr.length - 1;int pivot = partition(arr,left,right);Deque<Integer> stack = new LinkedList<>();if(pivot - 1 > left){stack.push(pivot - 1);stack.push(left);}if(right > pivot + 1){stack.push(right);stack.push(pivot + 1);}while(!stack.isEmpty()){left = stack.pop();right = stack.pop();pivot = partition(arr,left,right);if(pivot - 1 > left){stack.push(pivot - 1);stack.push(left);}if(right > pivot + 1){stack.push(right);stack.push(pivot + 1);}}
}

3.1.3 快速排序的特点

1. 快速排序整体的综合性能和使用场景都是比较好的,所以才叫快速排序 2. 时间复杂度:O(N*logN)

3. 空间复杂度:O(logN) 4. 稳定性:不稳定

3.2 冒泡排序

3.2.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 == false){return;}}}public static void main(String[] args) {int[] arr = {2,35,7,245,65,24,26,12,15,2,52,74,55,29,39,6};Sort.bubbleSort(arr);System.out.println(Arrays.toString(arr));}

输出:

[2, 2, 6, 7, 12, 15, 24, 26, 29, 35, 39, 52, 55, 65, 74, 245]

3.2.2 冒泡排序的特点

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

4. 归并排序

4.1 归并排序的基本思想

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使 子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:

4.2 归并排序的代码实现

递归实现归并排序

public static void mergeSort(int[] arr){divide(arr,0,arr.length - 1);
}private static void divide(int[] arr,int left,int right){if(left >= right){return;}int mid = (left + right)/2;divide(arr,left,mid);divide(arr,mid+1,right);merge(arr,left,right,mid);
}private static void merge(int[] arr, int start, int end, int mid) {int s1 = start;int s2 = mid + 1;int[] temp = new int[end - start + 1];int k = 0;while(s1 <= mid && s2 <= end){if(arr[s1] < arr[s2]){temp[k++] = arr[s1++];}else{temp[k++] = arr[s2++];}}while(s1 <= mid){temp[k++] = arr[s1++];}while(s2 <= end){temp[k++] = arr[s2++];}for (int i = 0; i < temp.length; i++) {arr[start + i] = temp[i];}}

非递归实现归并排序

    public static void mergeSort(int[] arr){int gap = 1;while(gap < arr.length){for (int i = 0; i < arr.length; i += 2 * gap) {int left = i;int mid = left + 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,right,mid);}gap *= 2;}}

4.3 归并排序的特点

1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。 2. 时间复杂度:O(N*logN) 3. 空间复杂度:O(N) 4. 稳定性:稳定

5. 其他排序

5.1 计数排序

5.1.1 计数排序的基本思想

思想:计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。 操作步骤: 1. 统计相同元素出现次数 2. 根据统计的结果将序列回收到原来的序列中

5.1.2 计数排序的代码实现

    public static void countSort(int[] arr){int max = arr[0];int min = arr[0];for (int i = 1; i < arr.length; i++) {if(arr[i] > max){max = arr[i];}if(arr[i] < min){min = arr[i];}}int[] count = new int[max - min + 1];for (int i = 0; i < arr.length; i++) {count[arr[i] - min]++;}int k = 0;for (int i = 0; i < count.length; i++) {while(count[i] > 0){arr[k] = i + min;k++;count[i]--;}}}

5.1.3 计数排序的特点

【计数排序的特性总结】

1. 计数排序在数据范围集中时,效率很高,是一种不需要比较大小的排序方式,但是适用范围及场景有限。

2. 时间复杂度:O(MAX(N,范围))

3. 空间复杂度:O(范围)

4. 稳定性:稳定

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

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

相关文章

python自学之《21天学通Python》(10)——正则表达式

第13章 正则表达式 最初的正则表达式出现于理论计算机科学的自动控制理论和形式化语言理论中。在这些领域中有对计算&#xff08;自动控制&#xff09;的模型和对形式化语言描述与分类的研究。 程序员所用的正则表达式是指用某种模式去匹配一类具有共同特征的字符串。正则表达…

机器学习调参

机器学习调参常用调参方法举例K邻近算法&#xff08;最常规版本&#xff09;加入交叉验证加上网格搜索GridSearchCV函数介绍GridSearchCVcross_val_score常用调参方法举例 sklearn使得我们在很多编写代码的时候更多的工作倾向于调参数而不是去写算法本身&#xff0c;本篇文章整…

卸载Node.js

0 写在前面 无论您是因为什么原因要卸载Node.js都必须要卸载干净。 请阅读&#xff1a; 1 卸载步骤 1.1通过控制面板卸载node.js winR—>control.exe—>卸载程序—>卸载Node.js 等待—>卸载成功 1.2 删除安装时的nodejs文件夹 通过记忆或者Everthing搜索找…

「自控元件及线路」14 电子电力技术与功率放大器概述

本节介绍电子电力技术的基本概念 本节介绍PD、SCR、GTR、MOSFET、IGBT等电子电力器件 本节介绍功率放大器的基本概念和线性功率放大器 文章目录电力电子技术概述电能变换电子电力器件功率二极管PD晶闸管SCR功率晶体管GTR功率场效应晶体管PowerMOSFET绝缘栅双极晶体管IGBT功率放…

使用 ThreeJS 实现第一个三维场景(详)

文章目录参考描述index.html三维场景的基本实现导入 ThreeJS准备工作场景摄像机视锥体正交摄像机透视摄像机渲染器后续处理将摄像机添加至场景中移动摄像机设置画布尺寸将渲染器创建的画布添加到 HTML 元素中渲染物体结构材质合成将物体添加至场景中代码总汇执行效果动画reques…

你的自动化框架如何设计的?为什么感觉面试官总是不满意,到底问题出在哪?

前言去面试自动化测试岗位&#xff0c;尤其是接口自动化岗位&#xff0c;面试官总会问&#xff1a;说下你的自动化框架如何设计的&#xff1f;为什么回答后&#xff0c;面试官对你的框架设计总是感觉不满意&#xff1f;自动化测试实现的几种方式对于不同的公司来说&#xff0c;…

2023年地方两会政府工作报告汇总(各省市23年重点工作)

新年伊始&#xff0c;全国各地两会密集召开&#xff0c;各省、市、自治区2023年政府工作报告相继出炉&#xff0c;各地经济增长预期目标均已明确。相较于2022年&#xff0c;多地经济增长目标放缓&#xff0c;经济不断向“高质量”发展优化转型。今年是二十大后的开局之年&#…

【参加CUDA线上训练营】零基础cuda,一文认识cuda基本概念

【参加CUDA线上训练营】零基础cuda,一文认识cuda基本概念1.术语2.线程层次2.1 Block、Warp与Thread之间的关系2.2 Thread index1.术语 \\%序号名称描述1HostCPU和内存&#xff08;host memory&#xff09;2DeviceGPU和显存&#xff08;device memory&#xff09;3SMStreaming M…

101-并发编程详解(上篇)

并发编程详解在学习之前&#xff0c;如果多线程的理解足够&#xff0c;可以往下学习&#xff0c;否则的话&#xff0c;建议先看看26章博客&#xff08;只是建议&#xff09;&#xff0c;注意&#xff1a;可能有些字的字体不对&#xff0c;那么一般是复制粘贴来的&#xff0c;但…

开关电源-一种方便快捷计算开关电源环路参数的方法及实例

一种方便快捷计算开关电源环路参数的方法及实例 接上文《技术实例 | 开关电源环路测量时&#xff0c;注入信号的幅值对测量结果的影响》&#xff0c;得到电流环功率级的开环传递函数后&#xff0c;我们通过matlab的sisotool工具箱自动计算出了电流环路补偿器的传递函数C&#…

三层交换机【实验】

目录 1、要求&#xff1a; 2、拓扑&#xff1a; 3、创建vlan和端口定义并划入vlan&#xff1a; 4、创建以太网中继Eth-Trunk使sw1和sw2的相互冗余并且不浪费链路&#xff1a; 5、使用mstp定义组和对应的根&#xff1a; 6、配置网关冗余&#xff1a; 7、核心层的路由的IP配…

云仓仓储的运行模式是什么?

仓库能够简单地定义为一个规划空间&#xff0c;通常是一个用于处置和贮存货物的大型商业建筑。因而&#xff0c;仓储是指在这样一个规划空间中存储和处置货物所触及的一切过程。仓库中常见的货物包括&#xff1a;;机械零配件、建筑资料、废品农产品、家具和电子产品。仓库中的一…

Fluid-数据缓存亲和性调度原理解析

前言在Fluid中&#xff0c;Dataset资源对象中所定义的远程文件是可被调度的&#xff0c;这意味着你能够像管理你的Pod一样管理远程文件缓存在Kubernetes集群上的存放位置。另外&#xff0c;Fluid同样支持对于应用的数据缓存亲和性调度&#xff0c;这种调度方式将应用(e.g. 数据…

二进制部署K8S集群

目录 一、架构图 二、部署步骤 1、实验环境 2、操作系统初始化配置 3、部署 docker引擎 4、部署 etcd 集群 5、部署 Master 组件 一、架构图 二、部署步骤 1、实验环境 服务器类型IP地址master192.168.80.5node01192.168.80.8node02192.168.80.9 2、操作系统初始化配置…

SpringBoot整合Mybatis的核心原理

0. 前言&#xff1a;1. 自动配置类MybatisAutoConfiguration&#xff1a;1.1. SqlSessionFactory的生成&#xff1a;1.2. Mapper的扫描和代理生成&#xff1a;1.2.1. MapperScannerConfigurer1.2.2. MapperFactoryBean1.2.3. getMapper生成代理对象2. 小结&#xff1a;0. 前言&…

3D模型深度生成网络【ShapeAssembly】

推荐&#xff1a;使用 NSDT场景设计器 快速搭建 3D场景。 我们提出了一个深度生成模型&#xff0c;该模型学习在ShapeAssembly中编写新颖的程序&#xff0c;ShapeAssembly是一种用于建模3D形状结构的特定领域语言。 执行 ShapeAssembly 程序会生成一个由部件代理长方体的分层连…

2023,考个软考中级证书稳妥深圳入户,5月考试8月办入户

最新消息&#xff01;最新消息&#xff01;最新消息&#xff01; 2023年2月8日&#xff0c;深圳市发展和改革委员会深圳市公安局深圳市人力资源和社会保障局关于印发《深圳市积分入户办法》的最新通知↓ 来源《深圳市发展和改革委员会》 该积分入户将于2023年2月15日正式实施&…

Prometheus监控Java-JMX

一、什么是 JMX Exporter ? JMX Exporter 利用 Java 的 JMX 机制来读取 JVM 运行时的一些监控数据&#xff0c;然后将其转换为 Prometheus 所认知的 metrics 格式&#xff0c;以便让 Prometheus 对其进行监控采集。 那么&#xff0c;JMX 又是什么呢&#xff1f;它的全称是&a…

ChatGPT 支持的搜索引擎 Bing 究竟什么样?

微软于2月8日北京时间凌晨在 Redmond 线下举办一场媒体活动&#xff0c;围绕微软的产品以及 AI&#xff0c;公布最新消息。这里我们先回顾一下微软在 AI 上的布局。 2019年&#xff0c;微软向 OpenAI 投资10亿美元&#xff0c;成为了 OpenAI 紧密的合作伙伴&#xff0c;而微软…

Java中动态调用setter以及getter

0x00 前言 对于非专业程序员的安全人员来说&#xff0c;因为没有代码项目的积累&#xff0c;很多知识体系都不完善&#xff0c;所以有必要在一些常用的内容进行学习的总结。 在很多的调用链中都会用到**“动态调用setter以及getter”**这个知识点&#xff0c;比如经典的CB链&a…