思想:
(1)相邻两个元素依次比较,保证右边元素大于左边(小于则交换位置),一轮结束后保证最后一个元素一定是最大的元素
(2)对剩下n-1个元素再次执行(1)
(3)第n-1轮执行后序列排序完成
算法 | 最好复杂度 | 最差复杂度 | 平均复杂度 | 空间复杂度 | 适用场景 | 稳定性 |
---|---|---|---|---|---|---|
冒泡 | O(n) | O(n^2) | O(n^2) | O(1) | 数据量小,有序 | 稳定 |
实现代码:
public static void bubbleSort(int data[]) { boolean didSwap; for (int i = 0; i < data.length - 1; i++) { didSwap = false; for (int j = 0; j < data.length - i - 1; j++) { if (data[j + 1] < data[j] ) {//加等于则不稳定 int temp = data[j + 1]; data[j + 1] = data[j]; data[j] = temp; didSwap = true; } } if (!didSwap) { return; } } }
思想:
(1)从序列中选出最大元素
(2)如果最大元素不是在最后一位,将其互换位置
(3)对剩下的n -1 个元素执行(1)(2)
算法 | 最好复杂度 | 最差复杂度 | 平均复杂度 | 空间复杂度 | 适用场景 | 稳定性 |
---|---|---|---|---|---|---|
直接选择 | O(n^2) | O(n^2) | O(n^2) | O(1) | 数据量小 | 稳定 |
注:选择排序稳定性跟代码实现方式有关,这种写法为稳定
代码:
public static int[] selectionSort(int data[]){ for (int i = 0; i < data.length -1; i++) { int maxIndex = 0; for (int j = 0; j < data.length - i; j++) { if (data[j] >= data [maxIndex]) {//加等于稳定 maxIndex = j; } } if (maxIndex != data.length - i -1) { int temp = data[data.length - i -1]; data[data.length - i -1] = data[maxIndex]; data[maxIndex] = temp; } } return data; }
思想:
(1)将第i元素与排好序的n个数的序列做比较(默认第一个元素为有序),如果有序序列中有元素比第i个位置的元素大,互换位置,直至排好序的所有元素与第i个位置的元素比较过则前n+1个元素有序
(2)重复(1), 直至整个序列有序
算法 | 最好复杂度 | 最差复杂度 | 平均复杂度 | 空间复杂度 | 适用场景 | 稳定性 |
---|---|---|---|---|---|---|
直接插入 | O(n) | O(n^2) | O(n^2) | O(1) | 数据量小,有序 | 稳定 |
代码:
public static int[] straightInsertionSort(int data[]) { for (int i = 1; i < data.length ; i++) { for (int j = 0; j < i; j++) { if (data[j] > data [i]) { int temp = data[j]; data[j] = data[i]; data[i]= temp; } } } return data; }
思想:
(1)将元素通过步长分组,组内用直接插入排序使其有序
(2)随着步长逐渐减小,每个分组的记录数越来越多,当步长减为1时,所有记录合成一组,所有记录有序
算法 | 最好复杂度 | 最差复杂度 | 平均复杂度 | 空间复杂度 | 适用场景 | 稳定性 |
---|---|---|---|---|---|---|
希尔排序 | O(n^1.3) | O(n^2) | O(n^2) | O(1) | 数据量大 | 不稳定 |
代码:
public static int[] shellSort(int data[]) { for (int gap = data.length/2; gap > 0; gap /=2) { //保证初始前面只有一个元素使其有序 然后每次插入和之前有序序列比较 for (int i = gap; i < data.length; i++) { straightInsertionSort(data,i,gap); } } return data; } private static void straightInsertionSort(int[] data, int index, int gap) { for (int j = index%gap; j <= index - gap; j += gap) {//同一组的和之前有序的元素比较交换 if (data[index] < data [j]) { int temp = data[index]; data[index] = data[j]; data[j] = temp; } } }
思想:
(1)单次排序时,选择第一个元素为基准数,i,j分别指向第一个元素和最后一个元素,j先向前扫描,若data[j] 大于等于基准数,则继续往前扫描,当data[j]小于基准数时,基准数和data[j] 互换位置,i向后扫描,若data[i] 小于等于基准数,则继续往前扫描,当data[i]大于基准数时,基准数和data[i]] 互换位置,然后j继续向前扫描,i,j交替扫描,直至i == j,则一次排序完成
(2)一次排序使基准数左边小于基准数,右边大于基准数
(3)对于基准数左边和右边元素重复(1),(2)
算法 | 最好复杂度 | 最差复杂度 | 平均复杂度 | 空间复杂度 | 适用场景 | 稳定性 |
---|---|---|---|---|---|---|
快速排序 | O(nlog2n) | O(n^2) | O(nlog2n) | O(nlog2n) | 数据量大 | 不稳定 |
public static void quickSort(int data[],int low,int high) { if (low < high) { int middleIndex = getMiddleIndex(data,low,high); quickSort(data,low,middleIndex - 1); quickSort(data,middleIndex + 1,high); } } private static int getMiddleIndex(int[] data, int low, int high) { int key = data[low]; while (low < high) { while (data[high] > key && low < high){ high --; } data[low] = data[high]; while (data[low] <= key && low < high) { low ++; } data[high] = data[low]; } data[low] = key; return low; }
思想:
(1)将无序序列构造为大顶或者小顶堆,每次取出堆顶元素,将堆尾元素放在堆顶,再次调整为大顶堆或者小顶堆,直至堆只剩最后一个元素
算法 | 最好复杂度 | 最差复杂度 | 平均复杂度 | 空间复杂度 | 适用场景 | 稳定性 |
---|---|---|---|---|---|---|
堆排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(1) | 数据量大 | 不稳定 |
代码:
public static void heapSort(int data[]) { //建堆 //i 指向最后一个非叶子节点 for (int i = data.length/2 - 1; i >= 0; i --) { adjustHeap(data,i,data.length); } for (int k = data.length -1; k > 0; k--) { //把最大元素放到堆尾,此时只需调整根节点 swap(data,0,k); adjustHeap(data,0,k); } } //每个非叶子节点都要调整堆使之成为大顶堆,并循环调整所有子树 private static void adjustHeap(int[] data, int i, int length) { for (int j = 2 * i + 1; j < length; j = 2 * j + 1) { //判断右子节点是否存在,j 指向子节点中较大的 if (j + 1 < length && data[j] < data[j + 1]) { j++; } if (data[j] > data[i]) { swap(data,i,j); //循环检查子树 只调整结构改变的 i = j; } else { break; } } } private static void swap (int data[],int i,int j) { int temp = data[i]; data[i] = data[j]; data[j] = temp; }
思想:
利用分治思想递归求解,先分解成有序序列(每组只剩一个元素),然后每个有序序列合并,直至整个序列有序
算法 | 最好复杂度 | 最差复杂度 | 平均复杂度 | 空间复杂度 | 适用场景 | 稳定性 |
---|---|---|---|---|---|---|
归并排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(n) | 数据量大 | 稳定 |
代码:
public static void mergeSort (int data[],int left,int right,int temp[]) { if (left < right) { int mid = (left + right)/2; mergeSort(data,left,mid ,temp); mergeSort(data,mid+1,right,temp); merge(data,left,right,mid,temp); } } private static void merge(int[] data, int left, int right, int mid, int[] temp) { int i = left; int j = mid + 1; int k = left; while (i <= mid && j <= right) { temp[k++] = data[i] <= data[j] ? data[i++]:data[j++]; } if (i > mid){ while (k <= right) { temp[k++] = data[j++]; } } if (j > right){ while (k <= right) { temp[k++] = data[i++]; } } for (int m = left; m <= right; m++) { data[m] = temp[m]; } }
思想:
从个位开始将排序的元素按个位的值放入0~9编号的桶中,再将得到的子序列按十位的值放入桶中,一直到最高位为止
算法 | 最好复杂度 | 最差复杂度 | 平均复杂度 | 空间复杂度 | 适用场景 | 稳定性 |
---|---|---|---|---|---|---|
基数排序 | O(d*(n+r)) | O(d*(n+r)) | O(d*(n+r)) | O(n+r) | 数据量大 | 稳定 |
其中,d 为位数(代码中为count),r 为基数(10),n 为原数组个数(data.length)。
代码:
public static void radixSort(int data[]){ int places = getMaxPlaces(data);//最大数的位数 int bucket[][] = new int [10][data.length]; //十个桶 int bucketCount[] = new int[10];//每个桶数据量 for (int i = 0; i < places; i++) { putBucket(data,i,bucketCount,bucket); getBucket(data,bucketCount,bucket); } } private static void getBucket(int[] data, int[] bucketCount, int[][] bucket) { int k = 0; for (int i = 0; i < 10; i++) { for (int j = 0; j < bucketCount[i]; j++) { data[k++] = bucket[i][j]; } bucketCount[i] = 0; } } //按第i位入桶 private static void putBucket(int[] data,int i,int bucketCount[],int bucket[][]) { int index = 0; for (int num : data) { index = (int)(num/Math.pow(10,i))%10; bucket[index][bucketCount[index]] = num; bucketCount[index]++; } } private static int getMaxPlaces(int[] data) { int max = data[0]; for (int i = 1; i < data.length; i++) { if (data[i] > max){ max = data[i]; } } int count = 0; //取最大值的位数 while (max > 0) { max = max/10; count ++; } return count; }