数据结构之排序

目录

排序的概念及引用

排序的概念

常见的排序算法

常见排序算法的实现

插入排序

1.直接插入排序:

2.希尔排序( 缩小增量排序 )

选择排序

直接选择排序

堆排序

交换排序

冒泡排序

快速排序

1)Hoare版

2)挖坑法

3)前后指针

4)快速排序优化

5)快速排序非递归

快速排序总结

归并排序

归并排序总结

海量数据的排序问题

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


排序的概念及引用

排序的概念

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

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

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

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

常见的排序算法


常见排序算法的实现

插入排序

直接插入排序是一种简单的插入排序法,其基本思想是:

把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到 一个新的有序序列 。实际中我们玩扑克牌时,就用了插入排序的思想。

1.直接插入排序:

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

方法代码:

package Sort_data;/*** 插入排序*/
public class insert {public void insertSort(int[] array){for (int i = 0; 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;}}}

测试代码:

/*** 1.插入排序*/public static void main(String[] args) {insert insert = new insert();int[] test = {2,10,5,4,12};insert.insertSort(test);System.out.println(Arrays.toString(test));}

直接插入排序的特性总结:

1. 元素集合越接近有序,直接插入排序算法的时间效率越高

2. 时间复杂度:O(N^2)

3. 空间复杂度:O(1),它是一种稳定的排序算法

4. 稳定性:稳定


2.希尔排序( 缩小增量排序 )

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成多个组, 所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达 =1时,所有记录在统一组内排好序。

代码展示:

/*** 希尔排序法*/public 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;}}public static void shellSort(int[] array){int gap = array.length;while (gap>1){gap = gap/2;shell(array,gap);}}

测试代码:

/*** 希尔排序*/public static void main(String[] args) {insert insert = new insert();int[] test = {2,10,5,4,12,99,7,34,22,24,11};insert.insertSort(test);System.out.println(Arrays.toString(test));}

希尔排序的特性总结:

1. 希尔排序是对直接插入排序的优化。

2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很 快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。

3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的希尔排序的时间复杂度都不固定

4. 稳定性:不稳定

5.复杂度:O(1)


选择排序

基本思想:

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

直接选择排序

·在元素集合array[i]--array[n-1]中选择关键码最大(小)的数据元素

·若它不是这组元素中的最后一个(第一个)元素,将它这组元素中的最后一个(第一个)元素交换

·在剩余array[i]--array[n-2](array[i+1]--array[n-1])集合中,重复上述步骤,直到集合剩余1个元素

代码展示:

/*** 直接选择排序*/public  void selectSort(int[] array){int j = 0;for (int i = 0; i < array.length; i++) {j = i + 1;int minIndex = i;for (; j < array.length; j++) {if (array[j] < array[minIndex]){minIndex = j;}}swap(array,i,minIndex);}}public static void swap(int[] array,int i,int j){int tem = array[i];array[i] = array[j];array[j] = tem;}

测试代码:

 /*** 直接选择排序*/public static void main(String[] args) {insert insert = new insert();int[] test = {2,10,5,4,12,99,7,34,22,24,11};insert.selectSort(test);System.out.println(Arrays.toString(test));}

【直接选择排序的特性总结】

1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用

2. 时间复杂度:O(N^2)

3. 空间复杂度:O(1)

4. 稳定性:不稳定


堆排序

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

升序代码展示:

    /*** 升序:建大根堆*/// 1.建立大根堆public static void  createBigHeap(int[] array){for (int parent = (array.length-1-1)/2; parent >= 0 ; parent--){shiftDown(array,parent,array.length);}}// 2.向下调整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[child+1]){// 是否有右孩子,且有孩子的值大于左孩子child++;}if (array[child] > array[parent]){swap(array,parent,child);// 要一直往下走,一直把这整一棵树都调整完parent = child;child = 2*parent+1;}else {break;}}}public void heapSort4(int[] array){createBigHeap(array);  // 先建立一个大根堆int end = array.length-1;while (end>0){swap(array,0,end);shiftDown(array,0,end);end--;}}

升序测试代码:

【直接选择排序的特性总结】

1. 堆排序使用堆来选数,效率就高了很多。

2. 时间复杂度:O(N*logN)

3. 空间复杂度:O(1)

4. 稳定性:不稳定


交换排序

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

冒泡排序

代码展示:

/*** 冒泡排序法*/public void bubbleSort(int[] array){for (int i = 0; i < array.length-1; i++) {boolean flg = false;   // 不一定要走那么多趟,有的时候走1/2趟就有序了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;}}}

测试代码:

/*** 冒泡排序法*/public static void main(String[] args) {insert insert = new insert();int[] test = {2,10,5,4,12,99,7,34,22,24,11};insert.bubbleSort(test);System.out.println(Arrays.toString(test));}

【冒泡排序的特性总结】

1. 冒泡排序是一种非常容易理解的排序

2. 时间复杂度:O(N^2)【高】

3. 空间复杂度:O(1)

4. 稳定性:稳定


快速排序

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

1)Hoare版

上图是小编画的关于Hoare版思想的一个简图,帮助大家理解

代码示例:最重要的代码是找基准

/*** 快速排列*/public void quickSort(int[] array){quick(array,0,array.length-1);}// 递归用的private static void quick(int[] array,int start,int end){while (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--;}while (left<right && array[left] <= key){left++;}swap(array,left,right);}swap(array,left,i);return left;}

测试代码:

 // 1.Hoare版public static void main(String[] args) {insert insert = new insert();int[] test = {2,10,5,4,12,99,7,34,22,24,11};insert.quickSort(test);System.out.println(Arrays.toString(test));}

2)挖坑法(优先级最高)

基本思想示意图:

方法代码:

 public void quickSort(int[] array){quick(array,0,array.length-1);}// 递归用的private static void quick(int[] array,int start,int end){while (start>=end){return;   // 左边是一个节点或者一个节点都没有}int pivot = partition1(array,start,end);quick(array,start,pivot-1);  // 递归左边quick(array,pivot+1,end);  // 递归右边}
/*** 快速排列之挖坑法*/private static int partition1(int[] array,int left,int right){int key = array[left];int i = left;while (left < right){while (left<right && array[right] >= key){right--;}array[left] = array[right];while (left<right && array[left] <= key){left++;}array[right] = array[left];}array[left] = key;return left;}

测试代码:

 // 1.Hoare版public static void main(String[] args) {insert insert = new insert();int[] test = {2,10,5,4,12,99,7,34,22,24,11};insert.quickSort(test);System.out.println(Arrays.toString(test));}

3)前后指针

方法代码:关于这个方法代码里面涉及到的一些方法我没有复制出来,但是在我前面的代码中都有涉及到,所以小编还是那句话,代码示例只起到一个示例作用。

/*** 快速排列之前后指针法*/private static int partition2(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;}
4)快速排序优化

1. 三数取中法选key

// 递归用的private static void quick(int[] array,int start,int end){while (start>=end){return;   // 左边是一个节点或者一个节点都没有}// 三数取中(优化快速排序用的)int index = midOfThree(array,start,end);swap(array,index,start);  //交换完之后一定能保证start的下标一定是中间大的数字int pivot = partition2(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[right]){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;}}}

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

// 这个插入排序是为了解决快排用的public static void selectSortRange(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 {break;}}array[j+1] = tmp;}}// 递归用的private static void quick(int[] array,int start,int end){while (start>=end){return;   // 左边是一个节点或者一个节点都没有}// 递归到小的子区间时,可以考虑使用插入排序if(end - start+1 < 15){selectSortRange(array,start,end);}// 三数取中(优化快速排序用的)int index = midOfThree(array,start,end);swap(array,index,start);  //交换完之后一定能保证start的下标一定是中间大的数字int pivot = partition2(array,start,end);quick(array,start,pivot-1);  // 递归左边quick(array,pivot+1,end);  // 递归右边}

5)快速排序非递归

非递归是需要一个

 /*** 快速排序非递归*/public 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);}}}

快速排序总结

1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

2. 时间复杂度:O(N*logN)

3. 空间复杂度:O(logN)

4. 稳定性:不稳定


归并排序

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

整体思路:1.分解左边;2.分解右边;3.合并

方法代码1:递归

/*** 归并排序*/public void mergeSort(int[] array){mergeSortFunc(array,0,array.length-1);}public static void mergeSortFunc(int[] array,int left,int right){if (left>=right){return;}int mid = (left+right)/2;mergeSortFunc(array,left,mid);  // 分解左边mergeSortFunc(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[] temArr = new int[right-left+1];int k = 0;while (s1<=mid && s2<=right){if (array[s2]<=array[s1]){temArr[k++] = array[s2++];}else {temArr[k++] = array[s1++];}}// 并不知道哪个循环没走完while (s1<=mid){temArr[k++] = array[s1++];}while (s2<=right){temArr[k++] = array[s2++];}// tmpArr里面一定是这个区间内有序的数据for (int i = 0; i < temArr.length; i++) {array[i+left] = temArr[i];}}

在这个代码里面需要注意的就是拷贝数组的时候下标问题;

方法代码2:非递归

/*** 归并排序:非递归*/public void mergeSortNor(int[] array){int gap = 1;while (gap < array.length){for (int i = 0; i < array.length; i += 2*gap) {int left = i;int mid = left+gap-1;int right = mid+gap;if (mid>=array.length){mid = array.length-1;}if (right>=array.length){right = array.length-1;}merge(array,left,right,mid);}gap *= 2;}}

测试代码1:

/*** 归并排序*/public static void main(String[] args) {insert insert = new insert();int[] test = {2,10,5,4,12,99,7,34,22,24,11};insert.mergeSort(test);System.out.println(Arrays.toString(test));}

测试代码2:

 /*** 归并排序:非递归排序*/public static void main(String[] args) {insert insert = new insert();int[] test = {2,10,5,4,12,99,7,34,22,24,11};insert.mergeSortNor(test);System.out.println(Arrays.toString(test));}

归并排序总结

1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。

2. 时间复杂度:O(N*logN)

3. 空间复杂度:O(N)

4. 稳定性:稳定【只有插入排序、冒泡排序、归并排序是稳定的】


海量数据的排序问题

外部排序:排序过程需要在磁盘等外部存储进行的排序

前提:内存只有 1G,需要排序的数据有 100G

因为内存中因为无法把所有数据全部放下,所以需要外部排序,而归并排序是最常用的外部排序

1. 先把文件切分成 200 份,每个 512 M

2. 分别对 512 M 排序,因为内存已经可以放的下,所以任意排序方式都可以

3. 进行 2路归并,同时对 200 份有序文件做归并过程,最终结果就有序了


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

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

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

相关文章

从“泛读”到“精读”:合合信息文档解析如何让大模型更懂复杂文档?

从“泛读”到“精读”&#xff1a;合合信息文档解析如何让大模型更懂复杂文档&#xff1f; 一、引言&#xff1a;破解文档“理解力”瓶颈二、核心功能&#xff1a;合合信息的“破局”亮点功能亮点1&#xff1a;复杂图表的高精度解析图表解析&#xff1a;为大模型装上精准“标尺…

NoSQL 数据库的适用场景与局限性分析

NoSQL(Not Only SQL)数据库是一类非关系型数据库,通过灵活的数据模型和分布式架构解决传统关系型数据库在扩展性、性能和数据多样性上的瓶颈。以下从技术特性、适用场景、不适用场景及行业实践展开分析: 一、NoSQL数据库的核心技术特性 四大数据模型 文档型:以JSON/BSON格…

Pycharm(七):几个简单案例

一.剪刀石头布 需求&#xff1a;和电脑玩剪刀石头布游戏 考察点&#xff1a;1.随机数&#xff1b;2.判断语句 import random # numrandom.randint(1,3) # print(num) # print(**30) #1.录入玩家手势 playerint(input(请输入手势&#xff1a;&#xff08;1.剪刀 2.石头 3&…

Reactive编程:什么是Reactive编程?Reactive编程思想

文章目录 **1. Reactive编程概述****1.1 什么是Reactive编程&#xff1f;****1.1.1 Reactive编程的定义****1.1.2 Reactive编程的历史****1.1.3 Reactive编程的应用场景****1.1.4 Reactive编程的优势** **1.2 Reactive编程的核心思想****1.2.1 响应式&#xff08;Reactive&…

【数学建模】动态规划算法(Dynamic Programming,简称DP)详解与应用

动态规划算法详解与应用 文章目录 动态规划算法详解与应用引言动态规划的基本概念动态规划的设计步骤经典动态规划问题1. 斐波那契数列2. 背包问题3. 最长公共子序列(LCS) 动态规划的优化技巧动态规划的应用领域总结 引言 动态规划(Dynamic Programming&#xff0c;简称DP)是一…

Linux基础之软硬链接

参考链接&#xff1a;https://baijiahao.baidu.com/s?id1770724291436944734&wfrspider&forpc 一、定义 1.硬链接&#xff08;Hard Link&#xff09; 硬链接是指多个文件名指向同一个物理文件的链接关系。它们在文件系统中具有相同的inode号&#xff08;索引节点号…

python每日十题(13)

一般把计算机完成一条指令所花费的时间称为一个指令周期。指令周期越短&#xff0c;指令执行就越快。本题答案为D选项。 顺序程序具有顺序性、封闭性和可再现性的特点&#xff0c;使得程序设计者能够控制程序执行的过程(包括执行顺序、执行时间&#xff09;&#xff0c;对程序执…

0328-内存图2

是否正确待定&#xff1a; Perso类 package com.qc.内存图2;public class Perso {public int age;public String name;public static int flag;public void m1() {}public static void m2() {}Overridepublic String toString() {return "Perso [age" age "…

Java 开发中的 AI 黑科技:如何用 AI 工具自动生成 Spring Boot 项目脚手架?

在 Java 开发领域&#xff0c;搭建 Spring Boot 项目脚手架是一项耗时且繁琐的工作。传统方式下&#xff0c;开发者需要手动配置各种依赖、编写基础代码&#xff0c;过程中稍有疏忽就可能导致配置错误&#xff0c;影响开发进度。如今&#xff0c;随着 AI 技术的迅猛发展&#x…

一文详解k8s体系架构知识

0.云原生 1.k8s概念 1. k8s集群的两种管理角色 Master&#xff1a;集群控制节点&#xff0c;负责具体命令的执行过程。master节点通常会占用一股独立的服务器&#xff08;高可用部署建议用3台服务器&#xff09;&#xff0c;是整个集群的首脑。 Master节点一组关键进程&#xf…

ubuntu下docker 安装 graylog 6.1

下载docker compose相关仓库 https://github.com/Graylog2/docker-compose 按readme所述&#xff0c;拷贝.env.example并重命名 .env 按.env中的说明创建密码和密钥 创建GRAYLOG_PASSWORD_SECRET 用: pwgen -N 1 -s 96 创建GRAYLOG_ROOT_PASSWORD_SHA2 用: echo -n yourpa…

创新驱动 智领未来丨中威电子全景展示高速公路数字化创新成果

在数字经济与新型基础设施建设深度融合的背景下&#xff0c;中国智慧交通产业正迎来前所未有的发展机遇。3月27日&#xff0c;第27届中国高速公路信息化大会暨技术产品博览会在青岛市红岛国际会议展览中心盛大开幕。作为高速公路信息化领域的创新先锋&#xff0c;中威电子&…

计算机期刊征稿 | 计算机-网络系统:物联网系统架构、物联网使能技术、物联网通信和网络协议、物联网服务和应用以及物联网的社会影响

IEEE Internet of Things Journal 学科领域&#xff1a; 计算机-网络系统 期刊类型&#xff1a; SCI/SSCI/AHCI 收录数据库&#xff1a; SCI(SCIE),EI ISSN&#xff1a; 2327-4662 中科院&#xff1a; 1区 影响因子&#xff1a; 8.2 JCR&#xff1a; Q1 IEEE Internet…

springBoot统一响应类型3.3版本

前言&#xff1a; 通过实践而发现真理&#xff0c;又通过实践而证实真理和发展真理。从感性认识而能动地发展到理性认识&#xff0c;又从理性认识而能动地指导革命实践&#xff0c;改造主观世界和客观世界。实践、认识、再实践、再认识&#xff0c;这种形式&#xff0c;循环往…

mapbox基础,加载popup弹出窗

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️mapboxgl.Map style属性1.3 ☘️popup 弹出窗 api1.3.1 ☘️构造函数1.…

MySQL基础语法1

目录 #1.创建和删除数据库 ​编辑#2.如果有lyt就删除,没有则创建一个新的lyt #3.切换到lyt数据库下 #4.创建数据表并设置列及其属性,name是关键词要用name包围 ​编辑 #5.删除数据表 #5.查看创建的student表 #6.向student表中添加数据,数据要与列名一一对应 #7.查询st…

【ESP32S3】esp32获取串口数据并通过http上传到前端

通过前面的学习&#xff08;前面没发过&#xff0c;因为其实就是跑它的demo&#xff09;了解到串口配置以及开启线程实现功能的工作流程&#xff0c;与此同时还有esp32作为STA节点&#xff0c;将数据通过http发送到服务器。 将这两者联合 其实是可以得到一个&#xff1a;esp32获…

CSS 美化页面(二)

一、CSS 属性详解 1、字体属性 (Font) 属性描述值示例简写属性font-family设置字体系列"Arial", sans-serif font: italic small-caps bold 16px/1.5 "Arial", sans-serif; font-size设置字体大小16px, 1.2em, 1remfont-weight设置字体粗细normal, bold,…

win32汇编环境,网络编程入门之十四

;win32汇编环境,网络编程入门之十四 ;在这一教程里&#xff0c;学习一下&#xff0c;如何得到网页的标题 ;这里需要理解一下html语言&#xff0c;<title> </title>标签对里面的内容即为网页的标题 ;其原理是把返回的字符串&#xff0c;按字节进行检查&#xff0c;发…

[已解决]服务器CPU突然飙高98%----Java程序OOM问题 (2024.9.5)

目录 问题描述问题排查问题解决参考资料 问题描述 业主单位服务器自8月29日晚上21:00起CPU突然飙高至98%&#xff0c;内存爆满&#xff0c;一直到9月5日&#xff1a; 问题排查 ①执行 top 命令查看Java进程PID top②执行top -Hp PID 命令查看具体的线程情况 top -Hp 3058输入上…