| 排序方法 | 平均情况 | 最好情况 | 最坏情况 | 辅助空间 | 稳定型 |
|---|---|---|---|---|---|
| 冒泡排序 | O(n2) | O(n) | O(n2) | O(1) | 稳定 |
| 简单选择排序 | O(n2) | O(n2) | O(n2) | O(1) | 稳定 |
| 插入排序 | O(n2) | O(n) | O(n2) | O(1) | 稳定 |
| 希尔排序 | O(nlogn) ~ O(n2) | O(n1.3) | O(n2) | O(1) | 不稳定 |
| 堆 | O(nlogn) | O(nlogn) | O(nlogn) | O(1) | 不稳定 |
| 归并排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) | 稳定 |
| 快速排序 | O(nlogn) | O(nlogn) | O(n2) | O(1)~O(n) | 不稳定 |
冒泡排序:
相邻元素两两比较,将较大值移到后面。
tips:可以引入哨兵机制,在最好情况下达到O(n)时间复杂度。
public int[] bubbleSort(int[] arr){
int len = arr.length;
// 哨兵
boolean flag = false;
for(int i = 0;i < len; ++i){
for(int j = 0; j < len - i - 1; ++j){
if(arr[j] > arr[j + 1]){
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
flag = true;
}
}
if(!flag){
break;
}
}
return arr;
}
简单选择排序:
给数组某一位,选择该位置后面所有数据中最小的数填入。
tips:无论如何要把所有的位置遍历一遍,要把这个位置后面所有的数据遍历一遍。
public int[] selectSort(int[] arr){
int len = arr.length;
for (int i = 0; i < len; ++i ) {
int sub = i;
int min = arr[i];
for (int j = i; j < len; ++j) {
if (arr[j] < min) {
min = arr[j];
sub = j;
}
}
int tmp = arr[i];
arr[i] = arr[sub];
arr[sub] = tmp;
}
return arr;
}
插入排序:
这个画图理解的话,思路就会清晰不少。
public void insertSort(int[] arr) {
int len = arr.length;
for (int i = 1; i < arr.length; i++){
int j = i - 1;
int tmp = arr[i];
while(j >= 0 && tmp < arr[j]){
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = tmp;
}
}
希尔排序:
简单插入排序中,插入的数字会造成之前大量已经排好序的数组再次发生移动。而希尔排序可以避免跨度过大的插入出现。他是将数组按一定的规律分成很多的小区间,在上面执行插入排序,然后区间范围逐渐扩大,并最终达到整个区间的排序。
需要注意的是gap的终止条件是gap >= 1。
public void shellSort(int[] arr) {
if (arr == null) {
return;
}
int len = arr.length;
for (int gap = len / 2; gap >= 1; gap /= 2) {
for (int i = gap; i < len; i += gap) {
int j = i - gap;
int tmp = arr[i];
while (j >= 0 && arr[j] > tmp) {
arr[j + gap] = arr[j];
j -= gap;
}
arr[j + gap] = tmp;
}
}
}
堆排序:
堆排序是基于堆这种数据结构来的,是一种完全二叉树,并且衍生出了大顶堆和小顶堆。大顶堆就是根节点比所有的子节点值都大,小顶堆就是根节点比所有子节点值都小。
堆排序的流程是:
- 将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆
- 将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端
- 重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
参考资料:
图解排序算法(三)之堆排序
归并排序:
把数组递归平分,当只剩一个元素的时候,出栈按大小顺序合并。
tips:合并的时候,需要另创建一个数组有序保存待合并的数组,所以空间O(n);合并O(n),递归O(logn),故总的O(nlogn)。动图链接:[图解] 归并排序
这里有一个小技巧,就是遇到乘法或者除法的时候,尽量都将他们转换为位移运算,会提高运算效率。
public void mergeSort(int[] arr){
if (arr == null) {
return;
}
int len = arr.length;
int[] tmp = new int[len];
mergeSort_c(arr, 0, len - 1, tmp);
}
private void mergeSort_c(int[] arr,int start, int end, int[] tmp){
if (start < end) {
// 采用右移,提高计算的速度。
int mid = ((end - start) >> 1) + start;
mergeSort_c(arr, start, mid, tmp);
mergeSort_c(arr, mid + 1, end, tmp);
merge(arr, start, mid, end, tmp);
}
}
private void merge(int[] arr, int start, int mid, int end, int[] tmp){
int i = start, j = mid + 1;
int k = start;
while (i <= mid && j <= end){
if (arr[i] <= arr[j]) {
tmp[k++] = arr[i++];
}else{
tmp[k++] = arr[j++];
}
}
while(i <= mid){
tmp[k++] = arr[i++];
}
while(j <= end){
tmp[k++] = arr[j++];
}
for (int z = start; z <= end; z++) {
arr[z] = tmp[z];
}
}
快速排序:
数组中选定一个值,比他小的排左边,比他大的排右边,递归上述过程。
public void quickSort(int[] arr){
if (arr == null) {
return ;
}
quickSort_c(arr, 0, arr.length - 1);
}
public void quickSort_c(int[] arr, int start, int end){
if (start < end) {
int p = partition(arr, start, end);
quickSort_c(arr, start, p);
quickSort_c(arr, p + 1, end);
}
}
public int partition(int[] arr, int start, int end){
int tmp = arr[start];
int i = start, j = end;
while(i < j){
while(i < j && arr[j] >= tmp){
j--;
}
if (i < j && arr[j] < tmp) {
arr[i] = arr[j];
i++;
}
while(i < j && arr[i] <= tmp){
i++;
}
if (i < j && arr[i] > tmp) {
arr[j] = arr[i];
j--;
}
}
arr[i] = tmp;
return i;
}
有几种优化快速排序的方式:
- 分到一定区间采用插入排序。
- 利用三数取中法求的枢纽元。
- 尾递归优化。
- 将相同key的元素放在一起。
参考资料:
三种快速排序以及快速排序的优化

本文深入讲解了各种排序算法,包括冒泡排序、简单选择排序、插入排序、希尔排序、堆排序、归并排序和快速排序。详细分析了每种算法的时间复杂度、空间复杂度和稳定性,提供了算法实现代码,并探讨了优化策略。
19万+

被折叠的 条评论
为什么被折叠?



