排序

来源:互联网 发布:佳能mg3580清零软件 编辑:程序博客网 时间:2024/04/29 08:54

分治策略

1. Divide:将大问题分解成若干个小问题;
2. Conquer:递归解决所有子问题;
3. Conbine:合并
T(n) = 2T(n/2) + theta(n);    T(n) = θ(nlgn);

二分搜索

1. Divide x与中间值比较
2. Conquer 递归找下一个子数组
3. Conbine 不做动作
T(n) = T(n/2) + θ(1);    T(n) = θ(lgn);

x^n

Sol. 1 x*x*x*x...x T(n) = θ(n);
Sol. 2 x^n = x^(n/2) * x^(n/2)T(n) = θ(n);

Fibonacci数列

F(n) = F(n-1) + F(n-2); F(1) = 1, F(0) = 0;
Sol. 1 递归:从n求到1,时间复杂度指数级增长
Sol. 2 Bottom-up求解:从1到n求解,F(1), F(2), ... F(n)时间复杂度:θ(n)
Sol. 3 利用下面的矩阵我就可以实现θ(lgn)的时间复杂度

集成电路完全二叉树布局

正常情况二叉树面积θ(nlogn)
改进后面积θ(n)

矩阵相乘

Sol. 1 用三重循环计算,时间复杂度θ(n^3)
Sol. 2 用分治,包含8个递归,时间复杂度仍然为θ(n^3) T(n) = 8T(n/2) + θ(n^2) 
Sol. 3 Strassen算法,通过数学转换将8个递归转换成7个递归,时间复杂度降为θ(n^2.81)
理论上矩阵相乘的时间复杂度可以达到θ(n^2.376)

快速排序

原理与实现


xi  j
5 4 8 9 6 3 2 5 7 1
x  i  j
5 4 8 9 6 3 2 5 7 1
x  i     j
5 4 8 9 6 3 2 5 7 1
x  i        j
5 4 8 9 6 3 2 5 7 1
x  i           j
5 4 8 9 6 3 2 5 7 1
x     i        j
5 4 3 9 6 8 2 5 7 1
x     i           j
5 4 3 9 6 8 2 5 7 1
x        i        j
5 4 3 2 6 8 9 5 7 1
x        i           j
5 4 3 2 6 8 9 5 7 1
x        i                 j
5 4 3 2 6 8 9 5 7 1
x        i                 j
5 4 3 2 6 8 9 5 7 1
x           i              j
5 4 3 2 1 8 9 5 7 6
i           x              j
1 4 3 2 5 8 9 5 7 6

////   排序代码
Partition(A, p ,q)
r = p; i = p; x = A[p];   // i 指向最后一个小于x的值的序号,j指向当前循环位置
//  对于随机算法 r = random(p,q);
for j = p+1 to q
if x > A[j]
i++;
exchange(A[i],A[j]);
exchange(A[i],A[p]);
return i;
QickSort(A,p,q)
if p < q
r = Partition(A,p,q)
QickSort(A,p,r);
QickSort(A,r+1,q);
//  代码
#include <iostream>  #include <string>  using namespace std;void exchange(int& a, int& b){int m = a;a = b;b = m;}int Partition(int* A,int& p ,int& q){int r = p; int i = p; int x = A[p];   // i 指向最后一个小于x的值的序号,j指向当前循环位置for(int j = p+1; j <= q; ++j)     {if( x > A[j]){i++;exchange(A[i],A[j]);}}exchange(A[i],A[p]);return i;}void QickSort(int* A,int p ,int q){if(p < q) {int r;r = Partition(A,p,q);QickSort(A,p,r-1);QickSort(A,r+1,q);}}int main(){int A[] = {4,5,6,8,3,4,9,7,3,5,8,5,10,15,0,2,36,5,8,12};int len = sizeof(A)/sizeof(int);cout<<"A排序前:";for (int i = 0; i < len; ++i){cout<<A[i]<<" ";}cout<<endl;QickSort(A,0,len-1);cout<<"A排序后:";for (int i = 0; i < len; ++i){cout<<A[i]<<" ";}cout<<endl;}



算法时间复杂度分析

快排相对于归并排序来说时间复杂度最好时是一样的,归并排序是稳定的排序算法,但要求窗外的内存空间,而快排是原址排序,时间复杂度上是不稳定的,除非加入随机算法过程;

最坏的情况:
就是每次递归得到的值都在一边,则此时时间复杂度有 T(n) = T(0) + T(n-1) + θ(n); T(n) = θ(n^2);

一般情况:
取正中间 T(n) = 2T(n/2) + θ(n); T(n) = θ(nlgn); 
取其它值:时间复杂T(n) = θ(nlgn); 

非随机化版本:
好坏交替出现的情况,其时间复杂度与最好的情况一样,T(n) = θ(nlgn);   // 即平均情况
随机化版本:
平均时间复杂度为T(n) = θ(nlgn); 



快速排序在应用中是归并排序的3倍快

非比较排序

前面的插入、选择、堆、归并、冒泡、快速排序版本,最好的情况都只能达到T(n) = θ(nlgn); 
原因是它们都是通过比较大小的模型 来排序,因此为了达到更快的排序效果,我们可以使用非比较大小的方式来实现;
决策树模型:
树的每一个内节点有下标:i:j
对于a1,a2,...an;比较ai与aj,如果ai<=aj,左子树;否则右子树
最终每个叶节点代表一种排序

计数排序

对于n个输入元素中每个数在0~k之间,如果k=O(n),则排序时间为T(n) = θ(n);    // 即一般如果数的个数多但范围有限且相对n较小,则可以用计数排序。

// 伪代码     n为数组长度,k为最大数,则里面的数组C长度应该为k+1(0~k)
CountSort(A,n,k)for i = 0 to kC[i] = 0;for j = 0 to n-1C[A[j]]++;  // C[i]表示了值为i的个数for i = 1 to kC[i] += C[i-1];   // 对于当前位i的值C[i]表示了至少有C[i]个数小于等于i// 排序for i = n-1 to 0B[C[A[i]]] = A[i];C[A[i]]--;

// 代码
int* CountSort(int* A,const int n,const int k){int *C = new int[k+1]();    // k+1长度,初始化为0int *B = new int[n]();for(int j = 0; j < n; ++j)C[A[j]]++;  // C[i]表示了值为i的个数for(int t = 1; t <= k+1; ++t)C[t] += C[t-1];   // 对于当前位i的值C[i]表示了至少有C[i]个数小于等于i// 排序for(int m = 0; m < n; ++m){B[C[A[m]]] = A[m];C[A[m]]--;}//delete[] C;   // delete会崩溃return B;}





基数排序

桶排序








 
0 0
原创粉丝点击