数据结构如何计算复杂度

发布时间:2024-11-29 14:01

在学习具体的数据结构和算法之前,每一位初学者都要掌握一个技能,即善于运用时间复杂度空间复杂度来衡量一个算法的运行效率。

通过算法所编写出的程序的运行效率。程序的运行效率具体可以从 2 个方面衡量,分别为:

  • 程序的运行时间。
  • 程序运行所需内存空间的大小。

数据结构中,用时间复杂度来衡量程序运行时间的多少;用空间复杂度来衡量程序运行所需内存空间的大小。

时间复杂度 

判断一个算法所编程序运行时间的多少,并不是将程序编写出来,通过在计算机上运行所消耗的时间来度量。原因很简单,一方面,解决一个问题的算法可能有很多种,一一实现的工作量无疑是巨大的,得不偿失;另一方面,不同计算机的软、硬件环境不同,即便使用同一台计算机,不同时间段其系统环境也不相同,程序的运行时间很可能会受影响,严重时甚至会导致误判。

实际场景中,我们更喜欢用一个估值来表示算法所编程序的运行时间。所谓估值,即估计的、并不准确的值。注意,虽然估值无法准确的表示算法所编程序的运行时间,但它的得来并非凭空揣测,需要经过缜密的计算后才能得出。

也就是说,表示一个算法所编程序运行时间的多少,用的并不是准确值(事实上也无法得出),而是根据合理方法得到的预估值。

那么,如何预估一个算法所编程序的运行时间呢?

很简单,先分别计算程序中每条语句的执行次数,然后用总的执行次数间接表示程序的运行时间。

大 O 记法的表示方法也很简单,格式如下:

O(频度)

其中,这里的频度为最简之后所得的频度。

例题1):

for(int i = 0 ; i < n ; i++)     //<- 从 0 到 n-1,执行n次
{
    a++; 
}

看循环次数,循环n次,所以程序的时间复杂度为 O(n);

例题2):

for(int i = 0 ; i < n ; i++) 
{ 
    for(int j = 0 ; j < m ; j++) 
    {
        num++; 
    }
}

外层循环n,内层循环m次,要知道,当 n、m 都无限大时,我们完全就可以认为 n==m。

程序的时间复杂度为 O(n^2)。

例题3):

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+2*N+M;

事实上,对于一个算法(或者一段程序)来说,其最简频度往往就是最深层次的循环结构中某一条语句的执行次数。例如 2n+1 最简为 n,实际上就是 a++ 语句的执行次数;同样 2n2+2n+1 简化为  n2,实际上就是最内层循环中 num++ 语句的执行次数。

简写程序时间复杂度为O(N^2);

例题5):

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

只要是常数次执行,时间复杂度统统都是O(1);

例题6):

//二分查找
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;
		}
		else
			return mid;
	}

	return -1;
}

最糟糕的情况就是没找到目标,总数N个,每次查找数量减少一半

可以表示为 :N/2/2/2/……=1;

N=1*2*2*2*2*……;

2^x=N;

x=log2N
所以时间复杂度为O(log2N);

例题7):

//递归斐波那契数
long long Fib(size_t N)
{
	if (N < 3)
		return 1;

	return Fib(N - 1) + Fib(N - 2);
}

数据结构如何计算复杂度_第1张图片

时间复杂度为,2^0+2^1+2^2+2^3+……+2^N

简写程序的时间复杂的为 :O(2^N) 

O(1)常数阶 < O(logn)对数阶 < O(n)线性阶 < O(n2)平方阶 < O(n3)(立方阶) < O(2n) (指数阶)

空间复杂度

和时间复杂度类似,一个算法的空间复杂度,也常用大 O 记法表示。

要知道每一个算法所编写的程序,运行过程中都需要占用大小不等的存储空间,例如:

  • 程序代码本身所占用的存储空间;
  • 程序中如果需要输入输出数据,也会占用一定的存储空间;
  • 程序在运行过程中,可能还需要临时申请更多的存储空间。


首先,程序自身所占用的存储空间取决于其包含的代码量,如果要压缩这部分存储空间,就要求我们在实现功能的同时,尽可能编写足够短的代码。

程序运行过程中输入输出的数据,往往由要解决的问题而定,即便所用算法不同,程序输入输出所占用的存储空间也是相近的。

事实上,对算法的空间复杂度影响最大的,往往是程序运行过程中所申请的临时存储空间。不同的算法所编写出的程序,其运行时申请的临时存储空间通常会有较大不同。

 例题:

void BubbleSort(int* a, int n)
{
	assert(a);

	for (int end = n; end > 0; end++)
	{
		int exchange = 0;
		for (int 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);

注意:在多数场景中,一个好的算法往往更注重的是时间复杂度的比较,而空间复杂度只要在一个合理的范围内就可以。

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号