时间复杂度和空间复杂度的了解和计算

1.算法效率

  • 算法效率分为两种:第一种是时间效率,第二种是空间效率。

2.大O渐进表示法

  • 大O符号(Big O notation):用于描述函数渐进行为的数学符号。
  • 大O渐进表示法是估算
  • 推导大O阶方法:
  1. 常数1取代运行时间中的所有加法常数。
  2. 在修改后的运行次数函数中,保留最高阶项
  3. 如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。

3.时间复杂度

  • 时间复杂度:算法中的基本操作的执行次数,为算法的时间复杂度。
  • 时间复杂度是一个估算,是去看表达式中影响最大的一项。
  • 时间复杂度用大O渐进表示法。

例子一:

// 请计算一下Func1基本操作执行了多少次?
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;
	}

    printf("%d\n",count);
}

其中F(N)=N^2+2*N+10,按大O阶方法推导完后:O(N^2)

例子二:

// 计算Func2的时间复杂度?
void Func2(int N)
{
	int count = 0;
	for (int k = 0; k < 2 * N; ++k)
	{
		++count;
	}

	int M = 10;
	while (M--)
	{
		++count;
	}
}

其中F(N)=2*N+10,按大O阶方法推导完后:O(N)

例子三:

// 计算Func3的时间复杂度?
void Func3(int N, int M)
{
	int count = 0;
	for (int k = 0; k < M; ++k)
	{
		++count;
	}

	for (int k = 0; k < N; ++k)
	{
		++count;
	}
}

其中F(N)=M+N,按大O阶方法推导完后:O(M+N)

这里说明不一定有一个未知数。

例子四:

// 计算Func4的时间复杂度
void Func4(int N)
{
	int count = 0;
	for (int k = 0; k < 100; ++k)
	{
		++count;
	}
	printf("%d\n", count);
}

其中F(N)=100,按大O阶方法推导完后:O(1)

说明:确定的常数次都是O(1)

例子五:

// 计算strchr的时间复杂度?
const char* strchr(const char* str, char character)
{
	while (*str != '\0')
	{
		if (*str == character)
			return str;
		++str;
	}
	return NULL;
}

假设字符串的长度是N

有些算法的时间复杂度存在最好,平均,最坏的情况。

最坏情况:任意输入规模的最大运行次数(上界)。              O(N)

平均情况:任意输入规模的期望运行次数。                             O(N/2)

最好情况:任意输入规模的最小运行次数(下界)。               O(1)

当算法存在这三种情况的时候,看最坏情况

所以这题的时间复杂度是:O(N)

例子六:

// 计算BubbleSort的时间复杂度?
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;
	}
}

这是一个冒泡排序

第一趟冒泡:N

第二趟冒泡:N-1

第三趟冒泡:N-2

.............

第N趟冒泡:1

这是一个等差数列:(N+1)*N/2

所以时间复杂度:O(N^2)

例子七:

// 计算BinarySearch的时间复杂度?
int BinarySearch(int* a, int n, int x)
{
	assert(a);
	int begin = 0;
	int end = n;
	while (begin < end)
	{
		int mid = begin + ((end - begin) >> 1);
		if (a[mid] < x)
			begin = mid + 1;
		else if (a[mid] > x)
			end = mid;
		else
			return mid;
	}
	return -1;
}

这是一个折纸查找也叫二分查找

最好情况:O(1)

假设找了X次:1*2*2*2*2.....*2=N -> 2^X=N -> X=log\binom{N}{2}

算法的复杂度计算喜欢省略简写成logN

有些书本,或者网上资料会写成O(lgN),严格是不对的

所以最坏情况O(logN)

例子八:

// 计算阶乘递归Factorial的时间复杂度?
long long Factorial(size_t N)
{
	return N < 2 ? N : Factorial(N - 1) * N;
}

这是一个递归函数

 1*2*3*4*5*6*7*8*9*10

递归了n 次,每次递归运算多少次->O(1),整体就是O(N)

4.空间复杂度

  • 空间复杂度是一个对一个算法在运行过程中临时占用储存空间大小的亮度,算的是变量的个数。
  • 空间复杂度也是用大O渐进表示法,类似时间复杂度的方式去计算变量个数

例子一:

// 计算BubbleSort的空间复杂度?
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;
	}
}

BubbleSort中有 a,n,end,exchange,i五个变量

所以空间复杂度:O(1)

例子二:

// 计算Fibonacci的空间复杂度?
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;
}

Fibonacci中有malloc(n+1)和其他5个变量。

malloc是开了一个数组大小是n+1个变量。

所以空间复杂度:O(n)

例子三:

// 计算阶乘递归Factorial的空间复杂度?
long long Factorial(size_t N)
{
	return N < 2 ? N : Factorial(N - 1) * N;
}

   调用时,建立栈帧;返回时,销毁栈帧。

递归调用了N层,每次调用建立一个栈帧,每个栈帧使用了常熟个空间->O(1)<-每一层都是

有n个栈帧,空间复杂度:O(N)

5.常见的时间复杂度

O(N^2)     O(N)    O(logN)    O(1)

O(1)随着数量的增加次数不变(最优的时间复杂度)。 

O(logN)仅次于O(1)。

6.时间复杂度和空间复杂度小结

时间不算时间算执行次数,空间不算空间算变量个数。

时间是累计的,空间是不累计的 -> 循环走了n次,重复利用的是一个空间

7.复杂度练习题

  1. 消失的数字OJ链接: https://leetcode-cn.com/problems/missing-number-lcci/
  2. 旋转数组OJ链接:http:// https://leetcode-cn.com/problems/rotate-array/

第二题的非常nb的人写出的思路当然不是我

前n-k个逆置

后k个逆置

再整体逆置

第一题参考答案

int missingNumber(int* nums, int numsSize)
{
    int x=0;
    int i=0;
    int j=0;
    for(i=0;i<numsSize;i++)
    {
        x^=nums[i];
    }
    for(j=0;j<numsSize+1;j++)
    {
        x^=j;
    }

    return x;
}

第二题参考答案

void Reverse(int*nums,int left,int right)
{
    while(left<right)
    {
        int tmp=nums[left];
        nums[left]=nums[right];
        nums[right]=tmp;

        left++;
        right--;
    }
}

void rotate(int* nums, int numsSize, int k)
{
    if(k>=numsSize)
    {
        k%=numsSize;
    }
    Reverse(nums,numsSize-k,numsSize-1);
    Reverse(nums,0,numsSize-k-1);
    Reverse(nums,0,numsSize-1);

}

大家好我是一个小白,这是我听课笔记总结了一下,希望对大家有所帮助。

  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值