冒泡排序
1、冒泡排序
1.1算法思想
冒泡排序算法每次比较相邻的两个数,如果左边的数小于右边的数,那么交换这两个元素,否则不交换,经过第一趟交换后,最大的元素肯定在最右边的位置上,然后对前n−1个元素重复上述操作。
1.2代码
public static void bubbleSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}//合法性检测
/*外层循环控制要比较的范围,范围每次减少1,第一次范围为arr.length
内层循环在范围内进行比较相邻两个数的大小,
如果前面的数大于后面的数则进行交换
*/
for (int i = arr.length - 1; i > 0; i--) {
for (int j = 0; j < i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
}
}
}
2、选择排序
2.1算法思想
选择排序在未排序的范围内找到最小值,并将最小值放在未排序范围内的第一个位置,直到未排序范围为0。
2.2代码
public static void selectionSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
/*
外层循环控制未排序的范围,每次减一
minIndex代表该未排序的范围内的最小值
如果该范围内出现比minIndex更小的数的话就交换
最后将minIndex与i位置的数进行交换
*/
for (int i = 0; i < arr.length - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
minIndex = arr[j] < arr[minIndex] ? j : minIndex;
}
swap(arr, i, minIndex);
}
}
3、插入排序
3.1算法思想
往有序区中插入一个元素,直到后面每个元素都插入到有序区。
3.2代码
public static void insertionSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}/*
外层循环控制要插入的元素,该元素正好位于有序区的外面
内层循环:j>=0保证不越界,
arr[j]>arr[j+1]保证该元素插入到有序区的合适位置,
然后交换,j--
*/
for (int i = 1; i < arr.length; i++) {
for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
swap(arr, j, j + 1);
}
}
}
3.3补充
在工程上,虽然插入排序的时间复杂度为O(n2)但,其常数项非常小,因此,在数据量很小的时候,选择插入排序,数据量<=60时。
4、快速排序
4.1算法思想
使用了分治的思想,取数组右边界位置为num,以num为分界,比num小的都放在num左边,比num大的都放在num右边,等于num的放在中间区,然后递归处理左右两边。
4.2代码
public static void quickSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
quickSort(arr, 0, arr.length - 1);
}
public static void quickSort(int[] arr, int l, int r) {
if (l < r) {
//swap(arr, l + (int) (Math.random() * (r - l + 1)), r);//该步骤是生成随机的右边界
int[] p = partition(arr, l, r);//p[0]是中间区的左边界
quickSort(arr, l, p[0] - 1);p[1]是中间区的右边界
quickSort(arr, p[1] + 1, r);
}
}
public static int[] partition(int[] arr, int l, int r) {
int less = l - 1;
int more = r + 1;
int num = arr[r];
while (l < more) {
if (arr[l] < num) {
swap(arr, ++less, l++);
} else if (arr[l] > num) {
swap(arr, --more, l);
} else {
l++;
}
}
//swap(arr, more, l);
return new int[] { less + 1, more - 1 };
}
4.3补充
快速排序虽然时间复杂度达到了O(nlogn),但是他是不稳定的算法,在工程中,需要排序的元素为基本数据类型时,采用快速排序。普通的快速排序与数据状况有关,因此工程上常用随机快速排序,即x不选用中间位置,而是随机产生一个数组中某一个位置的元素,可以规避数据状况。
5、归并排序
5.1算法思想
要从l到r上排序,就排序好l到mid,mid+1到r。然后合并左右两边。
递归处理左右两边。
5.2代码
//重载
public static void mergeSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
mergeSort(arr, 0, arr.length - 1);
}
public static void mergeSort(int[] arr, int l, int r) {
if (l == r) {
return;//只有一个元素的时候直接返回
}
int mid = l + ((r - l) >> 1);
//递归处理左右两边
mergeSort(arr, l, mid);
mergeSort(arr, mid + 1, r);
//合并左右两边的数组
merge(arr, l, mid, r);
}
public static void merge(int[] arr, int l, int m, int r) {
int[] help = new int[r - l + 1];//辅助数组,大小为r-l+1
int i = 0;//i指向help数组的0位置
int p1 = l;//p1指向左边数组的第一个位置
int p2 = m + 1;//p2指向右边数组的第一个位置
/*
当p1和p2有一个遍历完自己的数组时,必然有且只有一个没有遍历完
只需要将没有遍历完的数组黏合在help数组后。
*/
while (p1 <= m && p2 <= r) {
help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
while (p1 <= m) {
help[i++] = arr[p1++];
}
while (p2 <= r) {
help[i++] = arr[p2++];
}
//因为merge的时候在l到r上,所以排好序后应该从arr[l+1]处放回。
for (i = 0; i < help.length; i++) {
arr[l + i] = help[i];
}
}
5.3补充
虽然归并排序也是O(nlogn),但是要额外使用一个数组,不过归并排序是稳定的算法。因此在工程上,如果要排序的元素为POJO类型时,选用归并排序,为基本数据类型时,选用快速排序(不需要额外数组)。
6、堆排序
6.1算法思想
构建大根堆heapInsert,然后将根结点放在数组最后一个位置,最后一个位置就排好序了,然后将前面位置的重新构建大根堆heapify,重复上述过程。
6.2代码
public static void heapSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, i);
}
int size = arr.length;
swap(arr, 0, --size);
while (size > 0) {
heapify(arr, 0, size);
swap(arr, 0, --size);
}
}
/*
构建大根堆,index的父节点为(index-1)/2
*/
public static void heapInsert(int[] arr, int index) {
while (arr[index] > arr[(index - 1) / 2]) {
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
/*
heapify()用来形成,在数组arr上,使index位置上的元素发生变化,
为了重新构建大根堆,就要使index与左右孩子比较,如果小就下沉,大就跳出
heapSize表示从0到heapSize-1范围上已经形成大根堆
*/
public static void heapify(int[] arr, int index, int heapSize) {
int left = index * 2 + 1;//index的左孩子
while (left < heapSize) {//保证左孩子存在
int largest;
if(left + 1 < heapSize)//如果右孩子存在
largest = arr[left + 1] > arr[left] ? left + 1 : left;
largest = arr[largest] > arr[index] ? largest : index;
if (largest == index) {//如果比左右两个孩子都大,
break; //直接跳出循环
}
swap(arr, largest, index);
index = largest;
left = index * 2 + 1;
}
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}