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

【数据结构】 排序算法

【数据结构】 排序算法

  • 一、排序
    • 1.1 排序是什么?
    • 1.2 排序的应用
    • 1.3 常见排序算法
  • 二、常见排序算法的实现
    • 2.1 插入排序
      • 2.1.1 直接插入排序
      • 2.1.2 希尔排序
    • 2.2 选择排序
      • 2.2.1 直接选择排序
        • 2.2.1.1 方法1
        • 2.2.1.1 方法2
      • 2.2.2 堆排序(数组形式)
    • 2.3 交换排序
      • 2.3.1 冒泡排序
      • 2.3.2 快速排序
        • 2.3.2.1 Hoare版 快速排序
        • 2.3.2.2 挖坑法 快速排序
      • 2.3.3 快排的优化
      • 2.3.4 快速排序非递归
    • 2.4 归并排序
  • 三、各排序算法的复杂度及稳定性
  • 四、 其他排序
    • 4.1 计数排序
    • 4.2 基数排序
    • 4.3 桶排序

一、排序

1.1 排序是什么?

在这里插入图片描述

1.2 排序的应用

在这里插入图片描述

1.3 常见排序算法

在这里插入图片描述

二、常见排序算法的实现

十大经典排序算法 动画演示

2.1 插入排序

2.1.1 直接插入排序

(1)图解

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

(2)直接插入排序 解题思路
在这里插入图片描述

(3)Java 代码实现

public class Sort {/*** 直接插入排序* 时间复杂度: 最坏情况 O(N^2)  最好情况O(N) -- 数据有序的情况下* 直接插入排序使用场景: 给定的数据 趋于有序时,可以使用直接插入排序。* 稳定性: 稳定* @param array*/public static void insertSort(int[] array){for (int i = 1; i < array.length; i++) {//从第2个元素开始排序int tmp = array[i];int j = i-1;for (; j >= 0 ; j--) {if(array[j] > tmp){array[j+1] = array[j];}else{//array[j+1] = tmp;break;}}//j=-1,把tmp的值再放回来array[j+1] = tmp;}}
}

测试类:

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Random;public class Test {//测试 直接排序法的时间效率//构造 有序数组public static void order(int[] array) {for (int i = 0; i < array.length; i++) {array[i] = i;}}//构造 逆序数组public static void  reverseOrder(int[] array){for (int i = 0; i < array.length; i++) {array[i] = array.length -i;}}//构造 随机数组public static void randomOrder(int[] array){Random random = new Random();for (int i = 0; i < array.length; i++) {array[i] = random.nextInt(array.length);}}public static void testInsertSort(int[] array){array = Arrays.copyOf(array,array.length);long startTime = System.currentTimeMillis();Sort.insertSort(array);long endTime = System.currentTimeMillis();System.out.println("直接插入排序耗时:"+(endTime-startTime));}public static void testShellSort(int[] array){array = Arrays.copyOf(array,array.length);long startTime = System.currentTimeMillis();Sort.insertSort(array);long endTime = System.currentTimeMillis();System.out.println("假设 希尔排序耗时:"+(endTime-startTime));}public static void main(String[] args) {int[] array = new int[1_0000];reverseOrder(array);testInsertSort(array);testShellSort(array);}public static void main1(String[] args) {int[] array = {81,12,31,4,15,6};Sort.insertSort(array);System.out.println(Arrays.toString(array));//[4, 6, 12, 15, 31, 81]}
}

2.1.2 希尔排序

(1)希尔排序法的基本思想:

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

(2)图解
在这里插入图片描述
在这里插入图片描述
(3)Java 代码实现

/*** 希尔排序* 时间复杂度:O(n^1.3)* 空间复杂度:O(1)* 稳定性:不稳定* @param array*/public static void shellSort(int[] array){int gap = array.length / 2;while(gap > 0){shell(array,gap);gap /= 2;}shell(array,1);}public static void shell(int[] array,int gap){for (int i = gap; i < array.length; i++) {//从第2个元素开始排序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;}}

2.2 选择排序

2.2.1 直接选择排序

2.2.1.1 方法1

(1)图解

在这里插入图片描述

在这里插入图片描述
(2)直接排序解题思路

在这里插入图片描述
(3)Java 代码实现

/*** 直接选择排序* 时间复杂度:O(N^2)* 空间复杂度: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[minIndex] > array[j]){minIndex = j;}}swap(array,minIndex,i);}}private static void swap(int[] array,int i,int j){int tmp = array[i];array[i] = array[j];array[j] = tmp;}

测试类:

 public static void main(String[] args) {int[] array = {81,12,31,4,15,6};Sort.selectSort(array);System.out.println(Arrays.toString(array));//[4, 6, 12, 15, 31, 81]}
2.2.1.1 方法2

(1)图解
在这里插入图片描述

在这里插入图片描述
(2)Java 代码

 //选择排序 方法2/*** 每次找到2个数据 一个最大 一个最小*/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);//假设第一个值 是最大值 81 3 6 2 4if(left == maxIndex){maxIndex = minIndex;}swap(array,maxIndex,right);left++;right--;}}
}

2.2.2 堆排序(数组形式)

(1)图解
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(2)Java 代码

//堆排序/*** 时间复杂度:O(n*log2n)* 空间复杂度:O(1)* 稳定性:不稳定*/public static void heapSort(int[] array){createHeap(array);int end = array.length -1;while(end > 0){swap(array,0,end);siftDown(array,0,end);end--;}}public static void createHeap(int[] array){for (int parent = (array.length-1-1)/2; parent >= 0 ; parent--) {siftDown(array,parent,array.length);}}public static void siftDown(int[] array,int parent,int length){int child = 2 * parent + 1;while(child < length){if(child+1 < length && array[child] < array[child+1]){//存在右孩子 并且 左孩子比右孩子小child++;}if(array[child] > array[parent]){swap(array,child,parent);parent = child;child = 2 * parent + 1;}else{break;}}}

2.3 交换排序

2.3.1 冒泡排序

    //冒泡排序/**当前代码是优化过的版本,分析时间复杂度的时候,不考虑优化情况* 时间复杂度:O(n^2);  如果考虑优化,最好情况下时间复杂度为O(n)* 空间复杂度:O(1)* 稳定性:稳定*/public static void bubbleSort(int[] array){//趟数:10个数据 比较9趟for (int i = 0; i < array.length-1; i++) {boolean flg = false;//未优化的版本:没有flg//每一趟的比较次数for (int j = 0; j < array.length-1-i; j++) {//未优化的版本:j < array.length-1if(array[j] > array[j+1]){swap(array,j,j+1);flg = true;}}if(!flg){break;}}}

2.3.2 快速排序

2.3.2.1 Hoare版 快速排序

(1)图解

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(2)Java 代码实现

//Hoare版 快速排序/**时间复杂度:O((n^log2n)*                当前代码的最坏情况下:O(N^2) 有序 或 逆序*空间复杂度: O(log2n) 最好*           O(N) 最坏* 稳定性:不稳定*/public static void quickSort(int[] array){quick(array,0,array.length-1);}public static void quick(int[] array,int start,int end){if(start >= end){return;}int par = partition(array,start,end);quick(array,start,par-1);quick(array,par+1,end);}private static int partitionHoare(int[] array,int left,int right){int i = left;int tmp = array[left];while(left < right){while(left < right && array[right] >= tmp){right--;}while(left < right && array[left] <= tmp){left++;}swap(array,left,right);}swap(array,left,i);return left;}
2.3.2.2 挖坑法 快速排序
private static int partition(int[] array,int left,int right){int tmp = array[left];while(left < right){while(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;}

2.3.3 快排的优化

  1. 三数取中

三数取中优化:

 //三数取中int index = midSum(array,start,end);swap(array,start,index);int par = partition2(array,start,end);quick(array,start,par-1);quick(array,par+1,end);}public static int midSum(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;}}}

快速排序:

 //Hoare版 快速排序/**时间复杂度:O((n^log2n)*                当前代码的最坏情况下:O(N^2) 有序 或 逆序*空间复杂度: O(log2n) 最好*           O(N) 最坏* 稳定性:不稳定*/public static void quickSort(int[] array){quick(array,0,array.length-1);}public static void quick(int[] array,int start,int end){if(start >= end){return;}//三数取中int index = midSum(array,start,end);swap(array,start,index);int par = partition2(array,start,end);quick(array,start,par-1);quick(array,par+1,end);}public static int midSum(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 i = left;int tmp = array[left];while(left < right){while(left < right && array[right] >= tmp){right--;}while(left < right && array[left] <= tmp){left++;}swap(array,left,right);}swap(array,left,i);return left;}//挖坑法 快速排序private static int partition2(int[] array,int left,int right){int tmp = array[left];while(left < right){while(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;}
  1. 递归到小的子区间时,可以考虑使用插入排序

直接插入排序:

 public static void quick(int[] array,int start,int end){if(start >= end){return;}if(end-start+1 <= 10){//采用直接插入排序,把当前区间排序insertSortRange(array,start,end);return;}//三数取中int index = midSum(array,start,end);swap(array,start,index);int par = partition2(array,start,end);quick(array,start,par-1);quick(array,par+1,end);}public static void insertSortRange(int[] array,int left,int right){for (int i = left+1; i <= right; i++) {int tmp = array[i];int j = i-1;for (; j >= left ; j--) {if(array[j] > tmp){array[j+1] = array[j];}else{break;}}array[j+1] = tmp;}}

快速排序:

    //Hoare版 快速排序/**时间复杂度:O((n^log2n)*                当前代码的最坏情况下:O(N^2) 有序 或 逆序*空间复杂度: O(log2n) 最好*           O(N) 最坏* 稳定性:不稳定*/public static void quickSort(int[] array){quick(array,0,array.length-1);}public static void quick(int[] array,int start,int end){if(start >= end){return;}if(end-start+1 <= 10){//采用直接插入排序,把当前区间排序insertSortRange(array,start,end);return;}//三数取中int index = midSum(array,start,end);swap(array,start,index);int par = partition2(array,start,end);quick(array,start,par-1);quick(array,par+1,end);}public static void insertSortRange(int[] array,int left,int right){for (int i = left+1; i <= right; i++) {int tmp = array[i];int j = i-1;for (; j >= left ; j--) {if(array[j] > tmp){array[j+1] = array[j];}else{break;}}array[j+1] = tmp;}}public static int midSum(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 i = left;int tmp = array[left];while(left < right){while(left < right && array[right] >= tmp){right--;}while(left < right && array[left] <= tmp){left++;}swap(array,left,right);}swap(array,left,i);return left;}//挖坑法 快速排序private static int partition2(int[] array,int left,int right){int tmp = array[left];while(left < right){while(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;}
  1. 前后指针法 快速排序
//前后指针法 快速排序public static int partition(int[] array,int left,int right){int prev = left;int cur = left+1;while(cur <= right){if(array[cur] <= array[right] && array[++prev] != array[cur]){swap(array,cur,prev);}cur++;}swap(array,prev,left);return prev;

2.3.4 快速排序非递归

(1)图解
在这里插入图片描述
在这里插入图片描述
(2)Java 代码实现

 //非递归的快速排序/** 时间复杂度:O(N*logN)* 空间复杂度:O(logN)* 不稳定*/public static void quickSortNor(int[] array){int left = 0;int right = array.length -1;int par = partition2(array,left,right);Stack<Integer> stack = new Stack<>();//左边有2个元素及以上if(par > left+1){stack.push(left);stack.push(par-1);}//右边有2个元素及以上if(par < right-1){stack.push(par+1);stack.push(right);}//相当于把0 4 6 9扔进里面了while(!stack.isEmpty()){right = stack.pop();left = stack.pop();par = partition2(array,left,right);if(par > left+1){stack.push(left);stack.push(par-1);}//右边有2个元素及以上if(par < right-1){stack.push(par+1);stack.push(right);}}}

2.4 归并排序

(1)图解
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
(2)Java 代码实现

//归并排序/** 时间复杂度:O(N*log2N)*  空间复杂度:O(N)*  稳定*/public static void mergeSort(int[] array){mergeSortChild(array,0,array.length-1);}private static void mergeSortChild(int[] array,int left,int right){if( left>= right){return;}int mid = (left+right) / 2;mergeSortChild(array, left, mid);mergeSortChild(array, mid + 1, right);//开始合并merge(array, left, mid, right);}private static void merge(int[] array, int left, int mid, int right) {//临时数组int[] tmpArr = new int[right-left+1];int k = 0;//tmpArr数组下标int s1 = left;int e1 = mid;int s2 = mid+1;int e2 = right;//当2个段 都有数据的时候while(s1 <= e1 && s2 <= e2){if(array[s1] < array[s2]){tmpArr[k++] = array[s1++];}else{tmpArr[k++] = array[s2++];}}//一个段走完了 把另一个段的数据 拷贝到临时数组while(s1 <= e1){tmpArr[k++] = array[s1++];}while(s2 <= e2){tmpArr[k++] = array[s2++];}//临时数组当中存储的是有序的数组 --> 拷贝数据到原始数组当中for (int i = 0; i < k; i++) {array[i+left] = tmpArr[i];}}

(3)非递归 归并排序

//非递归 归并排序
public static void mergeSortNor(int[] array) {if (array == null || array.length <= 1) {return;}int gap = 1; // 初始子数组长度while (gap < array.length) {// 遍历整个数组,每次合并两个相邻的子数组for (int i = 0; i < array.length; i += 2 * gap) {int left = i;int mid = Math.min(i + gap - 1, array.length - 1); // 防止越界int right = Math.min(i + 2 * gap - 1, array.length - 1); // 防止越界// 合并 [left, mid] 和 [mid+1, right]merge(array, left, mid, right);}gap *= 2; // 子数组长度翻倍}}

三、各排序算法的复杂度及稳定性

在这里插入图片描述

四、 其他排序

4.1 计数排序

(1)图解
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(2)java 代码实现

//计数排序/** 使用场景:数组集中在某个范围内*   时间复杂度:O(MAX(N,范围))*   空间复杂度:O(范围)*   稳定*/public static void countSort(int[] array){//1.求最大值 和 最小值int max = array[0];int min = 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[] countArray = new int[len];//3.遍历原始数组,计数数组开始计数for (int i = 0; i < array.length; i++) {int index = array[i] - min;countArray[index]++;}//4.遍历计数数组int k = 0;//array数组的新下标for (int i = 0; i < countArray.length; i++) {while(countArray[i] != 0){array[k] = i+min;k++;countArray[i]--;}}//执行到这里 array数组全部都有序了}

4.2 基数排序

4.3 桶排序

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

相关文章:

  • beego打包发布到Centos系统及国产麒麟系统完整教程
  • 【文件读取】open | with | as
  • 实体类JavaBean
  • 到底什么是“数字化”?数字化的本质是什么?
  • 从输入到路径:AI赋能的地图语义解析与可视化探索之旅(2025技术全景)
  • 边截图边操作?试试 Snipaste 的浮动贴图功能
  • adc模数转换器
  • Gartner《Choosing Event Brokers to Support Event-DrivenArchitecture》心得
  • OSE3.【Linux】练习:编写进度条及pv命令项目中的进度条函数
  • Postman - API 调试与开发工具 - 标准使用流程
  • 搜索与回溯算法(基础算法)
  • 华为交换机堆叠与集群技术深度解析附带脚本
  • Golang的并发编程实践总结
  • 【pathlib 】Python pathlib 库教程
  • 成都芯谷金融中心文化科技园:打造区域科技活力
  • nginx配置websocket
  • 用java,把12.25.pdf从最后一个点分割,得到pdf
  • Elastic 构建 Elastic Cloud Serverless 的历程
  • CertiK《Hack3d:2025年第二季度及上半年Web3.0安全报告》(附报告全文链接)
  • 61、【OS】【Nuttx】【构建】向量表
  • Redis-7.4.3-Windows-x64下载安装使用
  • 浅谈Docker Kicks in的应用
  • ‌Webpack打包流程
  • 为什么时序数据库IoTDB选择Java作为开发语言
  • Milvus docker-compose 部署
  • t检验​、​z检验、χ²检验中的P值
  • Vue3 使用 i18n 实现国际化完整指南
  • 浏览器F12开发者工具的使用
  • 大模型MCP技术之一句话安装Hadoop
  • DML-2-更新和删除