时间复杂度与空间复杂度的分析
算法复杂度分为时间复杂度和空间复杂度
时间复杂度的计算方法
- 用常数1代替运行时间中的所有加法常数
- 修改后的运行次数函数中,只保留最高阶项
- 去除最高阶项的系数
空间复杂度的计算方法
对于一个算法来说,空间复杂度和时间复杂度往往是相互影响的。当追求一个较好的时间复杂度时,可能会使空间复杂度的性能变差,即可能导致占用较多的存储空间;反之,当追求一个较好的空间复杂度时,可能会使时间复杂度的性能变差,即可能导致占用较长的运行时间。
有时我们可以用空间来换取时间以达到目的。
对二分法查找的分析
int BinarySearch(int arr[], int size,int x)
{
int left = 0, right = size-1,mid;
while (left<=right)
{
mid = left + ((right - left)>>1);
if (arr[mid] < x)
left = mid + 1;
else if (arr[mid]>x)
right = mid - 1;
else
return mid;
}
return -1;
}
int recurBinary(int arr[], int left, int right,int x)
{
int mid = left + (right - left);
if (arr[mid] == x)
{
return mid;
}
else if (mid<x)
{
BinarySearch2(arr, mid + 1, right, x);
}
else
{
BinarySearch2(arr, left, mid-1, x);
}
return -1;
}
二分法无论是否递归,都是O(log2 N) 每次查找范围缩小一半 因此可得出其最坏查找范围。
对斐波那契数的分析
#include <stdio.h>
#include <windows.h>
int main()
{
int num,ret;
num = 0;
while (num++<10)
{
ret = fibonacci(num);
printf("%d\n", ret);
}
num = 0;
while (num++<10)
{
ret = fibonacci2(num);
printf("%d\n", ret);
}
system("pause");
return 0;
}
int fibonacci(int num)
{
int one = 0, two = 1, count = 1, ret = 0;
if (num == 1)
{
return 1;
}
else
{
while ( num > count++)//num=2时有问题;
{
ret = one+two;
one = two;
two = ret;
}
return ret;
}
}
int fibonacci2(int num)
{
if (num < 2)
{
return num == 0 ? 0 : 1;
}
return fibonacci2(num - 1) + fibonacci2(num - 2);
}
从运行的时间上来看, 当要输出前n位斐波那契数列,n比较大时,
使用迭代实现 运行效率是比较高的
使用递归方式 运行效率就大大折扣了
递归使用的是选择结构,而迭代使用的是循环结构.
递归能使程序的结构更加清晰,更加简洁,更容易让人理解,从而减少读懂代码的时间,
但大量的递归调用会建立函数的副本,会消耗大量的时间和内存.迭代不不需要反复调用函数和占用额外的内存.
尾递归
如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归的。当递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归。尾递归函数的特点是在回归过程中不用做任何操作,这个特性很重要,因为大多数现代的编译器会利用这种特点自动生成优化的代码。
线性递归:
long Rescuvie(long n) {
return (n == 1) ? 1 : n * Rescuvie(n - 1);
}
尾递归:
long TailRescuvie(long n, long a) {
return (n == 1) ? a : TailRescuvie(n - 1, a * n);
}
long TailRescuvie(long n) {//封装用的
return (n == 0) ? 1 : TailRescuvie(n, 1);
}
当n = 5时
对于线性递归, 他的递归过程如下:
Rescuvie(5)
{5 * Rescuvie(4)}
{5 * {4 * Rescuvie(3)}}
{5 * {4 * {3 * Rescuvie(2)}}}
{5 * {4 * {3 * {2 * Rescuvie(1)}}}}
{5 * {4 * {3 * {2 * 1}}}}
{5 * {4 * {3 * 2}}}
{5 * {4 * 6}}
{5 * 24}
120
对于尾递归, 他的递归过程如下:
TailRescuvie(5)
TailRescuvie(5, 1)
TailRescuvie(4, 5)
TailRescuvie(3, 20)
TailRescuvie(2, 60)
TailRescuvie(1, 120)
120
很容易看出, 普通的线性递归比尾递归更加消耗资源, 在实现上说, 每次重复的过程
调用都使得调用链条不断加长. 系统不得不使用栈进行数据保存和恢复.而尾递归就
不存在这样的问题, 因为他的状态完全由n和a保存.