当前位置: 首页 > article >正文

80.【C语言】数据结构之时间复杂度

目录

1.数据结构的定义

2.算法的定义

3.算法的效率

1.衡量一个算法的好坏的方法

例题:计算以下代码的循环次数

2.大O的渐进表示法

练习1:求下列代码的时间复杂度

练习2:求下列代码的时间复杂度

练习3:求下列代码的时间复杂度

练习4:求下列代码的时间复杂度

4.总结:计算时间复杂度的方法

5.时间复杂度的排名


1.数据结构的定义

参见63.【C语言】再议结构体(上)

也可以理解为数据在内存中的存储形式(管理数据)

比如说写通讯录可以用静态数组,动态数组和链表形式存储

2.算法的定义

简单的定义:一系列的计算步骤,用来将输入数据转化成输出结果

3.算法的效率

1.衡量一个算法的好坏的方法

两个方面衡量:时间效率(计算时间分复杂度)和空间效率(空间复杂度)

比较时间要在同一个环境下进行(为了控制变量,但在实际情况下难以控制)

-->比较运行次数(和环境无关)

运行次数举例:对于同一组数据,快速排序和冒泡排序的运行次数不同

例题:计算以下代码的循环次数

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),其值为循环次数

可得:F(N)=N^2+2N+10

N=10F(N)=130
N=100F(N)=10210
N=1000F(N)=1002010

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

比如可以计算量级(N=10的次方),当N较大时(可理解为n \to \infty),影响F(N)的值主要为N^2,因此$F(N) \approx N^2$

对比N^2N^2+2N+10的两张图

2.大O的渐进表示法

大O符号:用于描述函数渐近行为的数学符号,可以用来衡量时间复杂度和空间复杂度

在上述函数中,Func1的时间复杂度为O(N^2),其去掉了对结果影响不大的地方

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

运行次数n==2N+10

1.取最高阶的式子(去常数)

n \to \infty时,+10对结果影响不大,因此$2N+10 \approx 2N$

2.去系数

因为n \to \infty时,N前面的系数对结果影响也不大(\lim_{n \to \infty} 2N = \lim_{n \to \infty} N = \infty),因此时间复杂度为O(N)

练习2:求下列代码的时间复杂度

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

O(100)吗?不是,为O(1),这里的1不是次数,而是指常数次

练习3:求下列代码的时间复杂度

#include <stdio.h>
const char* strchr( const char* str, char character)
{
	while (*str)
	{
		if (*str == character)
			return str;
		str++;
	}
	return str;
}

int main()
{
	char character;
	char str[100] = { 0 };
	scanf("%c", &character);
	scanf("%s", str); // 数组名就是数组的首元素地址
	const char* s_str=strchr(str, character);
	printf("%s", s_str);
}

发现:运算次数和字符串的长度以及和要找的字符位置有关

即该题的时间复杂度存在最好,平均和最坏情况:

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

平均情况:任意输入规模的期望运行次数

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

按预期管理来看,应该取最坏的情况,即O(N),N为输入字符串的长度

练习4:求下列代码的时间复杂度

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int n = 0;
	int arr[100] = { 0 };
	int tmp = 0;
 
	//输入元素
	a:printf("输入数组元素个数:");
	scanf("%d", &n);
	if (n > 100 || n < 1)
	{
		printf("输入的元素个数超出范围或无效!请重新输入!\n");
		goto a;
	}
	printf("输入数组元素:");
	for (int k = 0; k < n; k++)
	{
		scanf("%d", &arr[k]);
	}
	//冒泡排序
	for (int i = 1; i <= n - 1; i++)//趟数
	{
		int flag = 1;//每趟排序时置为1
		for (int j = 0; j <= n - i - 1; j++)//步数
		{
			if (arr[j] > arr[j + 1])
			{
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
				flag = 0;//发生交换flag置0
			}
		}
		if (1 == flag)
		{
			break;//未发生交换则退出循环
		}
	}
 
	//打印结果
	for (int q = 0; q < n; q++)
	{
		printf("%d ", arr[q]);
	}
	return 0;
}

同样的,在冒泡排序(参见42.【C语言】冒泡排序)中,运算次数也有最好和最坏的情况

显然此代码是按升序排列

最好:给的数组就是按升序排列的(如0 1 2 3 4 5 6 7 8 9)

尽管j的for循环中的if判断的交换没有执行(相当于空转),但由于flag一直为0,因此到j为8时才能退出循环

因此运行次数n==N-1-->舍去常数后-->N

因此最好:O(N)

最坏:给的数组就是按降序排列的(如9 8 7 6 5 4 3 2 1 0)

因此因此运行次数n==(N-1)+(N-2)+(N-3)+...+(1)==(1+N-1)*(N-1)/2==(N^2-N)/2

取最高阶的式子0.5N^2,去掉最高阶式子前的系数-->N^2

因此最坏:O(N^2)

4.总结:计算时间复杂度的方法

若次数为常数,时间复杂度为O(1)

若次数为N的表达式

1.取最高阶的式子(去常数) 2.去掉最高阶式子前的系数

若存在最好,最坏情况,取最坏的情况作为时间复杂度

5.时间复杂度的排名

O(1)<O(logN)<O(N)<O(N*logN)<O(N^3)<O(2^N)


http://www.kler.cn/news/356277.html

相关文章:

  • React Strict DOM:React Native 通用应用程序的未来
  • 应用指南 | 在IvorySQL中使用pglogical扩展模块
  • 【调教树莓派】如何获取树莓派的硬件ROOT(JTAG裸片调试)
  • docker 指令集
  • 计算机网络基本架构知识点
  • 手机摄影入门
  • 【FFmpeg】Common command
  • 网页前端开发之HTML入门篇:链接标签 a
  • 5 -《本地部署开源大模型》在Ubuntu 22.04系统下ChatGLM3-6B高效微调实战
  • window与ubuntu双系统时间同步
  • 易泊车牌识别:海外车牌快速定制,开启智能识别新时代
  • LSTM反向传播及公式推导
  • 如何查看公众号真实粉丝数,2024年还有哪些粉丝百万以上的大号?
  • 性能评测第一,阿里开源可商用AI模型Ovis 1.6使用指南,AI多模态大模型首选
  • java 第12天 单例 接口
  • Redis入门到精通(二):入门Redis看这一篇就够了
  • 云黑系统全解无后门 +搭建教程
  • 保研考研机试攻略:python笔记(1)
  • 初识git · 远程操作
  • DAY52WEB 攻防-XSS 跨站反射型存储型DOM 型标签闭合输入输出JS 代码解析