算法导论梳理(一)

算法导论这本书,断断续续看了很久也只看了三分之一左右。近来又忙于别的方面的事情,把这本大部头搁置了很久,于是决定写上几篇来帮助自己梳理回忆一下。

 

一、插入排序

这是书上开篇介绍的第一个算法,问题描述如下:

输入:n个数的一个序列

输出:输入序列的一个排列,满足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’<=…<=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))。



你可能感兴趣的:(算法导论)