简单排序包括:直接插入排序,简单选择排序,冒泡排序
高级排序包括:希尔排序,快速排序,归并排序,堆排序
主函数
public static void main(String[] args) {
int[] numbers = { 10, 20, 15, 0, 6, 7, 2, 1, -5, 55 };
//指定排序方法
selectSort(numbers);
}
插入排序
每次将一个待排的数据元素,插入到前面已经排好序的数列中的适当位置,是数列依然有序。
稳定,空间复杂度O(1),时间复杂度O(n²)。
public static void insertSort(int[] arr) {
int temp;
//外层循环是l-1次,第一个数无需比较直接插入。
for (int i = 1; i < arr.length; i++) {
temp = arr[i];
int j = i - 1;
/*
* 待排数据在i位置,与i-1到0位置数据比较,因为是已排好序的
* 所以一旦找到即可跳出循环,可以使用while
*/
while (j >= 0 && temp < arr[j]) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = temp;
}
}
结果如下:
第1趟:10 20 15 0 6 7 2 1 -5 55
第2趟:10 15 20 0 6 7 2 1 -5 55
第3趟:0 10 15 20 6 7 2 1 -5 55
第4趟:0 6 10 15 20 7 2 1 -5 55
第5趟:0 6 7 10 15 20 2 1 -5 55
第6趟:0 2 6 7 10 15 20 1 -5 55
第7趟:0 1 2 6 7 10 15 20 -5 55
第8趟:-5 0 1 2 6 7 10 15 20 55
第9趟:-5 0 1 2 6 7 10 15 20 55
排序完成后:-5 0 1 2 6 7 10 15 20 55
选择排序
每次从待排数据中选出最小(或最大)的元素,顺序放在已排好的序列后面。
不稳定,空间复杂度O(1),时间复杂度O(n²)。
public static void selectSort(int[] arr) {
int l = arr.length;
//外层循环是l-1次,最后的不需要选择,一定是最大的。
for (int i = 0; i < l-1; i++) {
int p = i;
/*
* 待排数据在i位置,要从i+1到l-1数据中找出最小的,所以要使用for循环来遍历剩余的数据
*/
for (int j = i + 1; j < l; j++) {
if (arr[p] > arr[j]) {
p = j;
}
}
int t = arr[i];
arr[i] = arr[p];
arr[p] = t;
}
}
结果如下:
第0趟:-5 20 15 0 6 7 2 1 55 10
第1趟:-5 0 15 20 6 7 2 1 55 10
第2趟:-5 0 1 20 6 7 2 15 55 10
第3趟:-5 0 1 2 6 7 20 15 55 10
第4趟:-5 0 1 2 6 7 20 15 55 10
第5趟:-5 0 1 2 6 7 20 15 55 10
第6趟:-5 0 1 2 6 7 10 15 55 20
第7趟:-5 0 1 2 6 7 10 15 55 20
第8趟:-5 0 1 2 6 7 10 15 20 55
排序完成后:-5 0 1 2 6 7 10 15 20 55
冒泡排序
依次比较相邻的两个数,将小数放在前面,大数放在后面。
稳定,空间复杂度O(1),时间复杂度O(n²)。
public static void bubbleSort(int[] arr) {
int l=arr.length;
for(int i=0;i<l-1;i++) {
for(int j=0;j<l-i-1;j++) {
if(arr[j]>arr[j+1]) {
int temp = arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
结果如下:
第0趟:10 15 0 6 7 2 1 20 -5 55
第1趟:10 0 6 7 2 1 15 -5 20 55
第2趟:0 6 7 2 1 10 -5 15 20 55
第3趟:0 6 2 1 7 -5 10 15 20 55
第4趟:0 2 1 6 -5 7 10 15 20 55
第5趟:0 1 2 -5 6 7 10 15 20 55
第6趟:0 1 -5 2 6 7 10 15 20 55
第7趟:0 -5 1 2 6 7 10 15 20 55
第8趟:-5 0 1 2 6 7 10 15 20 55
排序完成后:-5 0 1 2 6 7 10 15 20 55
希尔排序
也叫缩小增量排序,把记录按下标的一定增量分组,对每组使用直接插入排序,逐渐减少增量直到1,整个数组被分为一组,排序完成。(一般初次取数组半长,之后每次减半,直到增量为1)
不稳定,空间复杂度O(1),平均时间复杂度O(n^1.3)。
public static void shellSort(int[] arr) {
int gap = arr.length / 2;
for (; gap > 0; gap /= 2) { // 不断缩小gap,直到1为止
for (int j = gap; j < arr.length; j++) { // 内层使用直接插入排序,步长为gap
int temp = arr[j];
int k = j - gap;
while (k >= 0 && temp < arr[k]) {
arr[k + gap] = arr[k];
k -= gap;
}
arr[k + gap] = temp;
}
}
}
结果如下:
7 2 1 0 -5 10 20 15 55 6
-5 0 1 2 7 6 20 10 55 15
-5 0 1 2 6 7 10 15 20 55
排序完成后:-5 0 1 2 6 7 10 15 20 55
快速排序
是冒泡排序的升级,每次排序的时候设置一个基准点,将小于等于基准点的数全部放到基准点的左边,将大于等于基准点的数全部放到基准点的右边,递归进行,最终得到有序数列。
不稳定,空间复杂度O(nlogn),平均时间复杂度O(nlogn)。
public static void quickSort(int[] arr, int low, int high) { //quickSort(numbers,0,numbers.length-1);
int i, j, temp, t;
if (low > high) {
return;
}
i = low;
j = high;
// temp选数组的第一个数作为基准
temp = arr[low];
while (i < j) {
// 先看右边,依次往左递减
while (temp <= arr[j] && i < j) {
j--;
}
// 再看左边,依次往右递增
while (temp >= arr[i] && i < j) {
i++;
}
// 如果满足条件则交换
if (i < j) {
t = arr[j];
arr[j] = arr[i];
arr[i] = t;
}
}
// 最后将基准为与i和j相等位置的数字交换
arr[low] = arr[i];
arr[i] = temp;
// 递归调用左半数组
quickSort(arr, low, j - 1);
// 递归调用右半数组
quickSort(arr, j + 1, high);
}
结果如下:
2 -5 1 0 6 7 10 15 55 20
0 -5 1 2 6 7 10 15 55 20
-5 0 1 2 6 7 10 15 55 20
-5 0 1 2 6 7 10 15 55 20
-5 0 1 2 6 7 10 15 55 20
-5 0 1 2 6 7 10 15 55 20
-5 0 1 2 6 7 10 15 55 20
-5 0 1 2 6 7 10 15 55 20
-5 0 1 2 6 7 10 15 20 55
-5 0 1 2 6 7 10 15 20 55
排序完成后:-5 0 1 2 6 7 10 15 20 55
归并排序
归并排序利用的是分治的思想,对于给定的一组数据,利用递归与分治技术将数据序列划分成为越来越小的子序列,之后对子序列排序,最后再用递归方法将排好序的子序列合并成为有序序列。合并两个子序列时,需要申请两个子序列加起来长度的内存,临时存储新的生成序列,再将新生成的序列赋值到原数组相应的位置。
稳定,空间复杂度O(n),平均时间复杂度O(nlogn)。
public static void merSort(int[] arr, int left, int right) { //merSort(numbers,0,numbers.length-1);
if (left < right) {
int mid = (left + right) / 2;
merSort(arr, left, mid);// 左边归并排序,使得左子序列有序
merSort(arr, mid + 1, right);// 右边归并排序,使得右子序列有序
merge(arr, left, mid, right);// 合并两个子序列
}
}
private static void merge(int[] arr, int left, int mid, int right) {
int[] temp = new int[right - left + 1];// ps:也可以从开始就申请一个与原数组大小相同的数组,因为重复new数组会频繁申请内存
int i = left;
int j = mid + 1;
int k = 0;
while (i <= mid && j <= right) {
if (arr[i] < arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
}
}
while (i <= mid) {// 将左边剩余元素填充进temp中
temp[k++] = arr[i++];
}
while (j <= right) {// 将右序列剩余元素填充进temp中
temp[k++] = arr[j++];
}
// 将temp中的元素全部拷贝到原数组中
for (int k2 = 0; k2 < temp.length; k2++) {
arr[k2 + left] = temp[k2];
}
}
堆排序
树形选择排序,在排序过程中,将A[1…n]看成是一棵完全二叉树的顺序结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系来选择最小的元素。
堆排序适合海量数据取最大(小)的n个元素,步骤就是①取前n个元素建立一个小顶堆,②顺序读取后续元素直到结束。(堆排序一直是似懂非懂的,还是堆树和堆的不理解,代码来源于网上大佬的分享,详细的还是去看原文吧,感谢大佬)
https://blog.youkuaiyun.com/qq_35178267/article/details/78313306
不稳定,空间复杂度O(1),平均时间复杂度O(nlogn)。
static int parent(int i) {
return (i - 1) / 2;
}
static int left(int i) {
return 2 * i + 1;
}
static int right(int i) {
return 2 * i + 2;
}
static void maxHeapfy(int[] a, int i, int heapSize) { // 数组a,第i个结点,heapSize是数组种实际要排序的元素的长度
int left = left(i); // 有的时候能够递归到叶子结点,叶子结点无后继,下面两个if都注意到了这一点
int right = right(i);
int largest = i;
if (left < heapSize && a[left] > a[largest]) { //
largest = left;
}
if (right < heapSize && a[right] > a[largest]) {
largest = right;
}
if (largest != i) { // 把最大值给父结点
a[largest] = a[largest] ^ a[i];
a[i] = a[largest] ^ a[i];
a[largest] = a[largest] ^ a[i];
maxHeapfy(a, largest, heapSize); // 发生交换之后还要保证大根堆性质
}
}
static void buildMaxHeap(int[] a, int heapSize) {
for (int i = (heapSize - 2) / 2; i >= 0; i--) {
maxHeapfy(a, i, heapSize);
}
}
static void heapSort(int[] a) {
int len = a.length;
buildMaxHeap(a, len); // 初始建堆
a[len - 1] = a[0] ^ a[len - 1]; // 交换
a[0] = a[0] ^ a[a.length - 1];
a[len - 1] = a[0] ^ a[len - 1];
for (int i = 1; i < len - 1; i++) { // 初始建堆之后还要排a.length-2次
maxHeapfy(a, 0, len - i);
a[len - 1 - i] = a[0] ^ a[len - 1 - i]; // 交换
a[0] = a[0] ^ a[len - 1 - i];
a[len - 1 - i] = a[0] ^ a[len - 1 - i];
}
}