若是解决一个问题的算法被肯定下来,并用某种证实方法证实其是正确的,那么接下来就要判断该算法的运行时间,以及运行时占用的空间。这一章主要讨论html
上述说法实在太过晦涩了。举一个简单的例子。当g(N) = N^2时,g(N) = O(N^3),g(N) = O(N^4)都是对的。g(N) = Ω(N), g(N) = Ω(1)也都是对的。g(N) = Θ(N^2)则表示g(N) = O(N^2),g(N) = Ω(N^2)。即当前的结果时最符合g(N)自己的增加趋势的。如图所示:算法
有三条重要的法则须要记住:编程
在用大O表示法的时候,要保留高阶次幂,丢弃常数项和低阶次幂。经过增加率对函数进行分类如图:数组
咱们总能经过计算极限lim f(N) / g(N) (n->∞)来肯定两个函数f(N)和g(N)的相对增加率。可使用洛必达准则进行计算。数据结构
好比,f(N) = NlogN和g(N) = N^1.5的相对增加率,便可计算为f(N) / g(N) = logN / N^0.5 = log^2 N / N。又由于N的增加要快于logN的任意次幂。因此g(N)的增加快于f(N)的增加数据结构和算法
洛必达准则:若lim f(N) = ∞ (n->∞)且lim g(N) = ∞ (n->∞).则lim f(N)/g(N) = lim f'(N)/g'(N) (n->∞)。函数
为了便于分析问题,咱们假设一个模型计算机。它执行任何一个基础指令都消耗一个时间单元,而且假设它有无限的内存。学习
// 书上例程 // 计算i^3的累加求和 int sum (int N) { int i, PartialSum; PartialSum = 0; /*1*/ for(i = 1; i <= N; i++) /*2*/ PartialSum += i * i * i;/*3*/ return PartialSum; /*4*/ }
这里针对每行进行分析:编码
合计花费1+2N+2+4N+1=6N+4个时间单元。设计
可是实际上咱们不用每次都这样分析,由于面对成百上千行的程序时,咱们不可能每一行都这样分析。只需计算最高阶。可以看出for循环占用时间最多。所以时间复杂度为O(N)
for (i = 0; i < N; i++) for (j=0; j < N; j++) k++; // 1 * N * N = N^2,时间复杂度为O(N^2)
for (i = 0; i < N; i++) A[i] = 0; // O(N) for (i = 0; i < N; i++) for (j = 0; j < N; j++) A[i] += A[j] + i + j; // O(N^2) // 总时间为O(N) + O(N^2),所以取最高阶,总时间复杂度为O(N^2)
咱们要避免在递归调用中作重复的工做。
最大子序列问题:给定整数A1, A2, ... , AN(可能有负数),求任意连续整数和的最大值。若是全部整数均为负数,则最大子序列和为0
// 书上例程 int MaxSubsequenceSum(const int A[], int N) { int ThisSum, MaxSum, i, j, k; MaxSum = 0; for (i = 0; i < N; i++) { for (j = i; j < N; j++) { ThisSum = 0; for (k = i; k <= j; k++) { ThisSum += A[k]; } if (ThisSum > MaxSum) { MaxSum = ThisSum; } } } return MaxSum; }
int MaxSubsequenceSum(const int A[], int N) { int ThisSum, MaxSum, i, j, k; MaxSum = 0; for (i = 0; i < N; i++) { ThisSum = 0; for (j = i; j < N; j++) { ThisSum += A[k]; if (ThisSum > MaxSum) { MaxSum = ThisSum; } } } return MaxSum; }
// 书上例程 int max3(int a, int b, int c) { int x; x = a > b? a: b; return (x > c? x: c); } int MaxSubsequenceSum(const int A[], int Left, int Right) { int MaxLeftSum, MaxRightSum; int MaxLeftBorderSum, MaxRightBorderSum; int MaxLeftThisSum, MaxRightThisSum; int Center; int cnt; if (Left == Right) { if (A[Left] > 0) { return A[Left]; } else { return 0; } } Center = (Left + Right) / 2; MaxLeftSum = MaxSubsequenceSum(A, Left, Center); MaxRightSum = MaxSubsequenceSum(A, Center + 1, Right); MaxLeftBorderSum = 0; MaxLeftThisSum = 0; for (cnt = Center; cnt >= Left; cnt--) { MaxLeftThisSum += A[cnt]; if (MaxLeftThisSum > MaxLeftBorderSum) { MaxLeftBorderSum = MaxLeftThisSum; } } MaxRightBorderSum = 0; MaxRightThisSum = 0; for (cnt = Center + 1; cnt <= Right; cnt++) { MaxRightThisSum += A[cnt]; if (MaxRightThisSum > MaxRightBorderSum) { MaxRightBorderSum = MaxRightThisSum; } } return max3(MaxLeftSum, MaxRightSum, MaxRightBorderSum + MaxLeftBorderSum); }
//书上例程 int MaxSubsequenceSum(const int A[], int N) { int ThisSum, MaxSum, j; ThisSum = MaxSum = 0; for (j = 0; j < N; j++) { ThisSum += A[j]; if (ThisSum > MaxSum) { MaxSum = ThisSum; } else if (ThisSum < 0) { ThisSum = 0; } } return MaxSum; }
若是一个算法用常数时间(O(1))将问题的大小消减为其一部分(一般是1/2),那么该算法就是O(logN)。另外一方面,若是使用常数时间只是把问题减小一个常数(如将问题减小1),那么这种算法就是O(N)的。
// 书上例程,时间复杂度为O(logN) #define NotFound -1 int BinarySearch(const ElementType A[], ElementType X, int N) { int low, high, mid; low = 0; high = N - 1; mid = (low + high) / 2; while (low <= high) { if (A[mid] < X) { low = mid + 1; } else if (A[mid] > X) { high = mid - 1; } else { return mid; } } return NotFound; }
// 书上例程:展转相除法,时间复杂度O(logN) int test(unsigned int M, ungisned int N) { unsigned int Rem; while (N > 0) { Rem = M % N; M = N; N = Rem; } return M; }
// 书上例程,时间复杂度O(logN) long int Pow(long int X, unsigned int N) { if (N == 0) { return 1; } else if (N == 1) { return X; } if (isEven(N)) { return Pow(X * X, N / 2); } else { return Pow(X * X, N / 2) * X; } }
//书上例程,时间复杂度O(N^2) void test(int N) { int Rel = 0, Tot = 0; int i, j; for( i = 1; i <= N; i++) { for ( j = i + 1, j <= N; j++) { Tot++; if (Gcd(i,j) == 1) { Rel++; } } } printf("%f", (double)Rel / Tot); }
有时分析会估计过大。那么或者须要分析的更细致,或者平均运行时间显著小于最坏情形的运行时间而又没办法对所得的界加以改进。许多算法,最坏的界实经过某个不良输入达到的,可是实践中它一般是估计过大的。对于大多数这种问题,平均情形的分析是极其复杂的,或者未解决的。最坏情形的界有些过度悲观可是它是最好的已知解析结果。
敬告:
本文原创,欢迎你们学习转载_
转载请在显著位置注明:
博主ID:CrazyCatJack
原始博文连接地址:http://www.javashuo.com/article/p-tggaygmo-hu.html