时间复杂度

1.算法效率

如何衡量⼀个算法的好坏呢?
案例:旋转数组 https://leetcode.cn/problems/rotate-array/description/
思路:循环K次将数组所有元素向后移动⼀位

void rotate(int* nums, int numsize, int k)
{
	while (k--)
	{
		int end = nums[numsize - 1];//把最后一个先记住
		for (int i = numsize - 1; i > 0; i--)
		{
			nums[i] = nums[i - 1];//从倒数第二个开始给倒数第一个
		}
		nums[0] = end;//第一个赋值为最后一个
	}
}

但是未能通过全部案例,算法没问题,但是超出了时间限制。

2.复杂度

如何衡量算法的好坏呢,利用复杂度,复杂度有时间复杂度以及空间复杂度

复杂度是一个粗估的概念,并不是一个准确的值。

时间复杂度主要衡量⼀个算法的运⾏快慢,⽽空间复杂度主要衡量⼀个算法运⾏所需要的额外空间。在计算机发展的早期,计算机的存储容量很⼩。所以对空间复杂度很是在乎。但是经过计算机⾏业的迅速发展,计算机的存储容量已经达到了很⾼的程度。所以我们如今已经不需要再特别关注⼀个算法的空间复杂度

2.1时间复杂度

如何计算时间复杂度呢

// 请计算⼀下Func1中++count语句总共执⾏了多少
次?
	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^2+2N+10.

2.2⼤O的渐进表⽰法

由此可得,上面的Func1代码时间复杂度为O(N^2)

2.2.1

再来一题:

// 计算Func2的时间复杂度?
void Func2(int N)
{
	int count = 0;
	for (int k = 0; k < 2 * N; ++k)
	{
		++count;
	}
	int M = 10;
	while (M--)
	{
		++count;
	}
	printf("%d\n", count);
}

 2.2.2

T(N)=2N+10

由大O渐进表示法可知N前的常数项不要,低次项也不要,所以,为

T(N)=N

2.2.3

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;
	}
	printf("%d\n", count);
}

M和N都是未知数

T(N)=O(M+N)

若M>>N,O(M)

若M<<N,O(N)

若M≈N,O(M+N)

2.2.4

void Func4(int N)
{
	int count = 0;
	for (int k = 0; k < 100; ++k)
	{
		++count;
	}
	printf("%d\n", count);
}

由大O规则中的3可知O(100)==O(1)

O(1)中的1代表的是常数。

2.2.5

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

这是一个查找字符的代码

若在一开始,O(1)

若在中间,O(N/2)

若在后面或者超过N,O(N)

2.2.6冒泡排序的时间复杂度

// 计算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;
	}
}

外层循环一次,内层循环根据外层改变

根据等差公式得,T(N)=N(1+N)/2

根据大O渐进表示法,去掉低次项,去掉未知数前的系数,得T(N)=O(N^2)

2.2.7

void func5(int n)
{
	int cnt = 1;
	while (cnt < n)
	{
		cnt *= 2;
	}
}

假设执行的次数为x,2^x=n,假设n=10

即2^x=10,若要跳出循环,x=4.

所以时间复杂度为O(log n)

键盘敲不出2,所以写logn也可

2.2.8

// 计算阶乘递归Fac的时间复杂度?
long long Fac(size_t N)
{
	if (0 == N)
		return 1;
	return Fac(N - 1) * N;
}

一次函数栈帧执行的时间复杂度为O(1)

执行了N次,总的时间复杂度为O(N) .

2.3空间复杂度

跟时间复杂度类型,也采用大O渐进表示法

只需要计算函数运行时开辟的额外空间,在编译期间确定好的不用管

2.3.1

// 计算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;
	}
}

空间复杂度O(1)

1表示的还是常数。

2.3.2

// 计算阶乘递归Fac的空间复杂度?
long long Fac(size_t N)
{
	if (N == 0)
		return 1;
	return Fac(N - 1) * N;
}

递归了N次,创建了N个函数栈帧,所以空间复杂度为O(N)

2.3.3

这个空间复杂度也是O(N)

3.常见的复杂度对比 

 

4.复杂度算法题

4.1翻转数组

. - 力扣(LeetCode)

4.1.1

开始的时候我们写了一个代码

void rotate(int* nums, int numsSize, int k) {
    while (k--)
    {
        int end = nums[numsSize - 1];
        for (int i = numsSize - 1; i > 0; i--)
        {
            nums[i] = nums[i - 1];
        }
        nums[0] = end;
    }
}

 这里的时间复杂度为O(N^2),时间太久了,我们可以学习下列的代码

4.1.2

void rotate(int* nums, int numsize, int k)
{
    int numarr[numsize];//创建一个新数组
    for (int i = 0; i < numsize; i++)
    {
        numarr[(i + k) % numsize] = nums[i];//
    }
    for (int  i = 0; i < numsize; i++)
    {
        nums[i] = numarr[i];//赋值回来
    }

}

 

所以这里的时间复杂度为O(N)

空间复杂度为O(N)

这里是利用了空间换时间 

4.1.3

思路

void reverse(int* nums, int left, int right)
{
	while (left < right)
	{
		//left和right指向的下标开始交换
		int tmp = nums[left];
		nums[left] = nums[right];
		nums[right] = tmp;
		left++;
		right--;
	}
}
void rotate(int* nums, int numsize, int k)//数组  元素个数  移动几位
{
	//前n-k个数据逆置
	reverse(nums, 0, numsize - k - 1);//传的是交换数组的下标
	//后k个数据逆置
	reverse(nums, nums - k, numsize - 1);
	//整体逆置
	reverse(nums, 0, numsize - 1);
}

时间复杂度为O(N),空间复杂度为O(1)

这里发生了一个错误,假设元素个数numsize为1,移动位数k为3.则reverse传值会传负数,更改代码为

void reverse(int* nums, int left, int right)
{
	while (left < right)
	{
		//left和right指向的下标开始交换
		int tmp = nums[left];
		nums[left] = nums[right];
		nums[right] = tmp;
		left++;
		right--;
	}
}
void rotate(int* nums, int numsize, int k)//数组  元素个数  移动几位
{
	k = k % numsize;
	//前n-k个数据逆置
	reverse(nums, 0, numsize - k - 1);//传的是交换数组的下标
	//后k个数据逆置
	reverse(nums, nums - k, numsize - 1);
	//整体逆置
	reverse(nums, 0, numsize - 1);
}

更改处 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值