目录
1.插入排序
设我们有个array[n]数组需要排序,当插入第i(i >=1)个元素的时候,前面i - 1个元素已经排好序,此时,我们用第i个元素与前面的i - 1的元素进行比较,找到要插入的位置,而原来位置上的元素向后移动就可以了。
比如:当i == 1时,array[i] = 3,我们要将array[i] 和 array[i -1]比较,发现array[i] 比
array[i - 1]小
那么我们就将array[i]插入到array[i - 1]前,而array[i - 1]往后移一位就可以了。
又比如:当i == 2时,array[i] = 5,我们要将array[i] 和 array[i -1]、 array[i -2] ... array[0]比较,发现array[i] 比array[0]小
那么我们就将array[i]插入到array[0]前,而array[0]、array[1]、array[2]...array[i - 1]往后移一位就可以了。
上代码:
public static void insertSort (int[] array) {
for (int i = 1; i < array.length; i++) {
// 因为前1个元素已经有序,所以我们从第2个元素(array[1])开始与第1个元素(array[0])进行比较
int tmp = array[i];//将需要进行插入的第i个元素进行备份。
int j = i -1;
for (j = i - 1; j >= 0; j--) {//从第i - 1个元素开始,逐步向前比较
if (array[j] > tmp) {
array[j + 1] = array[j];//比要插入的元素大的就往后移动
} else {
//如果不小于了就停止比较,此时j指向的是比要插入的元素小的元素。
// 或者在i == 1时,且array[0]大于要插入的元素array[1]时,j会等于 -1;
break;
}
}
array[j + 1] = tmp;//找到要插入的位置
}
}
插入排序的特性:
1.元素集合越接近有序,插入排序算法的时间效率越高。元素越接近有序,那么在插入过程中,其余元素向后移位的次数就越少。
2.时间复杂度:。
3.空间复杂度:O(1)。
4,稳定性:稳定
2.希尔排序
希尔排序可以看成是优化之后的插入排序。利用插入排序元素集合越接近有序,插入排序算法的时间效率越高的特性来进行优化。
希尔排序法又称缩小增量法。希尔排序的基本思想是:选定一个整数gap,把待排序数组array分成gap个组,将所有距离为gap的元素分在同一组内,并对每一组进行插入排序。然后将gap不断缩小,重复上述的分组和插入排序的工作,当gap == 1时,即所有元素都在同一个组中,再次排好序。
假设我们有10个元素的数组,我们取gap = 5,这个数组分成5组,对它们分别进行插入排序:
取gap = 2,这个数组分成2组,对它们分别进行插入排序:
最后取gap = 1,这个数组变成1组,对它进行插入排序,就完成啦~~
上代码:
public static void shellSort(int[] array) {
int gap = array.length;
while (gap > 1) {
gap = (gap / 3) + 1;//+1的目的时为例避免出现gap = 0的情况发生,
// 比如当gap = 2的时候,直接除以3就会等于0
for (int i = gap; i < array.length; i++) {
//从第gap个元素开始
int tmp = array[i];
int j = i - gap;//要和插入的元素距离为gap的元素进行比较,及与同一组的元素进行比较
for (j = i - gap; j >= 0; j -= gap) {
if (array[j] > tmp) {
array[j + gap] = array[j];
} else {
break;
}
}
array[j + gap] = tmp;
}
}
}
不难看出,当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
希尔排序的特性:
1.时间复杂度:希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些书中给出的希尔排序的时间复杂度都不固定,我们暂且按照到
来计算。
2.空间复杂度:O(1)
3,稳定性:不稳定
3.选择排序法
基本思想就是在array[i]到array[n - 1]个元素选择一个最小的元素与下标为i的元素进行交换。
比如i = 0的时候
从array[0]到array[9]中选一个最小的值,和array[0]交换位置,
当i = 1时,那就在array[1]到array[9]中选一个最小值,和array[1]交换位置
以此类推~~
上代码:
public static void swap(int[] array, int x, int y) {
int tmp = array[x];
array[x] = array[y];
array[y] = tmp;
}
public static void selectSort (int[] array) {
for (int i = 0; i < array.length; i++) {
int j = i + 1;
//在i + 1及往后的元素寻找比array[i]小的元素,
// 如果没有,则array[i]就是i到n之间最小的元素
int mini = i;//记录当前最小的元素的下标
for (j = i + 1; j < array.length; j++) {
if (array[j] < array[mini]) {
mini = j;
}
}
//此时mini为最小元素的下标,让它与第i个元素互换位置
swap(array, mini, i);
}
}
快速排序的特性:
1.时间复杂度:
2.空间复杂度:O(1)
3.稳定性:不稳定
4.堆排序
堆排序在之前我写的堆的文章中有详细介绍,大家可以康康:
(7条消息) 堆 --- 永不过时的数据结构_如画亦枫的博客-CSDN博客
上代码:
public static void swap(int[] array, int x, int y) {
int tmp = array[x];
array[x] = array[y];
array[y] = tmp;
}
public static void heapSort (int[] array) {
for (int j = (array.length - 1 - 1) / 2; j >= 0; j--) {
shiftDown(array, j, array.length);
}//建堆
for (int i = array.length - 1; i > 0; i--) {
shiftDown(array, 0, i);
swap(array, 0, i);
}
}
public static void shiftDown (int[] array, int parent, int end) {
int child = parent * 2 + 1;
while (child < end) {
if (child + 1 < end && array[child] < array[child + 1]) {
child++;
}
if (array[parent] < array[child]) {
swap(array, parent, child);
parent = child;
child = parent * 2 + 1;
}
else {
break;
}
}
}
堆排序的特性:
1.时间复杂度:
2.空间复杂度:O(1)
3.稳定性:不稳定
5.冒泡排序法
冒泡排序法原理:
① 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
② 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
③ 针对所有的元素重复以上的步骤,除了最后一个。
④ 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
上代码:
public static void swap(int[] array, int x, int y) {
int tmp = array[x];
array[x] = array[y];
array[y] = tmp;
}
public static void bubbleSort(int[] array) {
for (int i = 0; i < array.length; i ++) {
boolean flag = false;
for (int j = 0; j < array.length - i - 1; j ++) {
if (array[j] > array[j + 1]) {
swap(array, j, j + 1);
flag = true;
}
}
if (flag == false) {
break;//flag 还等于false就证明该数组在上述阶段没有进行交换操作,即已经有序,
//就不用再进行比较了
}
}
}
冒泡排序法的特征:
1.时间复杂度:
2.空间复杂度:O(1)
3.稳定性:稳定
6.快速排序法
基本原理:任取待排序元素序列中的某元 素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有 元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
上代码(挖坑法):
public static void quickSort(int[] array) {
quick(array, 0, array.length - 1);
}
public static void quick(int[] array, int left, int right) {
if (left >= right) {
return;
}
int tmp = array[left];//我们把下标为left的值作为基准值,并进行备份
int begin = left;
int end = right;
while (begin < end) {
while (begin < end && array[end] > tmp) {
end--;//注意一定要先从右边找起,找到比基准值tmp小的数
}
array[begin] = array[end];//把这个比基准值小的数移到左边
while (begin < end && array[begin] < tmp) {
begin ++;//找到比基准值tmp大的数
}
array[end] = array[begin];//把这个比基准值小的数移到右边
}
array[end] = tmp;//最后再将基准值填回去
quick(array, left, end - 1);//左边同理
quick(array, end + 1, right);//右边同理
}
//acWing写法
public static void quickSort(int[] array, int left, int right) {
if (left >= right) {
return;
}
int l = left - 1;
int r = right + 1;
int tmp = array[left + right >> 1];
while (l < r) {
while (array[++ l] < tmp);
while (array[-- r] > tmp);
if (l < r) {
swap(array, l, r);
}
}
quickSort(array, left, r);
quick(array, r + 1, right);
}
快速排序的特性:
1.快速排序的两种优化方式:1. 三数取中法选key 2. 递归到小的子区间时,可以考虑使用插入排序。
2.时间复杂度:
3.空间复杂度:到O(N)
4.稳定性:不稳定
7.归并排序
归并排序采用了分治的思想:
再来看看治阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤。
上代码:
public static void mergeSort(int[] array) {
merge(array, 0, array.length - 1);
}
public static void merge(int[] array, int left, int right) {
if (left >= right) {
return;
}
int mid = left + right >> 1;
merge(array, left, mid);//分治
merge(array, mid + 1, right);
int[] tmp = new int[right - left + 1];
int begin1 = left;
int begin2 = mid + 1;
int index = 0;
while (begin1 <= mid && begin2 <= right) {
if (begin1 <= mid && array[begin1] <= array[begin2]) {
tmp[index ++] = array[begin1 ++];
}
if (begin2 <= right && array[begin1] > array[begin2]) {
tmp[index ++] = array[begin2 ++];
}
}
while (begin1 <= mid) {
tmp[index ++] = array[begin1 ++];
}
while (begin2 <= right) {
tmp[index ++] = array[begin2 ++];
}
for (int i = 0; i < tmp.length; i++) {
array[i + left] = tmp[i];
}
}
归并排序特性:
1.时间复杂度:
2.空间复杂度:O(N)
3.稳定性:稳定
总结:
排序方法 | 平均情况 | 最好情况 | 最坏情况 | 辅助空间 | 稳定性 |
冒泡排序法 | O(n) | O(1) | 稳定 | ||
简单选择排序法 | O(1) | 不稳定 | |||
直接插入排序法 | O(n) | O(1) | 稳定 | ||
希尔排序 | O(1) | 不稳定 | |||
堆排序 | O(1) | 不稳定 | |||
归并排序 | O(N) | 稳定 | |||
快速排序 | 不稳定 |