排序---冒泡排序、快速排序、选择排序、插入排序、希尔排序
1.冒泡排序 O(n2)
基本思路:在要排序的一组数中,从第一个元素开始,依次对比当前元素和下一个元素,让较大的数往后沉,较小的数往前冒。即当发现相邻的两个数和排序的要求相反时,就交换它们的位置。
int[] arr = {5,8,2,4,9,1,3,6,7};
// 1. 冒泡排序。外层循环控制循环次数,内层循环比较数据
@Test
public void fun () {
for(int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length-1-i; j++) {
if (arr[j] > arr[j+1] ) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
out();
}
// 冒泡排序优化
@Test
public void fun1() {
boolean flag;
for(int i = 0; i < arr.length-1; i ++) {
// 标识,用来判断数组是否发生了交换
flag = false;
for (int j = 0; j < arr.length-1-i; j++) {
if (arr[j] > arr[j+1] ) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
flag = true;
}
}
// 当一轮下去之后,数组未发生任何交换,则表示排序已经完成
if (!flag) {
break;
}
}
}
2.快速排序
基本思想:选择一个基准元素a(通常找第一个元素),目标是让a左边的元素都小于a,右边的元素都大于a。具体方法是:从数组两端开始探测,左边初始为哨兵i,右边为哨兵j。从左往右依次找一个大于基准数a的数,从右往左依次找一个小于基准数a的数,交换它们。当两个哨兵相遇,交换哨兵所在位置的元素和基准数,一次探测结束。接着对左右两边分别进行探测,直至排序完成。右边的哨兵要先探测,因为左哨兵所在位置是的元素是小于基准数的,而当最终左右哨兵相遇的时候需要将基准数和哨兵所在位置交换,因此这时候交换的数应该是小于基准数的,即满足基准数左边的元素都小于它。
int[] arr = {5,8,2,4,9,1,3,6,7}; @Test public void fun2() { int low = 0; // 左哨兵位置,从左向右寻找小于基准数的数 int high = arr.length - 1; // 右哨兵位置,从右向左寻找大于基准数的数 quickSort(low, high); out(); } // 2.快速排序---哨兵法 private void quickSort(int low, int high) { if(low>high){ return; } int tmp = arr[low]; // 基准数 int i = low; int j = high; while (i < j) { // 先移动右哨兵,依次往左递减 while (arr[j] >= tmp && i < j) { j -- ; } // 再移动左哨兵,依次往右递增 while (arr[i] <= tmp && i < j) { i ++ ; } // 交换 if (i < j) { int flag = arr[i]; arr[i] = arr[j]; arr[j] = flag; } } // 此时跳出循环,表明两个哨兵相遇,搜寻结束,交换基准数和哨兵所在位置 arr[low] = arr[i]; arr[i] = tmp; // 对基准数左边进行排序,基准数位置现在在j和i quickSort(low, j - 1); // 对右半边进行排序 quickSort(j + 1, high); }
// 快速排序,填坑法 public void quickSort2(int low, int high) { while(low > high) { return; } int i = low; int j = high; int tmp = arr[low]; // 挖出arr[low]记为基准数,这是一个坑 while(i < j) { // 从右向左寻找比基准数tmp小的数,找到后填补arr[low]的坑,这时arr[j]变为新的坑 while(i < j && arr[j] >= tmp) { j--; } // 跳出循环,说明找到比基准数小的数了 if (i < j) { arr[i] = arr[j]; i++; } // 从左向右寻找比基准数大的数,找到后填补arr[j]的坑,这时arr[i]变为新的坑 while(i < j && arr[i] <= tmp) { i++; } if (i < j) { arr[j] = arr[i]; j--; } } //循环结束,表明i和j相遇,将最后一个坑填上 arr[i] = tmp; quickSort2(low, i - 1); quickSort2(i + 1, high); }
3.选择排序 O(n2)
基本思想:在长度为n的数组中,第一次遍历n-1个数,找到最小的数与第一个元素交换;第二次遍历n-2个数,找到最小的值与第二个元素交换... ...
/
/ 选择排序 @Test public void fun8() { int tmp; // 最后一个元素不需要再对其进行一次排序,因为前面的元素已经完成了排序,则最后一个元素必定已排序过 for(int i = 0; i < arr.length - 1; i++) { int index = i; // 这里需要对i之后的所有元素都比较一下,防止有漏掉的 for (int j = i; j < arr.length; j++) { if (arr[j] < arr[index]) { index = j; } } if (index != i) { tmp = arr[i]; arr[i] = arr[index]; arr[index] = tmp; } } out(); }
4.插入排序 O(n2)
基本思想:在要排序的一组数中,假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使得这n个数也是有序的。如此反复循环,直到全部排好序。
相同的场景
//插入排序 @Test public void fun13() { for(int i = 1; i < len; i++) { for(int j = i; j > 0; j--) { if (arr[j] < arr[j - 1]) { int tmp = arr[j]; arr[j] = arr[j-1]; arr[j-1] = tmp; } } } out();
}
5.希尔排序
基本思想:这个排序又称为缩小增量排序。设待排序数组长度为n,取一个整数index(小于n)作为间隔将数组分为index个子序列,所有距离为index的元素放在一个子序列中,对每一个子序列分别进行直接插入排序。然后缩小index,重复上述子序列划分和排序工作。直至最后取到index=1将所有元素放在一个序列中进行排序。 由于开始时,index的取值比较大,每一个序列中的元素比较少,因此排序速度很快。到后面index逐渐增大,子序列中元素个数增加,但由于前面的工作基础,大多数元素已经是有序状态,所以排序速度依然很快。
// 希尔排序 @Test public void fun14() { int index = len; while(true) { index = index/3 + 1; // 增量,每次在当前增量的基础上除3再加1 for(int i = 0; i < index; i++) { // 根据增量进行分组,一共分为index个组 for(int k = i+index; k < len; k+=index) { // 进行插入排序 for(int j = k; j > i; j-=index){ if (arr[j] < arr[j - index]) { int tmp = arr[j]; arr[j] = arr[j-index]; arr[j-index] = tmp; } } } } if (index == 1) { break; } } out(); }
参考博客:https://www.runoob.com/w3cnote/sort-algorithm-summary.html
https://www.jianshu.com/p/40dcc3b83ddc

浙公网安备 33010602011771号