巧记八种排序算法
通过自己编的一句话巧记八种排序算法:冒险选择插入相同的哈希值,为了尽快归来,减少垃圾堆积。分别对应着:
- 冒泡排序
- 选择排序
- 插入排序
- 希尔排序
- 快速排序
- 归并排序
- 堆排序
- 基数排序 下面将逐一介绍并代码实现。
冒泡排序
基本思想
两个数比较大小,较大的数下沉,较小的数冒起来。
步骤
(1)从后向前比较;
(2)比较相邻的两个数据,如果第二个数小,就交换位置;
(3)两层循环,内层通过flag,如果没有进行任何交换,则可以提前结束。
复杂度
时间复杂度为O(n2)
代码实现
package com.example.quickdemo.sort;
import java.util.Arrays;
public class BubbleSort {
public static void main(String[] args) {
int[] arr = {2, 5, 21, 88, 13, 7, 3, 20, 1, 65, 19};
bubbleSort(arr);
System.out.println(Arrays.toString(arr));
}
private static void bubbleSort(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
for (int i = 0; i < arr.length - 1; i++) {
boolean flag = false;
for (int j = arr.length - 1; j > 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;
}
}
}
}
选择排序
基本思想
在长度为N的无序数组中,遍历一遍找到最小值,然后要第一个元素交换,不断重复
复杂度
时间复杂度为O(n2)
代码实现
package com.example.quickdemo.sort;
import java.util.Arrays;
public class SelectionSort {
public static void main(String[] args) {
int[] arr = {2, 5, 21, 88, 13, 7, 3, 20, 1, 65, 19};
selectionSort(arr);
System.out.println(Arrays.toString(arr));
}
private static void selectionSort(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
for (int i = 0; i < arr.length - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
if (minIndex != i) {
int temp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = temp;
}
}
}
}
插入排序
在要排序的一组数中,假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使得这n个数也是排好顺序的,如此循环,直到全部排好顺序。
类似抓扑克牌时插入牌的过程。
复杂度
时间复杂度为O(n2)
代码实现
package com.example.quickdemo.sort;
import java.util.Arrays;
public class InsertSort {
public static void main(String[] args) {
int[] arr = {2, 5, 21, 88, 13, 7, 3, 20, 1, 65, 19};
insertSort(arr);
System.out.println(Arrays.toString(arr));
}
private static void insertSort(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
for (int i = 0; i < arr.length - 1; i++) {
for (int j = i + 1; j > 0; j--) {
if (arr[j] < arr[j - 1]) {
int temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
} else {
break;
}
}
}
}
}
希尔排序
基本思想
在要排序的一组数中,根据某一增量分为若干子序列,并对子序列分别进行插入排序;
然后逐渐将增量减小,并重复上述过程,直至增量为1,此时数据序列基本有序,最后进行插入排序。
技巧
最外层是while(gap>=1);
里面三层for循环,第一层表示分为多少组,第二层和第三层是原来的插入排序。
代码实现
package com.example.quickdemo.sort;
import java.util.Arrays;
public class ShellSort {
public static void main(String[] args) {
int[] arr = {2, 5, 21, 88, 13, 7, 3, 20, 1, 65, 19};
shellSort(arr);
System.out.println(Arrays.toString(arr));
}
private static void shellSort(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
int gap = arr.length / 2;
while (gap >= 1) {
for (int i = 0; i < gap; i++) { // 分的组数
for (int j = i + gap; j < arr.length; j += gap) {
for (int k = j; k > i; k -= gap) {
if (arr[k] < arr[k - gap]) {
int temp = arr[k];
arr[k] = arr[k - gap];
arr[k - gap] = temp;
} else {
break;
}
}
}
}
gap /= 2;
}
}
}
快速排序
基本思想
是分治思想
步骤
(1)先从数列中取出一个数作为Key值(如取数组的第一个数);
(2)将比这个数小的数全部放在它的左边,大于或等于它的数放在它的右边,等于归属某一边;
(3)对左右两个小数列重复第二步,直至各区间只有一个数(即递归条件为left < right,采用前序遍历)
代码实现
package com.example.quickdemo.sort;
import java.util.Arrays;
public class QuickSort {
public static void main(String[] args) {
int[] arr = {2, 5, 21, 88, 13, 7, 3, 20, 1, 65, 19};
quickSort(arr);
System.out.println(Arrays.toString(arr));
}
private static void quickSort(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
sort(arr, 0, arr.length - 1);
}
private static void sort(int[] arr, int low, int high) {
if (low >= high) {
return;
}
int key = arr[low];
int i = low;
int j = high;
while (i < j) {
while (i < j && key <= arr[j]) {
j--;
}
if (i < j) {
arr[i] = arr[j];
i++;
}
while (i < j && key > arr[i]) {
i++;
}
if (i < j) {
arr[j] = arr[i];
j--;
}
}
arr[i] = key;
sort(arr, low, i - 1);
sort(arr, i + 1, high);
}
}
归并排序
基本思想
是分治思想,是一种建立在归并操作上的算法。
步骤
(1)提取方法实现两个有序的数组合并成一个数组:互相比较,谁小先取谁,指针后移一位,直到一个为空,然后把不为空的数组元素逐个添加到新数组上结束;
(2)通过递归,不断细分数组直到size为1,然后进行两两归并——后序遍历
代码实现
package com.example.quickdemo.sort;
import java.util.Arrays;
public class MergeSort {
public static void main(String[] args) {
int[] arr = {2, 5, 21, 88, 13, 7, 3, 20, 1, 65, 19};
mergeSort(arr);
System.out.println(Arrays.toString(arr));
}
private static void mergeSort(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
sort(arr, 0, arr.length - 1);
}
private static void sort(int[] arr, int low, int high) {
if (low >= high) {
return;
}
int mid = low + (high - low) / 2;
sort(arr, low, mid);
sort(arr, mid + 1, high);
sortTwoArray(arr, low, mid, high);
}
private static void sortTwoArray(int[] arr, int low, int mid, int high) {
int[] temp = new int[high - low + 1];
int firstLow = low;
int firstHigh = mid;
int secondLow = mid + 1;
int secondHigh = high;
int index = 0;
while (firstLow <= firstHigh && secondLow <= secondHigh) {
if (arr[firstLow] < arr[secondLow]) {
temp[index] = arr[firstLow];
index++;
firstLow++;
} else {
temp[index] = arr[secondLow];
index++;
secondLow++;
}
}
while (firstLow <= firstHigh) {
temp[index] = arr[firstLow];
index++;
firstLow++;
}
while (secondLow <= secondHigh) {
temp[index] = arr[secondLow];
index++;
secondLow++;
}
for (int i = 0; i < index; i++) {
arr[low + i] = temp[i];
}
}
}
堆排序
基本思想
将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;将堆顶元素与末尾元素交换,将最大元素“沉”到数组末端;重新调整结构,使其满足堆定义,然后交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
技巧
(1)构建堆时从arr.length/2 - 1的索引位置开始(即最后一个非叶子节点),然后从下到上,做右到左,也就是i--;
(2)节点i的左子节点索引为2i+1,右子结点索引为2i+2;
(3)进行元素交换之后,只需要进行一次调整就行(调整0的位置),不再需要完整的重建过程。
复杂度
时间复杂度O(NlogN),没有空间复杂度。
代码实现
package com.example.quickdemo.sort;
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int[] arr = {2, 5, 21, 88, 13, 7, 3, 20, 1, 65, 19};
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
private static void heapSort(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
// 构建堆,从最后一个非叶子结点开始,从下往上,从右往左
for (int i = arr.length / 2 - 1; i >= 0; i--) {
adjustHeap(arr, i, arr.length);
}
for (int i = arr.length - 1; i > 0; i--) {
swap(arr, 0, i);
adjustHeap(arr, 0, i);
}
}
/**
* @param arr 数组
* @param parent 调整的结点
* @param length 数组长度
*/
private static void adjustHeap(int[] arr, int parent, int length) {
int temp = arr[parent];
int childIndex = parent * 2 + 1; // 左子结点
while (childIndex < length) {
// 选择左右子结点中较大的
if (childIndex + 1 < length && arr[childIndex] < arr[childIndex + 1]) {
childIndex++;
}
// 注意:一直都和初始值temp相比
if (arr[childIndex] > temp) {
arr[parent] = arr[childIndex];
parent = childIndex;
childIndex = parent * 2 + 1;
} else {
break;
}
}
arr[parent] = temp;
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
基数排序
基本思想
按照位数进行排序,比如从低到高位进行排序,先根据最低位进行排序,排到最高位结束;需要找到最大值的位数是多少,即将会进行多少轮排序。
技巧
原数组放到临时数组时,记得从后往前排序
代码实现
package com.example.quickdemo.sort;
import java.util.Arrays;
public class RadixSort {
public static void main(String[] args) {
int[] arr = {2, 5, 21, 88, 13, 7, 3, 20, 1, 65, 19};
radixSort(arr);
System.out.println(Arrays.toString(arr));
}
private static void radixSort(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
// 找到最大值位数
int bitCount = maxBit(arr);
int length = arr.length;
int[] temp = new int[length];
int[] count = new int[10];
int radix = 1;
for (int bit = 1; bit <= bitCount; bit++) {
// 初始化临时数组
Arrays.fill(count, 0);
// 计算元素归属于哪个桶
for (int i = 0; i < length; i++) {
int index = (arr[i] / radix) % 10;
count[index]++;
}
// 将count的中值从第一个桶到当前桶的总数和
for (int i = 1; i < 10; i++) {
count[i] += count[i - 1];
}
// 将原始数组元素根据当前位的排序结果放到临时数组中,从后往前面,因为后面的值大
for (int i = length - 1; i >= 0; i--) {
int index = (arr[i] / radix) % 10;
count[index]--;
temp[count[index]] = arr[i];
}
// 将临时数组的元素拷贝回原始数组
for (int i = 0; i < length; i++) {
arr[i] = temp[i];
}
radix *= 10;
}
}
private static int maxBit(int[] arr) {
int length = arr.length;
int maxValue = arr[0];
for (int i = 1; i < length; i++) {
if (maxValue < arr[i]) {
maxValue = arr[i];
}
}
int indexCount = 1;
while (maxValue > 10) {
maxValue /= 10;
indexCount++;
}
return indexCount;
}
}
参考资料