因为还在学习的阶段,其实我对于八个排序算法中的堆排序和归并排序还是挺懵的…
1、冒泡排序
冒泡排序是一个简单的排序算法,核心思想是两两比较,每次循环将最大(最小)的数移动到数组末端,然后进行第二次循环…直到数组有序。算法的时间复杂度为〇(n²),效率较低。
代码:
public static void bubbleSort(int[] arr) {
int n;
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[ j ] > arr[ j + 1 ]) {
n = arr[ j ];
arr[ j ] = arr[ j + 1 ];
arr[ j + 1 ] = n;
}
}
}
}
冒泡排序的效率很低,所以我们对他进行改进,试想一下,如果一个数组是这样的 [1,0,2,3,4,5],(这个数组确实太极端了,但是我们只是想有一个可以对算法改进的例子而已)
对它进行排序,其实在第一次交换后就已经有序了,但还是需要循环n²次,所以我们对它改进:
public static void bubbleSort(int[] arr) {
int n;
boolean flag;
for (int i = 0; i < arr.length - 1; i++) {
flag = true;
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[ j ] > arr[ j + 1 ]) {
n = arr[ j ];
arr[ j ] = arr[ j + 1 ];
arr[ j + 1 ] = n;
flag = false;
}
}
if (flag) {
break;
}
}
}
定义一个标志,如果需要交换就将标志赋值为false,每次外层循环开始时把标志置为true,内层循环遍历完数组后判断是否发生交换,若没有,falg的值应为true,说明数组已经有序,退出循环。
2、选择排序
选择排序的宗旨是每次循环选择最大或最小的数,放在数组的第一位,然后进入下一次循环…直至数组有序,选择排序的每次排序仅仅只交换需要交换的数,而不是两两交换,时间复杂度为〇(n²)。
代码:
public static void selectSort(int[] arr) {
int min;
int index;
for (int i = 0; i < arr.length - 1; i++) {
index = i;
min = arr[ i ];
for (int j = i + 1; j < arr.length; j++) {
if (min > arr[ j ]) {
min = arr[ j ];
index = j;
}
}
arr[ index ] = arr[ i ];
arr[ i ] = min;
}
}
还有个聊胜于无的优化,当需要交换的索引为自身的时候,不需要交换:
public static void selectSort(int[] arr) {
int min;
int index;
for (int i = 0; i < arr.length - 1; i++) {
index = i;
min = arr[ i ];
for (int j = i + 1; j < arr.length; j++) {
if (min > arr[ j ]) {
min = arr[ j ];
index = j;
}
}
if (index != i) {
arr[ index ] = arr[ i ];
arr[ i ] = min;
}
}
}
3、插入排序
插入排序的主要思想是把一个数组分为两个部分,前一个部分是有序的数组,后一个部分是无序的数组,每次把无序数组的第一个数插入到有序数组中,第一次数组第一个元素为一个有序的序列,时间复杂度为〇(n²)。
代码:
public static void insertionSort(int[] arr) {
int indexVal;
int index;
for (int i = 1; i < arr.length; i++) {
indexVal = arr[ i ];
index = i;
//在索引还能往前走的情况下,比较要插入有序序列的值 应该在哪个位置,
// 每次循环前一个元素把当前元素覆盖,相当于把比目标元素小的元素后移,直到找到可以插入的位置 退出
while (index - 1 >= 0 && indexVal < arr[ index - 1 ]) {
arr[ index ] = arr[ index - 1 ];
index--;
}
arr[ index ] = indexVal;
}
}
4、希尔排序
希尔排序是选择排序的一个进阶版本,对选择排序进行了改进,使得效率更高,平均时间复杂度为〇(n ㏒n),最坏时间复杂度接近于〇(n²)。
希尔排序又称为缩小增量排序,增量可以理解为把一个数组分为若干个小组,一般初始增量为数组长度的一半,然后每个小组进行选择排序,每次循环将增量折半,直到增量缩减为1,完成排序。其实比起增量我更喜欢叫步长,因为同一组元素的距离就是步长。
public static void shellSort(int[] arr) {
int len = arr.length;
int step = len;
int index;
int indexVal;
while (step != 0) {
step = step >> 1;
//step指的是步长,即隔step个数为一组,step也指的是每次第一组的第二个数(因为第一个数默认有序)
//例如:10/2=5,第一组第一个数是arr[0],第二个是arr[5]。。。5/2=2,第一组第一个数是arr[0],第二个数是arr[2]
for (int i = step; i < len; i++) {
index = i;
indexVal = arr[ i ];
if (arr[ i ] < arr[ i - step ]) {
while (index - step >= 0 && indexVal < arr[ index - step ]) {
arr[ index ] = arr[ index - step ];
index -= step;
}
arr[ index ] = indexVal;
}
}
}
}
5、快速排序
快排利用递归,每次递归将数组(不是完整的数组,因为方法需要两个参数,一个左边界,一个右边界)看做两半,取中心轴,然后把数组中比中轴小的数放在数组的左边,大的放在数组的右边,最后把中轴插在中心,再次对中轴的左子数组和右子数组进行相同的操作,最后数组有序。
我觉得快速排序要比归并排序更容易理解,而且jdk源码中Arrays工具类的sort方法使用的也是快速排序,平均时间复杂度为〇(n ㏒n),最坏时间复杂度为〇(n²)。
public static void quickSort(int[] arr, int left, int right) {
if (left > right) {
return;
}
int l = left;
int r = right;
//这个中轴取数组中的哪个数都是一样的,只是逻辑要修改一点
int pivot = arr[ l ];
while (l < r) {
while (l < r && arr[ r ] >= pivot) {
r--;
}
if (l < r) {
arr[ l ] = arr[ r ];
}
while (l < r && arr[ l ] <= pivot) {
l++;
}
if (l < r) {
arr[ r ] = arr[ l ];
}
if (l >= r) {
arr[ l ] = pivot;
}
}
quickSort(arr, left, l - 1);
quickSort(arr, l + 1, right);
}
/**
* 重载方法
* @param arr 要排序的数组
*/
public static void quickSort(int[] arr) {
quickSort(arr, 0, arr.length - 1);
}