常见八大排序
创始人
2024-06-03 16:18:16
0次

目录

一、插入排序

二、希尔排序

三、选择排序

四、堆排序

五、冒泡排序

六、快速排序

七、归并排序

八、计数排序


一、插入排序

排序过程:

从第 j 个数字开始,往前面寻找比 arr[j] 小的数,如果找到比 arr[j] 小的数,假设下标为 i,然后交换 arr[i] 和 arr[j] 即可。

时间复杂度:最好情况(正序)下是 O(N),最坏情况(逆序)下是O(N^2)

空间复杂度:O(1)

稳定性:稳定

private static void InsertSort(int[] arr) {for (int i = 1; i < arr.length; i++) {int tmp = arr[i];int j = i;for (; j > 0; j--) {if (tmp < arr[j-1]) {arr[j] = arr[j-1];}else {break;}}arr[j] = tmp;}}

二、希尔排序

排序过程:

在插入排序的基础之上分组进行排序,设置一个间隔 gap,然后按照 gap 进行分组,在分好的每个小组进行插入排序。

时间复杂度:O(N^1.3~N^1.5)

空间复杂度:O(1)

稳定性:不稳定

private static void shellSort(int[] arr) {int gap = arr.length;while (gap > 1) {shell(arr,gap);// gap 每次都除以2,可以减少排序的次数// 如果除数太大,和直接使用插入排序效果差不多// 如果一次次的 -1或者 -2,就需要多次分组排序,这也会降低效率gap /= 2;}shell(arr,1);
}
​
private static void shell(int[] arr, int gap) {for(int i = gap; i < arr.length; i++) {int tmp = arr[i];int j = i-gap;for(; j >= 0; j -= gap) {if (tmp < arr[j]) {arr[j+gap] = arr[j];}else {break;}}arr[j+gap] = tmp;}
}

三、选择排序

直接选择排序过程:

从下标为 i 处开始,往后遍历,找到值最小的数字的位置 minIndex,并且交换 arr[i] 和 arr[minIndex]。

时间复杂度:O(N*logN)--- 直接选择排序对数据不敏感,无论有序还是无序,时间复杂度都是O(n^2)

空间复杂度:O(1)

稳定性:不稳定

private static void selectSort(int[] arr) {for(int i = 0; i < arr.length; i++) {int minIndex = i;for (int j = i; j < arr.length; j++) {minIndex = arr[minIndex] > arr[j] ? j : minIndex;}swap(arr,i,minIndex);}
}private static void swap(int[] arr, int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;
}

双指针选择排序过程:

同时遍历出最小值和最大值,然后在左右两侧进行交换。

private static void selectSort2(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++) {minIndex = arr[minIndex] > arr[j] ? j : minIndex;maxIndex = arr[maxIndex] < arr[j] ? j : maxIndex;}swap(arr,left,minIndex);// 修正 max 下标if (left == maxIndex) {maxIndex = minIndex;}swap(arr,right,maxIndex);left++;right--;}
}

四、堆排序

堆排序过程:

先创建一个大根堆,然后再从最后一个叶子结点开始往前调整。调整方式如图所示

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

空间复杂度:O(1)

稳定性:不稳定

private static void heapSort(int[] arr) {// 建立大根堆createBigHeap(arr);// 调整的节点下标int end = arr.length-1;while(end > 0) {swap(arr,0,end);shidtDown(arr,0,end);end--}
}
private static void createBigHeap(int[] arr) {for (int parent = (arr.length-1-1)/2; parent >= 0; parent--) {//向下调整shiftDown(arr,parent,arr.length);}
}private static void shiftDown(int[] arr, int parent, int length) {// 判断左孩子int child = parent*2+1;while(child < length) {if (child + 1 < length && arr[child+1] > arr[child]) {child++;}if(arr[child] > arr[parent]) {swap(arr,child,parent);parent = child;child = parent*2+1;}else {break;}}
}
private static void swap(int[] arr, int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;
}

五、冒泡排序

排序过程:

从 i 开始遍历,若 arr[i+1] > arr[i],则arr[i+1] 和 arr[i] 进行交换即可,交换一轮之后,最大值就会跑到最后一个位置。

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

空间复杂度:O(1)

稳定性:稳定

private static void bubbleSort(int[] arr) {for (int i = 0; i < arr.length-1; i++) {// 用于优化排序boolean flg = false;for (int j = 0; j < arr.length-i-1; j++) {if (arr[j] > arr[j+1]) {swap(arr,j,j+1);}else {// 如果遍历一轮并没有任何交换,说明不存 arr[i+1] > arr[i]的情况flg = !flg;}}// flg == true 说明数组已经有序if (flg) {break;}}
}
private static void swap(int[] arr, int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;
}

六、快速排序

1.Hoare法

时间复杂度:O(N*logN)——每次都是均分待排列序列,O(N^2)——数据有序或者逆序

空间复杂度:最好情况——O(logN),最坏情况O(N),当N越大的时候,递归的就越深

稳定性:不稳定

private static void quickSort(int[] arr) {quick(arr,0,arr.length-1);
}private static void quick(int[] arr, int left, int right) {if (left >= right) {return;}int pivot = partitionHoare(arr,left,right);quick(arr,left,pivot-1);quick(arr,pivot+1,right);
}
private static int partitionHoare(int[] arr, int left, int right) {// 事先存储好 left 和 right 下标int i = left;int j = right;int pivot = arr[left];while (i < j) {// 一定要先走右边,不然可能会把大于 pivot 的值传到左边while (i < j && arr[j] >= pivot) {j--;}while (i < j && arr[i] <= pivot) {i++;}swap(arr,i,j);}swap(arr,i,left);return i;
}
private static void swap(int[] arr, int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;
}

2.挖坑法

private static void quickSort(int[] arr) {quickHole(arr,0,arr.length-1);
}private static void quickHole(int[] arr, int left, int right) {if (left >= right) {return;}int pivot = partitionHole(arr,left,right);quickHole(arr,left,pivot-1);quickHole(arr,pivot+1,right);
}
private static int partitionHole(int[] arr, int left, int right) {int key = arr[left];while (left < right) {while (left < right && arr[right] >= key) {right--;}arr[left] = arr[right];while (left < right && arr[left] <= key) {left++;}arr[right] = arr[left];}arr[left] = key;return left;
}
private static void swap(int[] arr, int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;
}

3.非递归实现快速排序

private static void quickSort(int[] arr) {Stack stack = new Stack<>();int left = 0;int right = arr.length-1;int pivot = partitionHole(arr,left,right);if (pivot > left+1) {stack.push(left);stack.push(pivot-1);}if (pivot < right-1) {stack.push(pivot+1);stack.push(right);}while (!stack.isEmpty()) {right = stack.pop();left = stack.pop();pivot = partitionHole(arr,left,right);if (pivot > left+1) {stack.push(left);stack.push(pivot-1);}if (pivot < right-1) {stack.push(pivot+1);stack.push(right);}}
}
private static int partitionHole(int[] arr, int left, int right) {int key = arr[left];while (left < right) {while (left < right && arr[right] >= key) {right--;}arr[left] = arr[right];while (left < right && arr[left] <= key) {left++;}arr[right] = arr[left];}arr[left] = key;return left;
}

七、归并排序

排序过程:先将原数组分成最小单元,然后再将这些最小的单元一个个有序的合并起来,如下图所示。

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

空间复杂度:O(N)

稳定性:稳定

private static void mergeSort(int[] arr) {mergeSortFunc(arr,0,arr.length-1);
}private static void mergeSortFunc(int[] arr, int left, int right) {// 结束条件if (left >= right) {return;}int mid = (left+right)/2;mergeSortFunc(arr,left,mid);mergeSortFunc(arr,mid+1,right);// 合并merge(arr,left,right,mid);
}
private static void merge(int[] arr, int start, int end, int midIndex) {int s1 = start;// 第一个归并段的起点int s2 = midIndex+1;// 第二个归并段的起点int[] tmp = new int[end-start+1];int k = 0;while (s1 <= midIndex && s2 <= end) {if (arr[s1] <= arr[s2]) {tmp[k++] = arr[s1++];}else {tmp[k++] = arr[s2++];}}// 当一个归并段中没有数据之后while (s1 <= midIndex) {tmp[k++] = arr[s1++];}while (s2 <= end) {tmp[k++] = arr[s2++];}// 将 tmp 中的数据拷贝回原数组for (int i = 0; i < k; i++) {arr[i+start] = tmp[i];}
}

非递归实现归并排序

private static void mergeSort(int[] arr) {int gap = 1;while (gap < arr.length) {for (int i = 0; i < arr.length; i += gap*2) {int s1 = i;int e1 = s1+gap-1;if (e1 >= arr.length) {e1 = arr.length-1;}int s2 = e1+1;if (s2 >= arr.length) {s2 = arr.length-1;}int e2 = s2+gap-1;if (e2 >= arr.length) {e2 = arr.length-1;}merge(arr,s1,e2,e1);}gap *= 2;}}
private static void merge(int[] arr, int start, int end, int midIndex) {int s1 = start;// 第一个归并段的起点int s2 = midIndex+1;// 第二个归并段的起点int[] tmp = new int[end-start+1];int k = 0;while (s1 <= midIndex && s2 <= end) {if (arr[s1] <= arr[s2]) {tmp[k++] = arr[s1++];}else {tmp[k++] = arr[s2++];}}// 当一个归并段中没有数据之后while (s1 <= midIndex) {tmp[k++] = arr[s1++];}while (s2 <= end) {tmp[k++] = arr[s2++];}// 将 tmp 中的数据拷贝回原数组for (int i = 0; i < k; i++) {arr[i+start] = tmp[i];}
}

八、计数排序

排序过程:记录每个数据 val 出现的次数和该数字的大小,数字每出现一次 count[val] 就加 1,然后遍历 count,将记下的数据写回原数组中。

时间复杂度:O(N+范围)

空间复杂度:O(范围)

稳定性:无法判定

public static void countSort(int[] array) {int maxVal = array[0];int minVal = array[0];
​for (int i = 0; i < array.length; i++) {if(array[i] < minVal) {minVal = array[i];}if(array[i] > maxVal) {maxVal = array[i];}}//就能确定当前数组的最大值 和 最小值int len = maxVal - minVal + 1;int[] count = new int[len];//开始遍历array数组 进行计数for (int i = 0; i < array.length; i++) {int val = array[i];count[val-minVal]++;
​}
​int index = 0;//array数组的下标for (int i = 0; i < count.length; i++) {//确保 当前count数组 可以检查完while (count[i] != 0) {array[index] = i+minVal;index++;count[i]--;}}
}

相关内容

热门资讯

前端-session、jwt 目录:   (1)session (2&#x...
linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
关于测试,我发现了哪些新大陆 关于测试 平常也只是听说过一些关于测试的术语,但并没有使用过测试工具。偶然看到编程老师...
前缀和与对数器与二分法 1. 前缀和 假设有一个数组,我们想大量频繁的去访问L到R这个区间的和,...
nodejs:本地安装nvm实... 一、背景-使用不同版本node的原因 vue3+ts、nuxt3版本,node...
JAVA集合知识整理 Java集合知识整理 HashMap相关 HashMap的底层数据结构:jdk1.8之...
无刷直流电机介绍及单片机控制实... 无刷直流电机介绍及单片机控制实例前言基本概念优势与劣势使用寿命基本结构使用单片机控制实例电子调速器&...
fwdiary(2) dp2 1.传纸条  AcWing 275. 传纸条 - AcWing 走两条路,走一条最大的...
常用的DOS命令 常用的DOS命令 DOS(Disk Operating System,磁...
<C++> 类和对象(下) 1.const成员函数将const修饰的“成员函数”称之为const成员函数,cons...