算法导论这本书,断断续续看了很久也只看了三分之一左右。近来又忙于别的方面的事情,把这本大部头搁置了很久,于是决定写上几篇来帮助自己梳理回忆一下。
一、插入排序
这是书上开篇介绍的第一个算法,问题描述如下:
输入:n个数的一个序列<a1,a2, a3, …, an>
输出:输入序列的一个排列<a1’,a2’, …, an’>,满足a1’<=a2’<=…<=an’。
算法的伪代码如下:
INSERTION-SORT(A):
For j = 2 toA.length
Key = A[j]
//Insert A[j] into the sorted sequenceA[1..j – 1]
I = j – 1
While I > 0 and A[i] > key
A[i+1] = A[i]
I = i-1
A[I + 1] = key
然后从github上翻出了当年写的c代码:
void INSERTIONSORT(int * arr, int n) //n is the length of the arr
{
int key = 0,j;
for (int i = 1;i < n;i++)
{
key = arr[i];
for ( j = i - 1;j >= 0;j--)
{
if (arr[j] > key)
arr[j + 1] = arr[j];
else
break;
}
arr[j+1] = key;
}
}
这个算法的复杂度怎么计算呢?
可以想象,在最坏的情况下,每次插入的数都比之前的数更大,也就是要与之前所有的数进行一次比较,分别进行1,2,3,……,n-1次比较,这可以用等差数列求和来计算,结果是1/2*n^2。二、归并排序
问题与之前的插入排序相同。
输入:n个数的一个序列<a1,a2, a3, …, an>
输出:输入序列的一个排列<a1’,a2’, …, an’>,满足a1’<=a2’<=…<=an’。
书上的伪代码为:
MERGE(A,p,q,r):
N1 = q – p + 1
N2 = r – q
Let L[1..n1 + 1] and R[1..n2 + 1] be new arrays
For I = 1 to n1
L[i] = A[p + I-1]
For j = 1 to n2
R[j] = A[q +j]
L[n1 + 1] = 无穷
R[n2 + 1] = 无穷
I = 1
J = 1
For k = p to r
If L[i] <= R[j]
A[k] = L[i]
I = I + 1
Else A[k] =R[j]
J =j + 1
MERGE(A, p, r):
If p < r
Q = [(p +r)/2]
MERGE-SORT(A,p, q)
MERGE-SORT(A,q+1, r)
MERGE(A, p,q, r)
从github上找出来的c代码为:
void MERGESORT(int * arr, int p, int r)
{
if (r > p + 1)
{
int q = (p + r) / 2;
MERGESORT(arr, p, q);
MERGESORT(arr, q + 1, r);
MERGE(arr, p, q, r);
}
else if (r == p + 1)
{
if (*(arr + p) > *(arr + r))
{
int temp = *(arr + p);
*(arr + p) = *(arr + r);
*(arr + r) = temp;
}
}
}
void MERGE(int * arr, int p, int q, int r)
{
int n1 = q - p + 1;
int n2 = r - q;
int *arr1, *arr2;
arr1 = (int *)malloc(sizeof(int)*(n1 + 1));
arr2 = (int *)malloc(sizeof(int)*(n2 + 1));
for (int i = 0;i < n1;i++)
*(arr1 + i) = *(arr + p + i);
for (int i = 0;i < n2;i++)
*(arr2 + i) = *(arr + q + 1 + i);
*(arr1 + n1) = INT_MAX;
*(arr2 + n2) = INT_MAX;
int i = 0, j = 0;
for (int k = p;k <= r;k++)
{
if (*(arr1 + i) < *(arr2 + j))
{
*(arr + k) = *(arr1 + i);
i++;
}
else if (*(arr1 + i) > *(arr2 + j))
{
*(arr + k) = *(arr2 + j);
j++;
}
}
free(arr1);
free(arr2);
}
那么,如何计算这个算法的复杂度呢?
可以想象,我们每次将需要排序的序列从中分为两部分,那么,相当于在制造一个满二叉树且树的高度应为logn,此时将整个序列分解为单个的数,两两一对进行比较然后合并上去,复杂度就应该是cnlog(n)。
三、主定理
对于使用分治策略的递归算法,可以使用主定理快速计算其复杂度。
主定理可以表示为:
T(n) = aT(n/b) + f(n)
其中,a为将规模为n的问题分解为a个子问题,b为分解后的子问题的规模为n/b,f(n)为问题分解和子问题解合并的代价。
情况一:若对某个大于零的常数,有f(n)=O(n^logb(a-&)),则T(n)=theta(n^logb(a))
情况二:若f(n) = theta(n^logb(a)),则T(n)=theta(n^logb(a)*log(n))。
情况三:若对某大于零的常数,有f(n)=om(n^logb(a+&)),且对某个常数c小于1和所有足够大的n有af(n/b)<=cf(n),则T(n)=theta(f(n))。