详解常见排序

详解常见排序

目录

​编辑

插入排序

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

选择排序

冒泡排序

堆排序

快速排序

hoare版

 挖坑法

前后指针法

非递归版

归并排序

递归版

非递归版

计数排序


声明:以下排序代码由Java实现!!!

插入排序

步骤:

1.我们可以认为数组的第一个元素已经被排好序,因此只需考虑对后面的元素进行插入排序;

2.取下一个位置的元素val,让它和它之前的元素进行比较,顺序为从右向左;

3.如果该元素大于val,则将该元素移动到该元素所处位置的下一个位置;

4.重复步骤3,知道找到已排好序的序列中小于等于val的元素;

5.将值val放到该位置的下一个位置,如果已排好序的所有元素的值都大于val,则将val存放到数组下标为0的位置;

6.重复2~5步骤。

动画演示:

代码如下:

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; } }
折半插入排序 

在该值为val的元素找合适的位置时,是在已排好序的序列中进行找的,因此该过程可以使用二分查找(折半查找)来进行优化。

代码如下:

public static void bsInsertSort(int[] array){ for(int i=0;i<array.length;i++){ int tmp=array[i]; int left=0,right=i; while(left<right){ int mid=(left+right)>>1; if(tmp>=array[mid]) left=mid+1; else right=mid; } for(int j=i;j>left;j--) array[j]=array[j-1]; array[left]=tmp; } }
时间复杂度:最好情况下为O(N),此时待排数组为升序,或者说非常接近升序;

                     最坏情况下为O(N^N),此时待排数组为降序,或者说非常接近降序。

空间复杂度:O(1)

稳定性:稳定
希尔排序(缩小增量排序)

思想:

先选定一个小于N的整数gap作为第一增量,然后将所有距离为gap的元素划分在同一组,对每组的元素进行直接插入排序,然后再选取一个比第一增量小的整数作为第二增量gap,然后将所有距离为gap的元素划分在同一组,对每组的元素进行直接插入排序,以此类推.........,直到增量减小为1时,此时就相当于整个数组被划分为一组,进行一次直接插入排序即可。

增量gap大于1时,称为“预排序”,使得待排数组接近有序;增量gap为1时,称为直接插入排序。

动画演示

代码如下:

public static void shellSort(int[] array){ int gap=array.length; while(gap>1){ gap=gap/3+1; 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; } }
平均时间复杂度:O(N^1.3)

空间复杂度:O(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,i,minIndex); } } private static void swap(int[] array,int i,int j){ int ret=array[i]; array[i]=array[j]; array[j]=ret; }

实际上,我们可以每一趟同时选择出待排序列中的最小值和最大值,然后将最小值放待排序列起始位置,将最大值放到待排序列的末尾,直到待排数据全部排完序,这样的话比前一种方法快一倍。

代码如下:

public static void DoubleSelectSort(int[] array){ int left=0,right=array.length-1; while(left<right){ int minIndex=left,maxIndex=left; for(int i=left+1;i<=right;i++){ if(array[i]>array[maxIndex]) maxIndex=i; if(array[i]<array[minIndex]) minIndex=i; } swap(array,left,minIndex); if(maxIndex==left) maxIndex=minIndex; swap(array,right,maxIndex); left++; right--; } } private static void swap(int[] array,int i,int j){ int ret=array[i]; array[i]=array[j]; array[j]=ret; }
时间复杂度:O(N^2)

空间复杂度:O(1)
冒泡排序

进行N趟,每一趟中,如果前一个位置的元素大于后一个位置的元素,则交换两个位置的元素。

动画演示

代码如下:

public static void bubbleSort(int[] array){ for(int i=0;i<array.length;i++){ boolean flag=false; for(int j=0;j<array.length-1-i;j++){ if(array[j]>array[j+1]) { swap(array, j, j + 1); flag=true; } } if(!flag) break; } }
时间复杂度:O(N^2)

空间复杂度:O(1)
堆排序

排升序建大根堆,排降序建小根堆。

以排升序为例,先对数组建立大根堆,然后将堆顶元素和堆最后一个元素交换,然后从堆顶进行向下调整,不过此时堆的大小要 -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;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+1]>array[child]) child++; if(array[parent]<array[child]){ swap(array,parent,child); parent=child; child=2*parent+1; } else break; } } private static void swap(int[] array,int i,int j){ int ret=array[i]; array[i]=array[j]; array[j]=ret; }
时间复杂度:O(N*logN)

空间复杂度:O(1)
快速排序
hoare版

步骤:

1.选定一个值Key,下标记为Keyi,通常是最左边的元素或者最右边的元素;

2.定义两个指针begin和end,being从左往右走,end从右往左走;

3.若选定的Key值是最左边的元素,需要end先走,若选定的Key值是最右边的元素,需要begin先走;

4.在走的过程中,当end遇到小于Key的数,才停下;然后begin开始走,直到遇到大于Key的数,然后交换位于begin和end位置的值,然后end再走,规则如上,直到begin和end相遇,此时再将位于Keyi位置的Key值和begin和end相遇点的值进行交换;

5.此时,Key左边的值都是小于等于Key的数,Key右边的值都是大于等于Key的数;

6.然后再对Key左边的数以及Key右边的数分别进行如上操作,直到待排序列只有一个元素为止。

动画演示:

代码如下: 

因为该方法包含大量的递归,当数据量较大时会发生栈溢出,因此做一些优化,包括【三数取中】和【当待排区间长度小于某个常数时,不再递归进行快排,而是使用直接插入排序】

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; if(end-start<=7){ insertSortRange(array,start,end); return; } int pivot=partitionHoare(array,start,end); quick(array,start,pivot-1); quick(array,pivot+1,end); } private static void insertSortRange(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 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 int partitionHoare(int[] array,int left,int right){ int key=array[left]; int keyi=left; while(left<right){ while(left<right && array[right]>=key) right--; while(left<right && array[left]<=key) left++; swap(array,left,right); } swap(array,keyi,left); return left; } private static void swap(int[] array,int i,int j){ int ret=array[i]; array[i]=array[j]; array[j]=ret; }
时间复杂度:O(N*logN)

空间复杂度:O(1) 
 挖坑法

步骤:

1.选定一个Key值,通常是位于最左边或者是最右边,在该位置形成一个坑;

2.定义两个指针left和right,left从左向右走,right从左向左走;

3.如果Key值位于数组最左边,需要right先走;如果Key值位于数组组右边,需要left先走;

4.在走的过程中,当end遇到小于Key的数,才停下,然后将right位置的值填放到坑位置,此时right位置处形成一个坑;然后begin开始走,直到遇到大于Key的数,然后将left位置的值填放到坑位置,此时left位置处形成一个坑;然后right再走,规则如上,直到left和right相遇,此时再将位于Key值填放到坑位置处;

5.此时,Key左边的值都是小于等于Key的数,Key右边的值都是大于等于Key的数;

6.然后再对Key左边的数以及Key右边的数分别进行如上操作,直到待排序列只有一个元素为止。

动画演示:

代码如下: 

因为该方法包含大量的递归,当数据量较大时会发生栈溢出,因此做一些优化,包括【三数取中】和【当待排区间长度小于某个常数时,不再递归进行快排,而是使用直接插入排序】

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; if(end-start<=7){ insertSortRange(array,start,end); return; } int pivot=partitionHole(array,start,end); quick(array,start,pivot-1); quick(array,pivot+1,end); } private static void insertSortRange(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 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 int partitionHole(int[] array,int left,int right){ int key=array[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; } private static void swap(int[] array,int i,int j){ int ret=array[i]; array[i]=array[j]; array[j]=ret; }
时间复杂度:O(N*logN)

空间复杂度:O(1) 
前后指针法

步骤:

1.选定数组最左边的值为基准值;

2.定义两个指针prev和cur,开始时prev在最左边,cur在prev的下一个位置;

3.让cur向右走,如果cur位置的值大于基准值,只需cur继续向右移动,直到遇到比基准值小的值;

4.如果cur位置的值小于基准值,先让prev向右移动一个位置,然后交换prev位置的值和cur位置的值,直到cur走到数组末尾。

代码如下:

因为该方法包含大量的递归,当数据量较大时会发生栈溢出,因此做一些优化,包括【三数取中】和【当待排区间长度小于某个常数时,不再递归进行快排,而是使用直接插入排序】

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; if(end-start<=7){ insertSortRange(array,start,end); return; } int pivot=partitionDouble(array,start,end); quick(array,start,pivot-1); quick(array,pivot+1,end); } private static void insertSortRange(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 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 int partitionDouble(int[] array,int left,int right){ int prev=left,cur=prev+1; while(cur<array.length){ if(array[cur]<array[left] && array[++prev]!=array[cur]) swap(array,prev,cur); cur++; } swap(array,prev,left); return prev; } private static void swap(int[] array,int i,int j){ int ret=array[i]; array[i]=array[j]; array[j]=ret; }
时间复杂度:O(N*logN)

空间复杂度:O(1) 
非递归版

代码如下:

public static void quickSortNor(int[] array) { Stack<Integer> stack = new Stack<>(); int left = 0; int right = array.length-1; int piovt = partitionHole(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 = partitionHole(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); } } }
归并排序
递归版

使用递归不断将区间二分,直到区间只有一个元素为止,然后将两个区间进行排序合并,直到将所有区间合并完。

代码如下:

public static void mergeSort(int[] array){ int[] dst=new int[array.length]; dst= Arrays.copyOf(array,array.length); merge(array,dst,0,array.length-1); for(int i=0;i<array.length;i++) array[i]=dst[i]; } private static void merge(int[] src,int[] dst,int start,int end){ if(start>=end) return; int mid=(start+end)>>1; merge(dst,src,start,mid); merge(dst,src,mid+1,end); int i=start,j=mid+1,k=start; while(i<=mid || j<=end){ if(j>end || (i<=mid && src[i]<src[j])) dst[k++]=src[i++]; else dst[k++]=src[j++]; } }
时间复杂度:O(N*logN)

空间复杂度:O(N) 
非递归版

将整个区间划分为长度为1,2,4,8,..........最大为N的小区间,然后对相邻的长度为1,2,4,8,..........最大为N的小区间分别进行排序合并,最终就排好序了。

代码如下:

public static void mergeSortNor(int[] array){ int[] src=array; int[] dst=new int[array.length]; for(int step=1;step<array.length;step+=step){ for(int start=0;start<array.length;start+=step*2){ int mid=Math.min(start+step-1,array.length-1); int end=Math.min(start+2*step-1,array.length-1); int i=start,j=mid+1,k=start; while(i<=mid || j<=end){ if(j>end || (i<=mid && src[i]<src[j])) dst[k++]=src[i++]; else dst[k++]=src[j++]; } } int[] tmp=src; src=dst; dst=tmp; } for(int i=0;i<array.length;i++) array[i]=src[i]; }
时间复杂度:O(N*logN)

空间复杂度:O(N) 
计数排序

先求出序列中的最大值maxVal和最小值minVal,然后开辟一个长度为maxVal-minVal+1的数组,值全都初始化为0,然后遍历整个数组,将下标为每个位置的值减去minVal处的值++,然后再重复将每个位置的值表示的次数次,将对应的值【下标+minVal】存放到原数组中。

动画演示:

代码如下: 

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-minVal+1]; for (int i = 0; i < array.length; i++) { count[array[i]-minVal]++; } int index = 0; for (int i = 0; i < count.length; i++) { while (count[i] > 0) { array[index] = i+minVal; index++; count[i]--; } } }
时间复杂度:O(N)

空间复杂度:O(N) 

Read more

Microsoft Visual C++ 运行库安装教程(最新版完整指南 | DLL修复方案)

Microsoft Visual C++ 运行库安装教程(最新版完整指南 | DLL修复方案)

前言 用过大型软件或者玩过 3A 大作的小伙伴,多少都遇到过这种弹窗: * “缺少 msvcp140.dll” * “无法继续执行代码,因为系统找不到 vcruntime140_1.dll” * 甚至是“程序无法启动,因为计算机中丢失了 MSVCR100.dll” 别慌~其实这类报错几乎 100% 是因为 Microsoft Visual C++ 运行库(VC++ Redistributable)缺失或损坏。 本文将为你带来 2025年最新版 VC++运行库下载与安装教程,覆盖: *  一键修复方法(新手必备,解决 DLL 缺失) *  专业用户手动安装方案(x86 / x64 全兼容) *  常见报错与完整修复套路 *  DLL 问题常见 FAQ 帮助你在最短时间内修好 DLL 报错,

By Ne0inhk
计算机毕设Java基于mvc的酒店管理系统 基于SSM框架的酒店客房预订与运营管理系统 Java Web驱动的智能化民宿服务管理平台

计算机毕设Java基于mvc的酒店管理系统 基于SSM框架的酒店客房预订与运营管理系统 Java Web驱动的智能化民宿服务管理平台

计算机毕设Java基于mvc的酒店管理系统58s0e9 (配套有源码 程序 mysql数据库 论文) 本套源码可以在文本联xi,先看具体系统功能演示视频领取,可分享源码参考。 随着旅游业的蓬勃发展和消费升级趋势的持续深化,酒店行业正经历着从传统人工管理模式向数字化、智能化运营的重要转型期。当前多数中小型酒店仍依赖手工登记、纸质档案和分散式信息处理,导致客房资源调配效率低下、客户信息碎片化、财务结算易出错等问题日益凸显。在"互联网+"时代背景下,构建一套集成客房资源管理、客户信息维护、预订入住一体化流程的信息化系统,已成为提升酒店服务响应速度、降低运营成本、增强市场竞争力的关键路径。本系统采用Java作为核心开发语言,基于MVC分层架构模式,结合SSM(Spring+Spring MVC+MyBatis)主流技术栈与MySQL关系型数据库,旨在打造一款轻量级、易部署、高扩展的酒店业务管理解决方案,适用于中小型酒店及连锁民宿的日常运营管理场景。 本系统采用前后端分离的双端架构设计,面向不同角色提供差异化的功能入口与服务能力。 * 首页信息聚合展示,包含系统简介与快捷导航入口 *

By Ne0inhk
Java 实战:Qoder 数据采集卡快速上手(数据采集 / 配置核心代码)

Java 实战:Qoder 数据采集卡快速上手(数据采集 / 配置核心代码)

在工业控制、数据监测等场景中,Qoder 数据采集卡凭借稳定的性能、丰富的接口,成为硬件数据采集的常用选择。Java 作为跨平台编程语言,通过 Qoder 提供的 JNI 驱动或 SDK,可轻松实现采集卡的设备连接、参数配置、数据采集与存储等核心操作。本文将以 “快速上手” 为目标,带大家用 Java 语言操作 Qoder 数据采集卡,全程代码精简可直接复用,覆盖从环境搭建到实战落地的全流程,新手也能轻松掌握。 一、核心概念与对接逻辑 1. 关键术语说明 术语 核心作用 Qoder 数据采集卡 硬件设备,支持模拟量输入 / 输出、数字量输入 / 输出、计数器等功能 JNI 驱动 Qoder 提供的 Java Native Interface 驱动,

By Ne0inhk