数据结构与算法:复杂度

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:

数据结构与算法是学计算机必须掌握的基础,今天简单介绍一下复杂度问题


提示:以下是本篇文章正文内容,下面案例可供参考

一、时间复杂度

1.算法时间复杂度的概念

算法的时间复杂度是一个定量描述算法运行速度的快慢,由于不同的计算机CPU有所不同,所以时间复杂度是无法准确给出唯一的,但是算法的运行时间一定是和算法的语句执行数量成正比,所以给出定义,算法的基础语句操作次数就是我们的算法时间复杂度

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);
}

Func1执行的基本操作次数为N^2+2*N+10。
我们在实际使用时,只计算大概执行次数,所以这
里我们使用大O的渐进表示法。
所以表示为时间复杂度为O(N^2)。

2.大O的渐进表示法

(1)只保留当n无穷大时,对函数影响最大的要素,影响较小的可以忽略
(2)当运算次数为常数项时,可以直接用O(1)来表示时间复杂度
(3)如果出现N/2这种,需要去除常数项
(4)时间复杂度如果在各种情况下不同(如在数组内从第一个遍历寻找元素),以最坏的情况作为时间复杂度

3.时间复杂度例题

(1)

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);
}

这里遵照最高项原则,复杂度为O(2*N)

(2)

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?O(M): O(N)

(3)

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

有限的常数项用O(1)表示

(4)

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-1,n-2…2,1所以比较次数为(n*(n-1))/2,时间复杂度为O(n^2)

(5)

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

递归中从N到0,时间复杂度为O(N)

(6)

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

在这里插入图片描述

这里第一行是2^ 0,第二行是2^ 1,后面逐渐增加到2^(n-1)
利用等比数列错位相减法所以时间复杂度为O(2^n-1))

二、空间复杂度

1.空间复杂度的概念

同时间复杂度一样,空间复杂度也是一个数学表达式,用来表示一个算法在运行过程中临时(额外)占用存储空间大小的量度,也使用大O的渐进表示法

== 注:函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定。==

实例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)

实例2:

long long Fac(size_t N)
{
if(N == 0)
return 1;
return Fac(N-1)*N;
}
这里函数进行栈帧的开辟和销毁,调用了N次,每次开辟的都是常数量的栈帧,所以空间复杂度为O(N)

实例3:

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;
}

我们在上面提到,斐波那契数列递归算法中时间复杂度为O(2^N),这里我们提一个点,时间是不可回收,不可叠加的,但空间是可以回收的,在每一次调用完 fibArray[i - 1]后,开辟的空间会回收掉,根据函数栈帧的知识,fibArray [i - 2]的栈帧会在回收的fibArray[i - 1]函数的空间上开辟,所以空间复杂度就为O(N)。

总结

熟练掌握空间复杂度和时间复杂度相关的知识,对于我们优化代码有很大的帮助,尤其注意,常常会选择舍空间保时间的算法。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值