冒泡 插入 选择排序
| 排序 | 平均时间复杂度 | 最好 | 最坏 | 是否稳定 | 空间复杂度(是否原地排序) |
|---|---|---|---|---|---|
| 冒泡 | O(n2) | O(n) | o(n2) | 稳定 | O(1) |
| 插入 | O(n2) | O(n) | o(n2) | 稳定 | O(1) |
| 选择 | O(n2) | O(n) | o(n2) | 不稳定 | O(1) |
| 归并 | O(nlog(n)) | O(nlog(n)) | O(nlog(n)) | 稳定 | O(n) |
| 快速 | O(nlog(n)) | O(nlog(n)) | o(n2) | 不稳定 | O(1)或者O(n) |
| 桶排序 | O(n) | O(n) | o(nlog(n))单筒 | 稳定 | O(1) |
| 计数排序 | O(n2) | O(n) | o(nlog(n)) | 稳定 | O(1) |
| 基数排序排序 | O(dn) | O(dn) | o(dn) | 稳定 | O(1) |
稳定排序算法可以保持金额相同的两个对象,在排序之后的前后顺序不变。
复杂度分析: 近似:有序度 O(n) = 逆序读/满有序度
满有序度:n(n-1)/2 粗略估计交换次数
nlog(n) 复杂度分析:
T(1) = C; n=1时,只需要常量级的执行时间,所以表示为C。 T(n) = 2*T(n/2) + n; n>1
T(n) = 2T(n/2) + n = 2(2T(n/4) + n/2) + n = 4T(n/4) + 2n = 4(2T(n/8) + n/4) + 2n = 8T(n/8) + 3n = 8*(2T(n/16) + n/8) + 3n = 16T(n/16) + 4n ...... = 2^k * T(n/2^k) + k * n ......
当 T(n/2^k)=T(1) 时,也就是 n/2^k=1,我们得到 k=log2n
public static int[] bubbleSort(int[] a, int n) {
if (n <= 1) return a;
for (int i = 0; i < n; i++) {
boolean br = false;
for (int j = 0; j < n - i - 1; j++) {
if (a[j] > a[j + 1]) {
int temp = a[j + 1];
a[j + 1] = a[j];
a[j] = temp;
br = true;
}
}
if (!br) break;
;
}
return a;
}
public static int[] insertSort(int[] a, int n) {
if (n <= 1) return a;
for (int i = 1; i < n; i++) {
int value = a[i];
int j = i - 1;
for (; j >= 0; j--) {
if (a[j] > value) {
a[j + 1] = a[j];
} else {
break;
}
}
a[j + 1] = value;
}
return a;
}
public static int[] selectSort(int[] a, int n) {
if (n <= 1) return a;
for (int h = 0; h < n; h++) {
int min = 9999;
int pt = 0;
int i = h;
for (; i < n; i++) {
if (a[i] < min) {
min = a[i];
pt = i;
}
}
int temp = a[pt];
a[pt] = a[h];
a[h] = temp;
}
return a;
}
归并排序
public class MergeSort {
public void run(int[] a,int n){
sort(a,0,a.length-1);
}
public void sort(int[] a,int left,int right){
if(left>=right) return;
int middle = getMiddle(left,right);
sort(a,left,middle);
sort(a,middle+1,right);
merge(a,left,middle,right);
System.out.println(Arrays.toString(a));
}
private int getMiddle(int p ,int r){
return (p+r)/2;
}
public void merge(int[] a,int left,int middle,int right){
int[] tempA =new int[a.length];
int i = 0;
int p = left;
int q = middle+1;
while(p<=middle && q<=right){
if(a[p]<=a[q]){
tempA[i++] = a[p++];
}else{
tempA[i++] = a[q++];
}
}
while (p<=left){
tempA[i++] = a[p++];
}
while (q<=right){
tempA[i++] = a[q++];
}
i=0;
while (left<=right){
a[left] = tempA[i++];
left++;
}
}
}
class Main {
public static void main(String[] args){
MergeSort a = new MergeSort();
int[] arr={3,2,5,1,1,6};
a.run(arr, arr.length);
}
}
代码心得:
- 临时数组下标和原数组的对应 可以和数组下标保持一致,否则需要从0开始赋值.
- 注意(p+q)/2的括号
- 不要怀疑递推公式和结束条件,打断点跟踪函数是否出了问题,出现的两个问题都是函数实现有问题,例如merge函数拷贝临时数组到源数组时下标没对上
- java数组时对象传递,即引用传递.
快速排序
public class QuickSort {
public void run(int[] a,int n){
sort(a,0,a.length-1);
}
public void sort(int[] a,int left,int right){
if(left>=right) return;
int pivot = partition(a,left,right);
sort(a,left,pivot-1);
sort(a,pivot+1,right);
System.out.println(Arrays.toString(a));
}
public int partition(int[] a,int left,int right){
int pivot = a[right];
int i=left;
int j=left;
while(j<right){
if(a[j]<=pivot){
swap(a,i,j);
i++;
}
j++;
}
swap(a,i,j);
return i;
}
public void swap(int[] a,int i,int j){
int temp = a[i];
a[i] = a[j];
a[j] =temp;
}
}
class Main {
public static void main(String[] args){
QuickSort a = new QuickSort();
int[] arr={3,2,5,1,6,4};
a.run(arr, arr.length);
}
}
要点:
- i和j的区分,i指向小于pivot的值,j做循环
性能分析: 递归树 画出递归树,图转载于time.geekbang.org/column/arti…
设每次分组分别为1/10 和9/10 树每一层交换册数为n 1/10分组最长路径为 1/10n n/10的2次幂 ... n/10的k次幂 ... 1 n/10的k次幂 = 1; 解k = log1/10(n) 同理另一条路径的最长路径为 h = log10/9(n).层数*交还次数得: T(n) = O(nlog(n)