一.堆排序
(1)思想
基本原理也是选择排序,只是不在使用遍历的方式查找无序区间的最大的数,而是通过堆来选择无序区间的最大的数。
注意: 排升序要建大堆;排降序要建小堆。
(2)实现
public static void heapsort(int[] array){ //堆排序 升序为例
creatHeap(array); //建大堆
for(int i = 0; i < array.length; i++){
swap(array, 0, array.length - 1 - i); //将此时堆中最大值与堆最后值交换,把最大值放到排序数组中。也就是队尾逐渐向前。
shiftDown(array,array.length - i - 1, 0); //堆中元素就是总数 - 当前排序数组好总数
}
}
public static void creatHeap(int[] array){ //建堆
for(int cur = (array.length - 1 - 1) / 2; cur >= 0; cur-- ){
shiftDown(array,array.length,cur);
}
}
public static void shiftDown(int[] array,int size, int index){ //向下调整
int parent = index;
int child = 2 * parent + 1;
while(child < size){
if(child + 1 < size && child + 1 > child){
child = child + 1;
}
if(array[parent] < array[child]){
swap(array, parent, child);
}
parent = child;
child = 2 * parent + 1;
}
}
public static void swap(int[] array, int left, int right){
int tem = array[left];
array[left] = array[right];
array[right] = tem;
}
(3)性能分析
时间复杂度 空间复杂度
O(n * log(n)) O(1)
数据不敏感 数据不敏感
稳定性:不稳定
二.冒泡排序
(1)思想
在无序区间,通过相邻数的比较,将最大的数冒泡到无序区间的最后,持续这个过程,直到数组整体有序
(2)实现
public static void bubbleSort(int[] array){ //冒泡排序 升序
for(int bound = 0; bound < array.length; bound++){
//(bound,arr.length) 未排序
// [0,bound) 已排序组合
for(int cur = array.length - 1; cur > bound; cur--){ //从最后一个元素交换最小值到最前面,直到排序好数组前。
if(array[cur] < array[cur - 1]){
swap(array, cur, cur - 1);
}
}
}
}
(3)性能分析
时间复杂度 空间复杂度
最好 平均 最坏
O(n) O(n^2) O(n^2) O(1)
数据有序 数据逆序
稳定性:稳定
(3)快速排序
(1)思想
- 从待排序区间选择一个数,作为基准值(pivot);
- Partition: 遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,将比基准值大的(可以包含相等的)放到基准值的右边;
- 采用分治思想,对左右两个小区间按照同样的方式处理,直到小区间的长度 == 1,代表已经有序,或者小区间的长度 == 0,代表没有数据。
(2)实现
递归实现
public static void quickSort(int[] array) { //递归实现快速排序
quickSortInternal(array, 0, array.length - 1);
}
public static void quickSortInternal(int[] array, int left, int right){
if(left >= right){
return;
}
// 最简单的选择基准值的方式,选择 array[right] 作为基准值
// pivotIndex 代表基准值最终停留的下标
int pivotIndex = partition(array, left, right);
// [left, pivotIndex - 1] 都是小于等于基准值的
// [pivotIndex + 1, right] 都是大于等于基准值的
quickSortInternal(array, left, pivotIndex - 1);
quickSortInternal(array, pivotIndex + 1, right);
}
public static int partition(int[] array, int left, int right){
int baseIndex = right;
int baseValue = array[baseIndex];
while(left < baseIndex){ //从左往后看是否有比基准值大的,有就跳出
while(left < baseIndex && array[left] <= baseValue){
left++;
}
while(left < baseIndex && baseValue <= array[baseIndex]){ //从右往前看是否有比基准值小的,有就跳出
baseIndex--;
}
swap(array, left, baseIndex); //交换
}
swap(array, right, left); //交换基准值
return left;
}
非递归实现
其实和递归差不多,就是把他的左右界限,放到栈中,执行玩partition函数就分成俩半,需要快速排序的数组,继续放到栈中,直到栈空。
public static void quickSortByLoop(int[] array) {
Stack<Integer> stack = new Stack<>();
stack.push(array.length - 1);
stack.push(0);
while(!stack.isEmpty()){
int left = stack.pop();
int right = stack.pop();
if(left <= right){
continue;
}
int pivotIndex = partition(array,left,right);
//左半部分进栈
stack.push(pivotIndex - 1);
stack.push(left);
//右半部分进栈
stack.push(right);
stack.push(pivotIndex + 1);
}
}
(3)优化
你有没有想到牵涉到递归,会有空间不够用的问题,所以就得做出优化。
下面是一些简单的优化
(1) 在待排序区间选择一个基准值
选择基准值选最前面,最后面可能是最大值,那他就一直只能分为1个数组,另一个就是最大值。
- 选择左边或者右边
- 随机选取
- 几数取中法
(2)做 partition,使得小的数在左,大的数在右
(3) 待排序区间小于一个阈值时(例如 48),使用直接插入排序
分治处理左右两个小区间,直到小区间数目小于一个阈值,使用插入排序。
(4)性能分析
时间复杂度 空间复杂度
最好 平均 最坏 最好 平均 最坏
O(n * log(n)) O(n * log(n)) O(n^2) O(log(n)) O(log(n)) O(n)
稳定性:不稳定
四.归并排序
(1)思想
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
(2)实现
public static void mergeSort(int[] array){
mergeSortHelper(array, 0, array.length);
}
public static void mergeSortHelper(int[] array, int left, int right){
if(left >= right || right - left == 1){
return;
}
int mid = (left + right) / 2; //取中间值
mergeSortHelper(array, left, mid); //递归左边,
mergeSortHelper(array, mid, right); //递归右边
mergr(array, left, mid, right); //将递归后的数祖归并。
}
public static void mergr(int[] array, int left, int mid, int right){
int length = right - left; //得到数组的长度
int[] output = new int[length];
int outputIndex = 0;
int i = left;
int j = mid;
while(i < mid && j < right){ //排序数组
if (array[i] <= array[j]) {
// i 对应的元素比 j 小
// 就把 i 对应的元素插入到 output 末尾
output[outputIndex++] = array[i++];
} else {
output[outputIndex++] = array[j++];
}
}
while(i < mid){
output[outputIndex++] = array[i++];
}
while(j < right){
output[outputIndex++] = array[j++];
}
for(int k = 0; k < length; k++){
array[left + k] = output[k];
}
}
(3)性能分析
时间复杂度 空间复杂度
O(n * log(n)) O(n)
数据不敏感 数据不敏感
稳定性:稳定