排序
冒泡排序:
在无序区间,通过相邻的数比较,将最大的数冒泡到无序区间的最后,重复前面的步骤,直到所有元素都已排序完。
public class MySort {
// 冒泡排序:
public static void bubbleSort(int[] arr){
int bound = 0;
for (;bound < arr.length; ++bound){
for (int cur = arr.length-1; cur > bound; --cur){
if (arr[cur] < arr[cur-1]){
swap(arr,cur,cur-1);
}
}
}
}
}
时间 | 复杂 | 度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|
最好 | 平均 | 最坏 | ||
O(n) | O(n^2) | O(n^2) | O(1) | 稳定 |
数据有序 | 数据逆序 |
插入排序:
每次选择无序区间的第一个元素,在有序区间内选择合适的位置插入。
public class MySort {
public static void insertSort(int[] arr){
for (int i = 0; i < arr.length; ++i){
int flag = arr[i]; // 无序和有序区间的分界
int j = i - 1;
for (; j >= 0 && arr[j] > flag; --j){
arr[j + 1] = arr[j];
}
arr[j + 1] = flag;
}
}
}
时间 | 复杂 | 度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|
最好 | 平均 | 最坏 | ||
O(n) | O(n^2) | O(n^2) | O(1) | 稳定 |
数据有序 | 数据逆序 |
希尔排序:
先选定一个数n,将待排序区间分成若干个组,距离为n的分在同一组内,进行排序,然后接着重复前面的步骤,直到组内距离为1,进行整组排序。
(希尔排序是对插入排序的优化)
public class MySort {
// 希尔排序:
public static void shellSort(int[] arr){
// 使用希尔序列
int gap = arr.length / 2;
while (gap >= 1){
// 分组插排
_shellSort(arr, gap);
gap /= 2;
}
}
// 辅助希尔排序:与插入排序类似
public static void _shellSort(int[] arr, int gap){
for (int bound = gap; bound < arr.length; bound++){
int num = arr[bound];
int cur = bound - gap;
for (; cur >= 0; cur -= gap){
if (arr[cur] > num){
arr[cur + gap] = arr[cur];
}else
break;
}
arr[cur + gap] = num;
}
}
}
时间 | 复杂 | 度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|
最好 | 平均 | 最坏 | ||
O(n) | O(n^1.3) | O(n^2) | O(1) | 不稳定 |
数据有序 | 较难构造 |
选择排序:
每一次从无序区间选出最大/最小的元素,存放在无序区间的最后/最前。直到将所有数据排序完毕。
public class MySort {
// 选择排序:
public static void selectSort(int[] arr){
int bound = 0; // 标志 bound 前为已排序, bound 后为未排序
for (; bound < arr.length-1; ++bound){
for (int cur = bound + 1; cur < arr.length; ++cur){
if (arr[cur] < arr[bound]){
swap(arr,cur,bound);
}
}
}
}
// 交换
public static void swap(int[] arr, int x, int y){
int tmp = arr[x];
arr[x] = arr[y];
arr[y] = tmp;
}
}
时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|
O(n^2) | O(1) | 不稳定 |
数据不敏感 | 数据不敏感 |
快速排序:
采用分治思想:从待排序区间选择一个数作为基准值,将待排序区间中比基准值小的放在基准值左边,大的放在基准值右边,对左右两个小区间重复步骤,直到区间长度为1,排序结束。
public class MySort {
// 快速排序:
public static void quickSort(int[] arr){
_quickSort(arr,0,arr.length-1);
}
// 辅助快速排序
public static void _quickSort(int[] arr, int left, int right){
if (left >= right){
return;
}
// 获取 基准值 所在位置
int index = partition(arr,left,right);
// 递归处理 index 左/右 区间
_quickSort(arr,left,index-1);
_quickSort(arr,index+1,right);
}
public static int partition(int[] arr, int left, int right){
// 选取基准值
int model = arr[right];
int i = left, j = right;
while (i < j){
// 找到比基准值大的值
while (i < j && arr[i] <= model)
i++;
// 找到比基准值小的值
while (i < j && arr[j] >= model)
j--;
// 交换这两个元素
swap(arr,i,j);
}
// 当 i 和 j 相等时,交换当前位置的值 和 基准值,使得基准值位于数组的“中间”
swap(arr,i,right);
return i;
}
// 使用非递归实现快速排序
public static void quickSortByLoop(int[] arr){
Stack<Integer> stack = new Stack<>();
// 将第一组数据入栈
stack.push(0);
stack.push(arr.length-1);
// 循环取栈顶元素 进行 partition 操作
while (!stack.isEmpty()){
int end = stack.pop();
int start = stack.pop();
if (start >= end){
// 空区间 只有一个元素
continue;
}
// 调用 partition
int index = partition(arr,start,end);
// 把得到的子区间入栈
stack.push(end);
stack.push(index + 1);
stack.push(index - 1);
stack.push(start);
}
}
}
时间 | 复杂 | 度 | —— | 空间 | 复杂 | 度 | 稳定性 |
---|---|---|---|---|---|---|---|
最好 | 平均 | 最坏 | 最好 | 平均 | 最坏 | ||
O(n*log(n)) | O(n*log(n)) | O(n^2) | O(log(n)) | O(log(n)) | O(log(n)) | O(n) | 不稳定 |
归并排序:
采用分治法,先将每个子序列有序,在将已有序的子序列合并进行排序,直到最后合并为一个完全有序表。
import java.util.Arrays;
public class MySort {
// 归并排序:
public static void mergeSort(int[] arr){
_mergeSort(arr, 0, arr.length);
}
public static void _mergeSort(int[] arr, int left, int right){
if (right - left <= 1){
return;
}
int mid = (left + right) / 2;
_mergeSort(arr, left, mid);
_mergeSort(arr, mid, right);
// 合并左右两个有序数组
merge(arr, left, mid, right);
}
public static void merge(int[] arr, int left, int mid, int right){
int[] tmp = new int[right - left]; // 临时数组,用来存放合并后的结果
int tmpSize = 0;
int l = left;
int r = mid;
while (l < mid && r < right){
if (arr[l] <= arr[r]){
tmp[tmpSize] = arr[l];
tmpSize++;
l++;
}else{
tmp[tmpSize] = arr[r];
tmpSize++;
r++;
}
}
while (l < mid){
tmp[tmpSize] = arr[l];
tmpSize++;
l++;
}
while (r < right){
tmp[tmpSize] = arr[r];
tmpSize++;
r++;
}
for (int i = 0; i < tmp.length; ++i){
arr[left+i] = tmp[i];
}
}
//非递归归并排序
public static void mergeSortByLoop(int[] arr){
// 外层循环
// 第一次是将长度为 1 的有序数组两两合并
// 第二次是将长度为 2 的有序数组两两合并
// 第三次是将长度为 4 的有序数组两两合并
for (int gap = 1; gap < arr.length; gap *= 2){
// 里层循环
// 让两个相邻的数组合并
for (int i = 0; i < arr.length; i += 2*gap){
int left = i;
int mid = i + gap;
if (mid >= arr.length){
mid = arr.length;
}
int right = i + 2*gap;
if (right >= arr.length){
right = arr.length;
}
merge(arr,left,mid,right);
}
}
}
public static void main(String[] args) {
int[] arr = {9,5,2,6,3,6,8};
mergeSort(arr);
System.out.println(Arrays.toString(arr));
}
}
时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|
O(n*log(n)) | O(n) | 稳定 |
数据不敏感 | 数据不敏感 |
堆排序:
public class MySort {
// 堆排序:
public static void heapSort(int[] arr){
// 1.建堆
creatHeap(arr);
// 2.交换堆顶元素和最后一个元素,删除最后一个元素并向下调整
int heapSize = arr.length;
for (int i = 0; i < arr.length; ++i){
// 交换堆顶和最后一个元素
swap(arr,0,heapSize-1);
// 删除最后一个元素
heapSize--;
// 向下调整
shiftHeap(arr,heapSize,0);
}
}
// 向下调整
public static void shiftHeap(int[] arr, int size, int index){
int parent = index;
int child = 2 * parent + 1;
while (child < size){
// 找出左右子树较大的
if (child+1 < size && arr[child + 1] > arr[child]){
child += 1;
}
if (arr[parent] < arr[child]){
swap(arr,parent,child);
}else
break;
parent = child;
child = 2 * parent + 1;
}
}
// 建堆
public static void creatHeap(int[] arr){
for (int i = (arr.length - 1 - 1)/2; i >= 0; --i){
shiftHeap(arr,arr.length,i);
}
}
}
时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|
O(n*log(n)) | O(1) | 不稳定 |
数据不敏感 | 数据不敏感 |