数据结构入门-时间复杂度和空间复杂度

本节内容:

算法效率

时间复杂度

空间复杂度

算法效率

        一个算法的好坏不能单纯看实现方式是否简洁,不是代码越少,算法的效率就一定越高。算法在编写成可执行程序后,运行时需要耗费时间和空间两种资源,因此衡量一个算法的好坏,就是从时间和空间这两个维度来衡量,即时间复杂度和空间复杂度。

时间复杂度

        顾名思义,时间复杂度就是描述了该算法运行的时间,从理论来说,我们可以把算法都上机测试,但是这很麻烦,而且不同的机器运行同一个算法,所用的时间也是不一样的(机器的新旧,处理器等硬件的差别)。而我们知道,一个算法花费的时间与它执行的语句次数是成正比的,所以我们可以用算法中的基本操作的执行次数来作为时间复杂度的标准。

例子:
void Func1(int N)
{
int count = 0;
for (int i = 0; i < N ; ++ i)
{
    for (int j = 0; j < N ; ++ j)
        {
            ++count;
        }
}
for (int k = 0; k < 2 * N ; ++ k)
    {
        ++count;
    }
int M = 10;
while (M--)
    {
        ++count;
    }

         找到某条语句与问题规模N之间的数学表达式,就是该算法的时间复杂度。该算法中++count语句,第一个for循环执行了n次,第二个for循环执行n次,第一个for循环和第二个for循环是嵌套关系,第三个for循环执行2*n次。最后whille循环执行了10次,所以++count一共执行了n^2+2*n+10次。

        实际中我们计算时间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要大概执行次数,那么这里我们使用大O的渐进表示法。

大O阶方法:
1、用常数1取代运行时间中的所有加法常数。
2、在修改后的运行次数函数中,只保留最高阶项。
3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。


        使用大O的渐进表示法以后,上述代码Func1的时间复杂度为:O(N^2)

大O的渐进表示法去掉了那些对结果影响不大的项,简洁明了的表示出了执行次数。
另外有些算法的时间复杂度存在最好、平均和最坏情况:
最坏情况:任意输入规模的最大运行次数(上界)
平均情况:任意输入规模的期望运行次数
最好情况:任意输入规模的最小运行次数(下界)

例如:在一个长度为N数组中搜索一个数据x
最好情况:1次找到
最坏情况:N次找到
平均情况:N/2次找到
在实际中一般情况关注的是算法的最坏运行情况,所以数组中搜索数据时间复杂度为O(N)

        注意:有时候计算时间复杂度的时候,我们不能看见for循环 嵌套就单纯的把对应执行次数相乘,计算时间复杂度关键在于找出执行次数的表达式,然后用大O渐进表示法得出。

例子:
void BubbleSort(int* a, int n)
{
assert(a);
for (size_t end = n; end > 0; --end)
    {
        int exchange = 0;
        for (size_t i = 1; i < end; ++i)
        {
            if (a[i-1] > a[i])
                {
                    Swap(&a[i-1], &a[i]);
                    exchange = 1;
                }
        }
        if (exchange == 0)
            break;
    }
}
  1. 最坏情况: 在最坏的情况下,即数组完全逆序,每次比较都会导致相邻元素交换。冒泡排序的最坏情况时间复杂度是O(n^2),其中n是数组的大小。这是因为在每一轮遍历中,需要进行n-1次比较和可能的n-1次交换。

  2. 平均情况: 在平均情况下,我们通常会进行n/2轮遍历(n为数组大小),因为每轮遍历至少会将一个元素放置到其最终的有序位置。平均情况下,时间复杂度仍然是O(n^2)。

  3. 最好情况: 在最好的情况下,即数组已经有序,冒泡排序的最好情况时间复杂度是O(n),这是因为只需要进行一轮遍历,每次比较都不需要交换。

例子:
int BinarySearch(int* a, int n, int x)
{
    assert(a);
    int begin = 0;
    int end = n-1;
    while (begin <= end)
        {
            int mid = begin + ((end-begin)>>1);
            if (a[mid] < x)
                begin = mid+1;
            else if (a[mid] > x)
                end = mid-1;
            else
                return mid;
        }
    return -1;
}

        二分查找每次查找会将当前范围减少一半,找了多少次,除了多少个2,假设找了x次,那么,N=2^x,x = log N(以2为底n的对数可以简写),二分查找的时间复杂度为 O(log n)。

例子:
long long Fac(size_t N)
{
    if(0 == N)
        return 1;
    return Fac(N-1)*N;
}

        递归调用是多次调用累加和,每一层递归调用都会进行一次乘法操作,递归的深度是N。所以时间复杂度为O(N)。

例子:
long long Fac(size_t N)
{
    if(0 == N)
        return 1;

for(size_t i = 0;i<N;i++)
{
    //......
}

return Fac(N-1)*N;
}

        递归次数是一个等差数列,n+(n-1)+(n-2)+(n-3)+2+1。时间复杂度为O(N^2)。

例子:
long long Fib(size_t N)
{
    if(N < 3)
        return 1;
return Fib(N-1) + Fib(N-2);
}

        类似细胞分裂,递归次数是一个等比数列 ,2^(n-2)+2^(n-3)+......+2^2+2^1+2^0 = 2^(n-1) -1,时间复杂度为:O(2^n  )

空间复杂度

        空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时额外占用存储空间大小的量度。空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。空间复杂度计算规则基本跟实践复杂度类似,也使用大O渐进表示法。注意:函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定。

例子: 
long long* Fibonacci(size_t n)
{
    if(n==0)
        return NULL;
    long long * fibArray = (long long *)malloc((n+1) * sizeof(long long));
    fibArray[0] = 0;
    fibArray[1] = 1;
    for (int i = 2; i <= n ; ++i)
        {
            fibArray[i] = fibArray[i - 1] + fibArray [i - 2];
        }
    return fibArray;
}

        空间复杂度是算法运行中额外占用的空间,上述算法中因为算法需要,malloc开辟了n+1个空间,所以 空间复杂度是O(n)。

例子:
void BubbleSort(int* a, int n)
{
    assert(a);
    for (size_t end = n; end > 0; --end)
        {
            int exchange = 0;
            for (size_t i = 1; i < end; ++i)
                {
                    if (a[i-1] > a[i])        
                        {
                            Swap(&a[i-1], &a[i]);
                            exchange = 1;
                        }
                }
            if (exchange == 0)
                break;
        }
}

        数组a有n个空间,但是数组a是传参传给该算法的,并不是算法运行额外占用的,所以不计入空间复杂度的计算,在给定数组上直接进行元素比较和交换,不需要额外的数据结构或临时数组。所以该算法空间复杂度为O(1)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值