数据结构常用排序算法分为两大类:
- 比较排序,时间复杂度为O(nlogn) ~ O(n^2),主要有:交换排序(冒泡排序、快速排序),选择排序(直接选择排序、堆排序),插入排序(直接插入、希尔排序),归并排序等
- 非比较排序,时间复杂度可以达到O(n),主要有:计数排序,基数排序,桶排序等
排序稳定性
排序算法稳定性的简单形式化定义为:如果Ai = Aj,排序前Ai在Aj之前,排序后Ai还在Aj之前,则称这种排序算法是稳定的。
引用下网上的图(https://blog.csdn.net/m0_37962600/article/details/81475585)
1、冒泡排序
/**
* 在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较,让较大的数往下沉,较小的往上冒。
* 即:每当两相邻的数比较后发现他们的排序与排序要求相反时,就将他们互换。
*
* @param arr
*/
public void bubbleSort(int[] arr) {
if (arr == null || arr.length == 1) {
return;
}
for (int i = 0; i < arr.length; i++) {
boolean flag = false;
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
flag = true;
}
System.out.println("排序流程: " + JSON.toJSONString(arr));
}
System.out.println("=====: " + JSON.toJSONString(arr));
if (!flag) break;
}
}
测试结果:
2、快速排序
该方法的基本思想是:
1.先从数列中取出一个数作为基准数。
2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
3.再对左右区间重复第二步,直到各区间只有一个数。
虽然快速排序称为分治法,但分治法这三个字显然无法很好的概括快速排序的全部步骤。因此我的对快速排序作了进一步的说明:挖坑填数+分治法(可参考:https://www.runoob.com/w3cnote/quick-sort.html)
public void quickSort(int[] arr, int low, int high) {
if (low < high) {
//获取基准数据的正确索引
int index = this.getIndex(arr, low, high);
System.out.println("基准下标index:" + index + " low: " + low + " high: " + high);
//进行迭代对index之前和之后的数组进行相同的操作(进行排序)
quickSort(arr, low, index - 1);
System.out.println("左边排序:" + JSON.toJSONString(arr));
quickSort(arr, index + 1, high);
System.out.println("右边排序:" + JSON.toJSONString(arr));
}
}
private int getIndex(int[] arr, int low, int high) {
//当前数组的第一个元素作为基准数
int temp = arr[low];
while (low < high) {
// 当队尾的元素大于等于基准数据时,向前挪动high下标
while (low < high && arr[high] >= temp) {
high--;
}
arr[low] = arr[high];
当队首元素小于等于tmp时,向后挪动low下标
while (low < high && arr[low] <= temp) {
low++;
}
arr[high] = arr[low];
}
// 跳出循环时low和high相等,此时的low或high就是tmp的正确索引位置;将tmp赋值给arr[low]
arr[low] = temp;
return low;
}
3、选择排序
/**
* 选择排序每次比较的是数组中特定索引的值与全数组中每个值的大小比较,每次都选出一个最小(最大)值,如果当前索引的值大于之后索引的值,则两者进行交换
*
* @param arr
*/
public void selectSort(int[] arr) {
if (arr == null || arr.length == 1) {
return;
}
for (int i = 0; i < arr.length; i++) {
int minIndex = i; //数组中特定索引的值
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
System.out.println("排序流程: " + JSON.toJSONString(arr));
}
}
排序测试:
5、插入排序
插入排序是一种最简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
插入排序和冒泡排序一样,也有一种优化算法,叫做拆半插入
/**
* 插入排序是一种最简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
* 插入排序和冒泡排序一样,也有一种优化算法,叫做拆半插入
*
* @param sourceArray
*/
public int[] insertSort(int[] sourceArray) {
// 对 arr 进行拷贝,不改变参数内容
int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
// 从下标为1的元素开始选择合适的位置插入,因为下标为0的只有一个元素,默认是有序的
for (int i = 1; i < arr.length; i++) {
// 记录要插入的数据
int tmp = arr[i];
// 从已经排序的序列最右边的开始比较,找到比其小的数
int j = i;
while (j > 0 && tmp < arr[j - 1]) {
arr[j] = arr[j - 1];
j--;
}
// 存在比其小的数,插入
if (j != i) {
arr[j] = tmp;
}
}
return arr;
}
更多可参考: