咱们知道,同一问题可用不一样算法解决,而一个算法的质量优劣将影响到算法乃至程序的效率。算法分析的目的在于选择合适算法和改进算法。那么一个算法的好坏又由不少因素决定和影响,那么咱们怎么衡量一个算法的好坏呢?这里咱们引出来算法复杂度的概念。算法复杂度又分为时间复杂度和空间复杂度。下面咱们就一一来详细看下时间复杂度和空间复杂度。web
首先咱们看下官方是怎么定义时间复杂度的:算法的时间复杂度是一个函数,它定性描述了该算法的运行时间。这是一个关于表明算法输入值的字符串的长度的函数。时间复杂度经常使用大O符号表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是渐近的,它考察当输入值大小趋近无穷时的状况。那咱们本身通俗一点的解释就是时间复杂度其实就是一个函数,这个函数的功能是什么呢?是计算执行基本操做的次数。也就是程序中哪些操做或语句在大量执行,将这些操做认为是整个程序中占用的时间的大小。而且要知道时间复杂度是一个趋势,不是精确的数值,只是粗略数量级。
仍是用例子能更好的说明问题,咱们看下面这个例子:算法
void Test(int n)
{
int iCount = 0;
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < 0; ++j)
{
iCount++;
}
}
for (int k = 0; k < 2 * n; ++k)
{
iCount++;
}
int count = 10;
while (count--)
{
iCount++;
}
}
这个例子中咱们能够看到,有三个循环,第一个循环是一个双重循环,基本操做是icount++,循环了n^2次。第二个循环基本操做是iCount++,循环了2*n次。第三个循环基本操做是iCount++,循环了10次。因此这个程序算法基本操做执行的次数一共是 n^2+2*n+10。那么这是这个算法最终的正确的时间复杂度吗?咱们说不是的,下面咱们来看看时间复杂度具体是怎么计算的。数组
咱们通常使用大O渐进表示法表示时间复杂度,它的具体操做方法是:svg
在此咱们须要注意:算法一般存在最好、平均、 最坏状况。咱们一般关注的时间复杂度是算法的最坏运行状况。函数
咱们再来看几个例子对大O渐进表示法加深下理解:spa
void Test0(int n)
{
int iCount = 0;
for (int iIdx = 0; iIdx < 10; ++iIdx)
{
iCount++;
}
}
咱们能够看到这个算法基本操做时iCount++,循环中执行了10次,因此它基本操做执行了10次,根据大O渐进表示法,将常数用1取代,因此该算法的时间复杂度为1,即O(1)。3d
void Test1(int n)
{
int iCount = 0;
for (int iIdx = 0; iIdx < 10; ++iIdx)
{
iCount++;
}
for (int iIdx = 0; iIdx < 2 * n; ++iIdx)
{
iCount++;
}
}
能够看到这个算法中有两个循环,两个循环中的基本操做都是iCount++,第一个循环循环了10次,第二个循环循环了2*n次,因此基本操做执行的次数是2*n+10次,根据大O渐进表示法,用1取代常数,只保留最高阶,并去掉最高阶系数,这个程序的时间复杂度为n,即O(n)。code
一样的,咱们先看一下官方是怎么定义空间复杂度的:空间复杂度(Space Complexity)是对一个算法在运行过程当中临时占用存储空间大小的量度。咱们用本身的话通俗一点解释空间复杂度就是:代码对于存储空间的占用状况,也就是建立变量的个数。空间复杂度一样适用大O渐进表示法。xml
咱们下面再看几个例子:blog
void Test1(int n)
{
int iCount = 0;
for (int iIdx = 0; iIdx < 10; ++iIdx)
{
iCount++;
}
}
根据咱们本身的理解,这个程序建立了两个变量:count和i,即2,根据大O渐进表示法,用常数1代替常数项,因此它的空间复杂度为1,即O(1)。
int *Merge(int *array1, int size1, int *array2, int size2)
{
int index1 = 0;
int index2 = 0;
int index = 0;
int *temp = (int*)malloc(sizeof(int)*(size1 + size2));
if (temp == NULL)
{
return NULL;
}
while (index1 < size1 && index2 < size2)
{
if (array1[index1] <= array2[index2])
{
temp[index++] = array1[index1];
}
else
{
temp[index++] = array2[index2];
}
}
while (index1 < size1)
{
temp[index++] = array1[index1];
}
while (index2 < size2)
{
temp[index++] = array2[index2];
}
return temp;
}
咱们能够看到,上面这个程序一共定义了三个通常的局部变量index1,index2,index。在下面又动态开辟了(size1+size2)个int类型的空间,也就是建立了(size1+size2)个变量,因此一共建立的变量个数为size1+size2+3,根据大O渐进表示法,空间复杂度为size1+size2,即O(size1+size2),即O(m+n)。
至此,咱们的时间复杂度和空间复杂度介绍的差很少了,下面咱们来看下比较复杂的两个例子:二分查找和斐波那契数列。
首先咱们要知道二分查找的基本思想就是折半。先取数组最中间的元素,与所要查找的元素进行比较,若是所要查找的元素比最中间的元素大,则取右边一半的数继续进行上一步操做。不然取左边一半的数继续进行上一步的操做。直到找到所要查找的数,或者找完全部的数,依然没有找到要查找的数。
那么咱们要求它的时间复杂度和空间复杂度,怎么求呢?咱们看下面这幅图:
long long Fib(int n)
{
if(n<3)
return 1;
return Fib(n-1)+(n-2);
}
依旧是用图片来讲明问题: