目录
一、插入排序
二、希尔排序
三、选择排序
四、堆排序
五、冒泡排序
六、快速排序
七、归并排序
八、计数排序
排序过程:
从第 j 个数字开始,往前面寻找比 arr[j] 小的数,如果找到比 arr[j] 小的数,假设下标为 i,然后交换 arr[i] 和 arr[j] 即可。
时间复杂度:最好情况(正序)下是 O(N),最坏情况(逆序)下是O(N^2)
空间复杂度:O(1)
稳定性:稳定
private static void InsertSort(int[] arr) {for (int i = 1; i < arr.length; i++) {int tmp = arr[i];int j = i;for (; j > 0; j--) {if (tmp < arr[j-1]) {arr[j] = arr[j-1];}else {break;}}arr[j] = tmp;}}
排序过程:
在插入排序的基础之上分组进行排序,设置一个间隔 gap,然后按照 gap 进行分组,在分好的每个小组进行插入排序。
时间复杂度:O(N^1.3~N^1.5)
空间复杂度:O(1)
稳定性:不稳定
private static void shellSort(int[] arr) {int gap = arr.length;while (gap > 1) {shell(arr,gap);// gap 每次都除以2,可以减少排序的次数// 如果除数太大,和直接使用插入排序效果差不多// 如果一次次的 -1或者 -2,就需要多次分组排序,这也会降低效率gap /= 2;}shell(arr,1);
}
private static void shell(int[] arr, int gap) {for(int i = gap; i < arr.length; i++) {int tmp = arr[i];int j = i-gap;for(; j >= 0; j -= gap) {if (tmp < arr[j]) {arr[j+gap] = arr[j];}else {break;}}arr[j+gap] = tmp;}
}
直接选择排序过程:
从下标为 i 处开始,往后遍历,找到值最小的数字的位置 minIndex,并且交换 arr[i] 和 arr[minIndex]。
时间复杂度:O(N*logN)--- 直接选择排序对数据不敏感,无论有序还是无序,时间复杂度都是O(n^2)
空间复杂度:O(1)
稳定性:不稳定
private static void selectSort(int[] arr) {for(int i = 0; i < arr.length; i++) {int minIndex = i;for (int j = i; j < arr.length; j++) {minIndex = arr[minIndex] > arr[j] ? j : minIndex;}swap(arr,i,minIndex);}
}private static void swap(int[] arr, int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;
}
双指针选择排序过程:
同时遍历出最小值和最大值,然后在左右两侧进行交换。
private static void selectSort2(int[] arr) {int left = 0;int right = arr.length-1;while (left < right) {int minIndex = left;int maxIndex = left;for(int j = left+1; j <= right; j++) {minIndex = arr[minIndex] > arr[j] ? j : minIndex;maxIndex = arr[maxIndex] < arr[j] ? j : maxIndex;}swap(arr,left,minIndex);// 修正 max 下标if (left == maxIndex) {maxIndex = minIndex;}swap(arr,right,maxIndex);left++;right--;}
}
堆排序过程:
先创建一个大根堆,然后再从最后一个叶子结点开始往前调整。调整方式如图所示
时间复杂度:O(N*logN)
空间复杂度:O(1)
稳定性:不稳定
private static void heapSort(int[] arr) {// 建立大根堆createBigHeap(arr);// 调整的节点下标int end = arr.length-1;while(end > 0) {swap(arr,0,end);shidtDown(arr,0,end);end--}
}
private static void createBigHeap(int[] arr) {for (int parent = (arr.length-1-1)/2; parent >= 0; parent--) {//向下调整shiftDown(arr,parent,arr.length);}
}private static void shiftDown(int[] arr, int parent, int length) {// 判断左孩子int child = parent*2+1;while(child < length) {if (child + 1 < length && arr[child+1] > arr[child]) {child++;}if(arr[child] > arr[parent]) {swap(arr,child,parent);parent = child;child = parent*2+1;}else {break;}}
}
private static void swap(int[] arr, int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;
}
排序过程:
从 i 开始遍历,若 arr[i+1] > arr[i],则arr[i+1] 和 arr[i] 进行交换即可,交换一轮之后,最大值就会跑到最后一个位置。
时间复杂度:O(N^2)
空间复杂度:O(1)
稳定性:稳定
private static void bubbleSort(int[] arr) {for (int i = 0; i < arr.length-1; i++) {// 用于优化排序boolean flg = false;for (int j = 0; j < arr.length-i-1; j++) {if (arr[j] > arr[j+1]) {swap(arr,j,j+1);}else {// 如果遍历一轮并没有任何交换,说明不存 arr[i+1] > arr[i]的情况flg = !flg;}}// flg == true 说明数组已经有序if (flg) {break;}}
}
private static void swap(int[] arr, int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;
}
1.Hoare法
时间复杂度:O(N*logN)——每次都是均分待排列序列,O(N^2)——数据有序或者逆序
空间复杂度:最好情况——O(logN),最坏情况O(N),当N越大的时候,递归的就越深
稳定性:不稳定
private static void quickSort(int[] arr) {quick(arr,0,arr.length-1);
}private static void quick(int[] arr, int left, int right) {if (left >= right) {return;}int pivot = partitionHoare(arr,left,right);quick(arr,left,pivot-1);quick(arr,pivot+1,right);
}
private static int partitionHoare(int[] arr, int left, int right) {// 事先存储好 left 和 right 下标int i = left;int j = right;int pivot = arr[left];while (i < j) {// 一定要先走右边,不然可能会把大于 pivot 的值传到左边while (i < j && arr[j] >= pivot) {j--;}while (i < j && arr[i] <= pivot) {i++;}swap(arr,i,j);}swap(arr,i,left);return i;
}
private static void swap(int[] arr, int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;
}
2.挖坑法
private static void quickSort(int[] arr) {quickHole(arr,0,arr.length-1);
}private static void quickHole(int[] arr, int left, int right) {if (left >= right) {return;}int pivot = partitionHole(arr,left,right);quickHole(arr,left,pivot-1);quickHole(arr,pivot+1,right);
}
private static int partitionHole(int[] arr, int left, int right) {int key = arr[left];while (left < right) {while (left < right && arr[right] >= key) {right--;}arr[left] = arr[right];while (left < right && arr[left] <= key) {left++;}arr[right] = arr[left];}arr[left] = key;return left;
}
private static void swap(int[] arr, int i, int j) {int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;
}
3.非递归实现快速排序
private static void quickSort(int[] arr) {Stack stack = new Stack<>();int left = 0;int right = arr.length-1;int pivot = partitionHole(arr,left,right);if (pivot > left+1) {stack.push(left);stack.push(pivot-1);}if (pivot < right-1) {stack.push(pivot+1);stack.push(right);}while (!stack.isEmpty()) {right = stack.pop();left = stack.pop();pivot = partitionHole(arr,left,right);if (pivot > left+1) {stack.push(left);stack.push(pivot-1);}if (pivot < right-1) {stack.push(pivot+1);stack.push(right);}}
}
private static int partitionHole(int[] arr, int left, int right) {int key = arr[left];while (left < right) {while (left < right && arr[right] >= key) {right--;}arr[left] = arr[right];while (left < right && arr[left] <= key) {left++;}arr[right] = arr[left];}arr[left] = key;return left;
}
排序过程:先将原数组分成最小单元,然后再将这些最小的单元一个个有序的合并起来,如下图所示。
时间复杂度:O(N*logN)
空间复杂度:O(N)
稳定性:稳定
private static void mergeSort(int[] arr) {mergeSortFunc(arr,0,arr.length-1);
}private static void mergeSortFunc(int[] arr, int left, int right) {// 结束条件if (left >= right) {return;}int mid = (left+right)/2;mergeSortFunc(arr,left,mid);mergeSortFunc(arr,mid+1,right);// 合并merge(arr,left,right,mid);
}
private static void merge(int[] arr, int start, int end, int midIndex) {int s1 = start;// 第一个归并段的起点int s2 = midIndex+1;// 第二个归并段的起点int[] tmp = new int[end-start+1];int k = 0;while (s1 <= midIndex && s2 <= end) {if (arr[s1] <= arr[s2]) {tmp[k++] = arr[s1++];}else {tmp[k++] = arr[s2++];}}// 当一个归并段中没有数据之后while (s1 <= midIndex) {tmp[k++] = arr[s1++];}while (s2 <= end) {tmp[k++] = arr[s2++];}// 将 tmp 中的数据拷贝回原数组for (int i = 0; i < k; i++) {arr[i+start] = tmp[i];}
}
非递归实现归并排序
private static void mergeSort(int[] arr) {int gap = 1;while (gap < arr.length) {for (int i = 0; i < arr.length; i += gap*2) {int s1 = i;int e1 = s1+gap-1;if (e1 >= arr.length) {e1 = arr.length-1;}int s2 = e1+1;if (s2 >= arr.length) {s2 = arr.length-1;}int e2 = s2+gap-1;if (e2 >= arr.length) {e2 = arr.length-1;}merge(arr,s1,e2,e1);}gap *= 2;}}
private static void merge(int[] arr, int start, int end, int midIndex) {int s1 = start;// 第一个归并段的起点int s2 = midIndex+1;// 第二个归并段的起点int[] tmp = new int[end-start+1];int k = 0;while (s1 <= midIndex && s2 <= end) {if (arr[s1] <= arr[s2]) {tmp[k++] = arr[s1++];}else {tmp[k++] = arr[s2++];}}// 当一个归并段中没有数据之后while (s1 <= midIndex) {tmp[k++] = arr[s1++];}while (s2 <= end) {tmp[k++] = arr[s2++];}// 将 tmp 中的数据拷贝回原数组for (int i = 0; i < k; i++) {arr[i+start] = tmp[i];}
}
排序过程:记录每个数据 val 出现的次数和该数字的大小,数字每出现一次 count[val] 就加 1,然后遍历 count,将记下的数据写回原数组中。
时间复杂度:O(N+范围)
空间复杂度:O(范围)
稳定性:无法判定
public static void countSort(int[] array) {int maxVal = array[0];int minVal = array[0];
for (int i = 0; i < array.length; i++) {if(array[i] < minVal) {minVal = array[i];}if(array[i] > maxVal) {maxVal = array[i];}}//就能确定当前数组的最大值 和 最小值int len = maxVal - minVal + 1;int[] count = new int[len];//开始遍历array数组 进行计数for (int i = 0; i < array.length; i++) {int val = array[i];count[val-minVal]++;
}
int index = 0;//array数组的下标for (int i = 0; i < count.length; i++) {//确保 当前count数组 可以检查完while (count[i] != 0) {array[index] = i+minVal;index++;count[i]--;}}
}