前言
Algorithm入门,随便写一写,算法复杂度分析,和几个简单的问题——最大子序列之和、最大公因数、二分查找。
复杂度分析
常用记号
O(f(n))
O
(
f
(
n
)
)
,表示上界,
Ω(f(n))
Ω
(
f
(
n
)
)
表示下界,
Θ(f(n))
Θ
(
f
(
n
)
)
表示渐进。
一般情况下进行上界分析,也就是大O分析,有的情况下会进行复杂度的下界分析。
下面看几个简单问题。
最大子序列之和
问题描述,给出 A0,A1,...,An−1 A 0 , A 1 , . . . , A n − 1 ,n个数的序列,找出其最大的子序列之和。其中序列中的项均为正整数或者负整数。如果所有的项都是负数,那么最大子序列之和为0,即空序列的和。
首先,直接遍历求出所有和,比较得到最大者,时间复杂度 O(n3) O ( n 3 ) 。
int Max_subsequence_sum(const int A[],int n)
{
int Max_sum = 0;
for(int i = 0; i < n; i++)
{
for(int j = i; j < n; j++)
{
int This_sum = 0;
for(int k = i; k <= j; k++)
{
This_sum += A[k];
}
if(This_sum > Max_sum)
Max_sum = This_sum;
}
}
return Max_sum;
}
很容易看出,可以进行优化,于是有 O(n2) O ( n 2 ) 的下列算法。
int Max_subsequence_sum(const int A[],int n)
{
int Max_sum = 0;
for(int i = 0; i < n; i++)
{
int This_sum = 0;
for(int j = i; j < n; j++)
{
This_sum += A[j];
if(This_sum > Max_sum)
Max_sum = This_sum;
}
}
return Max_sum;
}
我们也可以利用分治策略解决这个问题,将序列平均分为左右两个子序列,那么和最大的子序列可能完全位于左边的子序列,也可能位于右边的子序列,也可能是左边子序列中一项到其最右边与右边第一项到某一项合起来的子序列。时间 T(n)=2T(n/2)+O(n) T ( n ) = 2 T ( n / 2 ) + O ( n ) ,由主定理很容易得到,时间复杂度为 O(nlogn) O ( n log n ) 。实现如下。
int Max_sub_sum(int A[],int left,int right)
{
if(left == right)
{
if(A[left] > 0)
return A[left];
else
return 0;
}
int mid = (left + right) / 2;
int max_left_sum = Max_sub_sum(A,left,mid);
int max_right_sum = Max_sub_sum(A,mid+1,right);
int left_border_sum = 0;
int max_left_border_sum = 0;
for(int i = mid; i >= left; i--)
{
left_border_sum += A[i];
if(left_border_sum > max_left_border_sum)
max_left_border_sum = left_border_sum;
}
int right_border_sum = 0;
int max_right_border_sum = 0;
for(int i = mid+1; i <= right; i++)
{
right_border_sum += A[i];
if(right_border_sum > max_right_border_sum)
max_right_border_sum = right_border_sum;
}
int max_cross_sum = max_left_border_sum + max_right_border_sum;
//reurn the max of three summation
if(max_left_sum >= max_cross_sum && max_left_sum >= max_right_sum)
return max_left_sum;
else if(max_right_sum >= max_left_sum && max_right_sum >= max_cross_sum)
return max_right_sum;
else
return max_cross_sum;
}
int Max_subsequence_sum(int A[],int n)
{
return Max_sub_sum(A,0,n-1);
}
上述分治算法看起来非常的冗长。但是还要一个更好更简单的时间复杂度为 O(n) O ( n ) 的算法。如下。
int Max_subsequence_sum(const int A[],int n)
{
int This_sum = 0;
int Max_sum = 0;
for(int i = 0; i < n; i++)
{
This_sum += A[i];
if(This_sum > Max_sum)
Max_sum = This_sum;
if(This_sum < 0)
This_sum = 0;
}
return Max_sum;
}
最后这个算法很容易理解。这也是求解这个问题最好的算法。
求最大公因数
最佳方法当然是辗转相除法/欧几里得算法了。
int gcd(int m,int n)
{
int tmp;
while(n > 0)
{
tmp = m % n;
m = n;
n = tmp;
}
return m;
}
递归写法:
int gcd(int m,int n)
{
return n == 0 ? m : gcd(n,m%n);
}
二分查找
问题:在排好序的n个数中查找等于x的元素,未找到返回-1。
假定这里是升序排列的。
int Binary_search(int A[],int x,int n)
{
int low = 0;
int high = n-1;
int mid;
while(low <= high)
{
mid = (low + high) / 2;
if(A[mid] > x)
high = mid-1;
else if (A[mid] < x)
low = mid+1;
else
return mid;
}
return -1;
}
很明显时间复杂度 O(logn) O ( log n ) 。