十大排序算法总结
一、冒泡排序
- 身世曰:冒泡排序可以誉为程序员跨入算法门槛的第一步,相信大家一定被冒泡排序一直萦绕在耳边。【且听冒泡吟】,冒泡乃排序家族之太上长老,掌门人不为过之。
- 闻之也:冒泡排序(Bubble Sort),乃计算机科学领域排序算法的简简易者。
- 知其身:它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行,直到没有相邻元素需要交换,也就是说该元素列已经排序完成。【百度百科】
- 名由来:这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。【百度百科】
附实录
/*
* 对数组a中的元素排序,改良时间复杂度,空间换时间,冗余flag变量
*/
public static void bubbleSort(Integer[] a) {
for (int i = 0; i < a.length - 1; i++) { // 外层循环,负责控制比较趟数
// 标志位,记录本趟循环是否进行了交换(即是否整体已经有序)
boolean flag = false;
for (int j = 0; j < a.length - 1 - i; j++) { // 内层循环,比较为冒泡的数
if (greater(a[j], a[j + 1])) {
exch(a, j, j + 1);
flag = true; // 记录已经交换过
}
}
if (!flag) break; // 本趟未交换,已经有序退出外层循环
}
}
知论之
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
赞曰
- 时间复杂度
- 最优:O(n) ,若待排序序列和期望序列顺序一致,则只许挨着扫描
- 最坏:O(n^2)
二、选择排序
- 身世曰:冒泡排序可以誉为程序员跨入算法门槛的第一步,相信大家一定被冒泡排序一直萦绕在耳边。【且听冒泡吟】,冒泡乃排序家族之太上长老,掌门人不为过之。
- 闻之也:冒泡排序(Bubble Sort),乃计算机科学领域排序算法的简简易者。
- 知其身:它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行,直到没有相邻元素需要交换,也就是说该元素列已经排序完成。【百度百科】
- 名由来:这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。【百度百科】
附实录
归并排序
归并排序乃二分法、递归值精华结合之。
概念:归并排序是一种分治的思想,将大问题拆解成为小问题,将一个大的数组先拆分成几个小的数组,然后再一点点的合并。
摘要:其排序思路中和快速排序算法一样使用到了递归的思想,同时在归并排序中还用到了一个算法,就是有序数组合并算法。配合递归与有序数组合并算法,归并排序能够高效且稳定的完成排序,归并排序的优点在于其时间复杂度低,稳定性高,但是缺点也是有的,那就是空间复杂度很高。
我们可以总结出归并排序的算法思路,那就是在将整个数组进行不断划分,知道划分的每个字数组的长度为0或者为1,这是每个字数组统统都是有序数组,这是再按照有序数组的拼接算法,对每个子数组进行拼接,这样就能保证每次的拼接结果都还是有序的最终拼接成一个之后,整个数组便都是有序的,而数组的排序也宣布完成,关于这个字数组的划分,实际上是通过递归实现的逻辑上的划分,接下来我们使用动图来看看这个排序过程:
归并排序的实现
public static void mergeSort(int[] arr,int left,int right){
if(left>=right){
return;
}
int mid = (left + right) / 2;
mergeSort(arr, left, mid);
mergeSort(arr, mid+1, right);
//上边为拆分过程
}
对于拆分过程实际上就是递归,我们不断进行左右的递归,并不断减小子数组的规模,最终便会减小到每个数组的规模为1或者为0,这里需要注意的是递归出口的判断条件为:left>=right,我们为什么不写成leftright呢?这是因为在右递归中,我们的左边界为mid+1,我们的mid是通过直接整除2得到的,当数组规模为1的时候,mid的运算结果为0,此时进入下一层递归时,左递归的左右边界都是0,下层左递归正常退出,但是右递归这是便出现了问题,这是右递归中的right = mid + 1,为1,如果使用leftright,就无法判断并终止这个情况了,所有此时我们还要加入一个新情况,那就是left>right,所以我们合写为left>=right。
之后我们来探讨合并的过程,在合并的时候我们需要使用到一个额外空间,在合并时,我们需要先将合并结果存放在那个额外的新空间上,然后再将新空间上的结果复制回我们的当前数组位置上,我们下面用一个例子来介绍,如图所示:
整体代码如下所示:
static int ans = 0;
public int InversePairs(int [] array) {
mergeSort(array, 0, array.length - 1);
return ans;
}
// 进行拆分
static void mergeSort(int [] array, int lt, int rt) {
// 终止条件
if (lt >= rt) return;
int mid = lt + (rt - lt >> 1);
mergeSort(array, lt, mid);
mergeSort(array, mid + 1, rt);
mergeSort(array, lt, mid, rt);
}
// 进行合并
static void mergeSort(int [] array, int lt, int mid, int rt) {
int[] tmp = new int[rt - lt + 1]; // 临时数组,长度为需要合并的数组长度
int lt1 = lt, rt1 = mid;
int lt2 = mid + 1, rt2 = rt;
int idx = 0;
while(lt1 <= rt1 && lt2 <= rt2) {
tmp[idx++] = array[lt1] < array[lt2] ? array[lt1++] : array[lt2++];
}
// 将有剩余的一边归入tmp
while(lt1 <= rt1) tmp[idx++] = array[lt1++];
while(lt2 <= rt2) tmp[idx++] = array[lt2++];
// 将tmp数组赋值到array中
for (int i = 0; i < tmp.length; i++){
array[lt + i] = tmp[i];
}
}
练习题:
题目:BM20数组中的逆序对、剑指 Offer 51. 数组中的逆序对
优化归并:
public int[] MySort (int[] arr) {
// write code here
int left = 0, right = arr.length - 1;
merge(left, right, arr);
return arr;
}
// 分
void merge(int left, int right, int[] arr) {
if (left >= right) {
return;
}
int mid = left + (right - left >> 1);
merge(left, mid, arr);
merge(mid + 1, right, arr);
// 优化,左右两边都有序且相对有序,则直接复制
if (arr[mid] <= arr[mid + 1]) {
System.arraycopy(arr, left, arr, left, right - left + 1);
return;
}
cure(left, mid, right, arr);
}
// 治
void cure(int left, int mid, int right, int[] arr) {
// 临时数组长度为当前区间长度
int[] tmp = new int[right - left + 1];
int idx = 0;
int l1 = left, r1 = mid;
int l2 = mid + 1, r2 = right;
while (l1 <= r1 && l2 <= r2) {
tmp[idx++] = arr[l1] < arr[l2] ? arr[l1++] : arr[l2++];
}
// 判断两边的剩余数,先左后右
while (l1 <= r1) {
tmp[idx++] = arr[l1++];
}
while (l2 <= r2) {
tmp[idx++] = arr[l2++];
}
// 赋值给arr数组
for (int i : tmp) {
arr[left++] = i;
System.out.println(left);
}
}
附录
具者类也:排序算法公用(交换和比较)
public static class CommonSortUtils {
/*
数组元素 i 和 j 交换位置方法
* */
public static void exch(Comparable[] a, int i, int j) {
Comparable temp;
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
/*
* 比较是否a > b的方法
* */
public static boolean greater(Comparable a, Comparable b) {
return a.compareTo(b) > 0;
}
/*
* 比较是否a < b的方法
* */
public static boolean less(Comparable a, Comparable b) {
return b.compareTo(a) > 0;
}
}