目录
一、冒泡排序算法
冒泡排序(Bubble Sorting)思想:
通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就像水底下的气泡一样逐渐向上冒。
以数组[3,9,-1,10,-2]为例 ,每趟排序结束完,数组后面多出一个最大数,后一趟比前一趟就少了一个比较的数,即每一趟排序的次数在逐渐减少。
优化:如果我们发现在某趟排序中,没有发生一次交换,可以提前结束冒泡排序。
public static void bubbleSort(int[] arr) {
System.out.println("原始数组~"+Arrays.toString(arr));
int temp = 0;//临时变量做交换用
boolean flag = false;//表示是否进行过交换
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]) {
flag = true;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
if (!flag) { //如果在这趟排序中,一次交换都没有发生过
break;
} else {
flag = false;//超级重要,重置flag,进行下次判断
}
System.out.println("第" + (i + 1) + "趟排序~"+Arrays.toString(arr));
}
}
二、选择排序算法
选择排序(SelectSorting)思想:
- 遍历整个序列,将最小的数放在最前面。 假定当前数是最小数,然后和后面每个数进行比较,如果发现有比当前数更小的数,就重新确定最小数,并得到下标。当遍历到数组的最后,就得到本轮最小数和下标,交换。
- 遍历剩下的序列,将最小的数放在最前面。
- 重复第二步,直到只剩下一个数。
public class SelectSort {
public static void main(String[] args) {
int[] arr = {32, 43, 23, 13, 5};
selectSort(arr);
}
public static void selectSort(int[] arr){
System.out.println("原始数组~"+Arrays.toString(arr));
for (int i = 0; i < arr.length - 1; i++) {
int min = arr[i];//假设的最小值索引
int minIndex = i;//假设的最小值
for (int j = i + 1; j < arr.length; j++) {
if (min > arr[j]) {
min = arr[j];//更新最小值
minIndex = j;//更新最小值索引
}
}
if (minIndex != i) {//如果发生过更新,代表要进行交换
arr[minIndex] = arr[i];//前面的假定最小值的地位替换掉后面的真实最小值的地位
arr[i] = min;//真实最小值替换掉前面的假定最小值
}
System.out.println("第" + (i + 1) + "趟排序~"+Arrays.toString(arr));
}
}
}
三、插入排序算法
插入排序(InsertionSorting)思想:
- 将第一个数和第二个数排序,然后构成一个有序序列。
- 将第三个数插入这个有序序列,构成一个新的有序序列(和打麻将插牌一样)。
- 对第四个数、第五个数......直到最后一个数,重复第二步。
(1)从第2个数开始作为待插入的数,将有序序列从后往前遍历。
(2)当前一个数一直比待插入的数大,则将前一个数后移,继续往前。
(3)回到循环遍历条件,继续比较前一个数和待插入数的大小。遍历条件不满足循环结束,找到待插入的位置,将待插入的数插入。
public static void insertionSort(int[] arr) {
System.out.println("原始数组~" + Arrays.toString(arr));
for (int i = 1; i < arr.length; i++) {
int target = arr[i];//从第2个数开始作为待插入的数
int j = i;//j从i值开始记录
//将有序序列从后往前遍历
// 1.当前一个数一直比待插入的数大,则将前一个数后移,继续往前
// 2.继续比较前一个数和待插入的数的大小
while (j > 0 && arr[j - 1] > target) {
arr[j] = arr[j - 1];//将前一个数后移
j--;//继续往前
}
//找到待插入的位置,将待插入的数插入
arr[j] = target;
System.out.println("第" + i + "趟排序~" + Arrays.toString(arr));
}
}
缺点: 当需要插入的数是较小的数时,后移的次数明显增多,对效率有影响.
四、希尔排序算法
希尔排序(Shell Sorting)思想:
1959年Shell发明,第一个突破O(n2)的排序算法,希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,仅增量因子为1 时,整个序列作为一个表来处理,算法便终止。
比较在希尔排序中是最主要的操作,而不是交换。用这样步长序列的希尔排序比插入排序和堆排序都要快,甚至在小数组中比快速排序还快,但是在涉及大量数据时希尔排序还是比快速排序慢。
public class SortTest {
public static void main(String[] args) {
int[] arr = {2, 3, 4, 5, 6, 7, 1, 8, 9,0};
shellSort(arr, arr.length / 2);
System.out.println(Arrays.toString(arr));
//缩小增量
shellSort(arr, arr.length / 2 / 2);
System.out.println(Arrays.toString(arr));
//缩小增量
shellSort(arr, arr.length / 2 / 2 / 2);
System.out.println(Arrays.toString(arr));
}
public static void shellSort(int[] arr, int gap) {
//外层循环遍历每一个组,每个组在内层循环进行插入排序
for (int k = 0; k < gap; k++) {
//插入排序
for (int i = k + gap; i < arr.length; i += gap) {
int target = arr[i];
int j = i;
while (j > gap- 1 && arr[j - gap] > target) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = target;
}
}
}
}
进行改进,把缩小增量的过程,加入到排序方法体内部。
public class SortTest {
public static void main(String[] args) {
int[] arr = {2, 3, 4, 5, 6, 7, 1, 8, 9,0};
shellSort(arr);
}
public static void shellSort(int[] arr) {
// 增量 gap, 并逐步的缩小增量
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int k = 0; k < gap; k++) {
//直接插入排序
for (int i = k + gap; i < arr.length; i += gap) {
int target = arr[i];
int j = i;
while (j > gap - 1 && arr[j - gap] > target) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = target;
}
}
System.out.println("增量:"+gap+"-->"+Arrays.toString(arr));
}
}
}