【C】数据结构与算法初识

算法的时间复杂度

算法时间复杂度定义与大O记法

算法的时间复杂度是指算法在执行过程中,随着输入数据规模的增长,算法所需执行时间的增长趋势。它是衡量算法效率的一个重要指标

在进行算法分析时,语句的总执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级

T(n)= O(f(n)),它表示随问题规模n的增大,算法执行时间的增长率和 f(n)的增长率相同,称作算法的渐近时间复杂度,简称为时间复杂度

像这样用大写O来体现算法时间复杂度的记法,我们称之为大O记法

一般情况下,随着n的增大,T(n)增长最慢的算法为最优算法

一些常见的阶
0(1):常数阶、O(n):线性阶、0(n²):平方阶…下面会再进行详细介绍

推导大O阶的方法

(1) 用常数1取代运行时间中的所有加法常数
(2) 只保留最高阶项
(3) 如果最高阶项存在且其系数不是1,则去除与这个项相乘的系数

常数阶O(1)

举个例子,现在我们要求1~100的和
根据等差数列求和公式,我们可以轻松得到:

int sum=0;          //执行一次 
sum=(1+100)*100/2;  //一次 
printf("%d",sum);   //一次 

可见,这段代码的运行次数为3,但是根据推导大O阶的方法,用1取代3,故仍为O(1)
因为,n的值的改变并不影响执行的次数

执行时间恒定的算法,称为具有O(1)的时间复杂度

线性阶O(n)

拿最简单的循环举例

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

首先,因为有循环,所以单独语句的执行次数对时间复杂度影响不大,故省略
设循环里的语句要执行a次,要循环n次,那执行次数为an
根据推导大O阶的方法,我们要将a这个常数变为1
故得到其时间复杂度为O(n)

对数阶O(logn)

int num=1;
while(num<n){
	num*=2;
}

把循环过程写出来的话,就是:num* 2 * 2 * 2 * 2…
随着循环次数的增加,num就会越来越接近n
也就是说,有多少个2相乘之后大于n
根据2的k次幂=n,得到x=log2(n)
记为O(logn)

平方阶O(n²)

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

可以看出,在n层循环外面又套了n层循环,即时间复杂度为O(n²)
若外层循环改为m次;即为O(mn)

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

还有一些其他的循环:

nlogn阶 O(nlogn)

for(int i=0;i<n;i++){
	int j=1;
	while(j<n){
		j*=2;
	}
}

立方阶O(n³)

指数阶O(2ⁿ)

常用的时间复杂度所耗费的时间:

O(1)<O(logn)<O(n)<O(nlogn)<O(n²)<O(n3)<O(2")<O(n!)<O(n")

最坏情况与平均情况

最坏运算时间是一种保障,一般情况下,我们提到的运算时间都是最坏运算时间
平均运行时间是指在多次运行一个程序或算法的情况下,总运行时间除 以运行次数得到的平均值
平均运算时间是所有情况中最有意义的,因为它是期望的运行时间
平均时间复杂度:计算所有情况的平均值
最坏时间复杂度:计算在最坏情况下的时间复杂度
一般没有特殊说明的情况下,都是指最坏时间复杂度

ps:一道c的编程题
标题 自然数分解
类别
流程控制
时间限制
2S
内存限制
1000Kb
问题描述
任何一个自然数 m 的立方均可写成 m 个连续奇数之和。例如:
1³=1
2³=3+5
3³=7+9+11
4³=13+15+17+19
编程实现:输入一自然数 n,求组成 n的三次方 的 n 个连续奇数。
输入说明
一个正整数 n,0<n<30。
输出说明
输出 n 个连续奇数,数据之间用空格隔开,并换行
输入样例
4
输出样例
13 15 17 19

下面是我的方法:
复杂难懂

#include <stdio.h>
#include<math.h>
int plus(int n, int k);
int main() {
	int num, sum = 0,k=1;
	scanf_s("%d", &num);
    while (1) {
        sum = plus(num, k);
        if (sum == (int)pow((double)num, 3)) {
            break;
        }
        k += 2;
    }
 	int t= k+num * 2;
    for (; k <t; k += 2) {
        printf("%d ", k);
    }
    printf("\n");
	return 0;
}
int plus(int num, int k) {
	int sum = 0;
	int t= k+num * 2;
	for (;k < t;k += 2) {
		sum += k;
	}
	return sum;
}

以及一位学长的方法:
清晰易懂

#include <stdio.h>
int main() {
	int num;
	scanf_s("%d", &num);
	for (int i = num * num - num + 1;i <= num * num + num - 1;i += 2) {
		printf("%d ", i);
	}
	return 0;
}

可以看出有很大的区别
因为我在分析题意的时候,并没有观察其中的特殊规律,忽略了最简单的方法
我的思路
先创建一个函数plus,将n个奇数加和,返回一个和sum
然后用while循环,调用plus,接着判断sum是否等于num,如果等,则输出,若不等,则k=k+2,将加和起始值k+2变到下一个奇数,重新plus,直到找到正确的第一个奇数,使sum=num

显然,当num比较大时,我这个方法的时间复杂度要远远大于另一种方法,而且在编写并修改程序的过程中,也十分困难

而这就相当于很经典的问题:
明明从1加到100可以通过等差数列求和公式一步求出,时间复杂度为 O(1)
却偏偏要用for循环:

int sum=0;
for(int i=1;i<100;i++){
	sum+=i;	
}

时间复杂度为O(n)

所以,这也提醒了我,在编写程序的时候还是要多思考,不能拿到题上来就写,要学会反思自我

最后

正因为岁月漫长,所以才有了无数热爱,和期待的理由,
正因为岁月漫长,所以等待和追求也更有了意义
愿你我都能耐得住寂寞,实现更好的自我~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值