冒泡排序
通过对待排序序列从前往后,依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐向后移动。
public void bubbleSort(int[] a) {
int tmp;
boolean flag = false;
for (int i = 0; i < a.length - 1; i++) {
for (int j = 0; j < a.length - i - 1; j++) {
if (a[j] > a[j + 1]) {
tmp = a[j];
a[j] = a[j + 1];
a[j + 1] = tmp;
flag = true;
}
}
if (!flag) {
break;
} else {
flag = false;
System.out.println("第" + (i + 1) + "躺排序结果为:" + Arrays.toString(a));
}
}
}
选择排序
排序思想:
(1)第一次从a[0]~a[n-1]中选择最小值,与a[0]交换。
(2)第二次从a[1]~a[n-1]中选择最小值,与a[1]交换。
(3)第三次从a[2]~a[n-1]中选择最小值,与a[2]交换。
······
(i)第i次从a[i-1]~a[n-1]中选择最小值,与a[i-1]交换。
(n-1)第i次从a[n-2]~a[n-1]中选择最小值,与a[n-2]交换。
public void selectSort(int[] a) {
for (int i = 0; i < a.length - 1; i++) {
int minIndex = i;
int min = a[i];
for (int j = i + 1; j < a.length; j++) {
if (min > a[j]) {
min = a[j];
minIndex = j;
}
}
if (minIndex != i) {
a[minIndex] =a[i];
a[i] = min;
}
System.out.println("第" + (i + 1) + "轮排序结果为:" + Arrays.toString(a));
}
}
插入排序
基本思想:将n个待排序的元素看成为一个有序表和一个无序表,开始时,有序表中只包含一个元素,无序表中包含n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之称为新的有序表。
public void insertSort(int[] a) {
for (int i = 1; i < a.length; i++) {
int insertValue = a[i];
int insertIndex = i - 1;
while (insertIndex >= 0 && insertValue < a[insertIndex]) {
a[insertIndex + 1] = a[insertIndex];
insertIndex--;
}
if ((insertIndex + 1) != i) {
a[insertIndex + 1] = insertValue;
}
System.out.println("第" + i + "轮排序结果为:" + Arrays.toString(a));
}
}
希尔排序
基本思想:把记录按下标的一定增量进行分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法终止。
// 有序数据插入时采用交换法
public void shellSort(int[] a) {
int tmp;
int step = a.length / 2;
int cnt = 0;
while (step > 0) {
for (int i = step; i < a.length; i++) {
for (int j = i - step; j >= 0; j -= step) {
if (a[j] > a[j + step]) {
tmp = a[j];
a[j] = a[j + step];
a[j + step] = tmp;
}
}
}
System.out.println("第" + (++cnt) + "轮排序结果为" + Arrays.toString(a));
step /= 2;
}
}
// 有序数据插入时采用移位法
public void shellSortByRemove(int[] a) {
int tmp;
int step = a.length / 2;
int cnt = 1;
while (step > 0) {
// 从第step个元素开始,逐个对其所在的组进行直接插入排序
for (int i = step; i < a.length; i++) {
int insertIndex = i;
int insertValue = a[insertIndex];
if (a[insertIndex] < a[insertIndex - step]) {
while (insertIndex - step >= 0 && insertValue < a[insertIndex - step]) {
a[insertIndex] = a[insertIndex - step];
insertIndex -= step;
}
a[insertIndex] = insertValue;
}
}
System.out.println("第" + (cnt++) + "轮排序结果为" + Arrays.toString(a));
step /= 2;
}
}
快速排序
基本思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个过程可以递归实现,以此达到整个数据变成有序序列。
public void quickSort(int[] a, int left, int right) {
int r = right;
int mid = a[(left + right) / 2];
int tmp;
while (l < r) {
while(a[l] < mid) {
l++;
}
while (a[r] > mid) {
r--;
}
if (l >= r) {
break;
}
tmp = a[l];
a[l] = a[r];
a[r] = tmp;
// 交换完后,若a[l] == mid, r--,前移
if (a[l] == mid) {
r--;
}
if (a[r] == mid) {
l++;
}
}
// System.out.println(Arrays.toString(a));
// 没有此判断,栈溢出
if (l == r) {
l++;
r--;
}
if (left < r) {
quickSort(a, left, r);
}
if (l < right) {
quickSort(a, l, right);
}
}
归并排序
采用分治策略进行排序
public void mergeSort(int[] a, int left, int right, int[] tmp) {
if (left < right) {
int mid = (left + right) / 2;
// 左递归分解
mergeSort(a, left, mid, tmp);
// 右递归分解
mergeSort(a, mid + 1, right, tmp);
merge(a, left, mid, right, tmp);
}
System.out.println(Arrays.toString(a));
}
public void merge(int[] a, int left, int mid, int right, int[] tmp) {
int i = left;
int j = mid + 1;
int k = 0;
while(i <= mid && j <= right) {
if (a[i] <= a[j]) {
tmp[k++] = a[i++];
} else {
tmp[k++] = a[j++];
}
}
while (i <= mid) {
tmp[k++] = a[i++];
}
while (j <= right) {
tmp[k++] = a[j++];
}
// tmp拷贝到a
k = 0;
int tmpLeft = left;
while (tmpLeft <= right) {
a[tmpLeft++] = tmp[k++];
}
}
基数排序
基本思想:基数排序是桶排序的扩展,将所有待比较数值统一为同样的数位长度,数位较短的数值前面补0。然后从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数值就变成了一个有序序列。
public void radixSort(int[] a, int max) {
// 最大数的位数
int maxLength = (max + "").length();
// 桶
int[][] bucket = new int[10][a.length];
// 为了记录每个桶实际放了多少数据
int[] bucketEleCnt = new int[10];
for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
for (int j : a) {
int digit = j / n % 10;
bucket[digit][bucketEleCnt[digit]++] = j;
}
// 按桶的数据将数据放入原来的数组
int index = 0;
for (int t = 0; t < bucketEleCnt.length; t++) {
if (bucketEleCnt[t] > 0) {
int k = 0;
while (k < bucketEleCnt[t]) {
a[index++] = bucket[t][k++];
}
}
bucketEleCnt[t] = 0;
}
System.out.println("第" + (i + 1) + "轮结果:" + Arrays.toString(a));
}
}
堆排序
后续更新
总结
n:数据规模
k:桶的个数
排序算法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 是否占用额外内存 | 稳定性 | 10万个数据所需时间(ms) |
冒泡排序 | 否 | 稳定 | 18165 | ||||
选择排序 | 否 | 不稳定 | 4048 | ||||
插入排序 | 否 | 稳定 | 1445 | ||||
希尔排序 | 否 | 不稳定 | (交换法)10457 (移位法)26 | ||||
归并排序 | 是 | 稳定 | 30 | ||||
快速排序 | 否 | 不稳定 | 25 | ||||
堆排序 | 否 | 不稳定 | |||||
基数排序 | 是 | 稳定 | 20 |