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

算法设计——最坏时间复杂度分析

目录

 一、简介

二、大 O 表示法的定义

三、基本步骤 

1.确定核心操作

2.分析执行次数

3.确定时间复杂度 

 四、例题

类型一:证明题

​类型二:非递归算法的时间复杂度分析

类型三:递归算法的时间复杂度分析


 一、简介

时间复杂度分为三种:

  • 最坏情况时间复杂度:用大 O 表示法,记作 O(f(n))。它给出了算法运行时间的上界,即对于所有可能的输入规模 n,算法的运行时间都不会超过 O(f(n)) 的增长速度。例如,冒泡排序的最坏情况时间复杂度是 O(n^2),表示随着输入规模 n 的增加,算法的运行时间增长速度不会超过 n^2 的增长速度。
  • 最好情况时间复杂度:用大 Ω 表示法,记作 Ω(g(n))。它给出了算法运行时间的下界,即对于所有可能的输入规模 n,算法的运行时间至少是 Ω(g(n)) 的增长速度。例如,冒泡排序的最好情况时间复杂度是 Ω(n),意味着在最好情况下,算法的运行时间增长速度不低于 n 的增长速度。
  • 平均情况时间复杂度:用大 Θ 表示法,记作 Θ(h(n))。它表示算法在平均情况下的运行时间与 h(n) 的增长速度相同。例如,快速排序的平均情况时间复杂度是 Θ(nlogn),说明在平均情况下,算法的运行时间与 nlogn 的增长速度一致。

         我们通常使用的时间复杂度一般是“最坏情况的时间复杂度”,本篇文章主要介绍的就是用大 O 表示法对代码的时间复杂度进行分析。

二、大 O 表示法的定义

        大  表示法是一种用于描述函数渐近行为的数学符号,它忽略常数因子和低阶项,只保留最高阶项。对于一个函数 ,如果存在正常数  和 ,使得当  时,,则称 

三、基本步骤 

1.确定核心操作

        这里的“核心操作”指的是算法中最核心、执行次数最多且对运行时间影响最大的操作。举个例子,这里给出冒泡排序的C语言代码:

// 冒泡排序算法
void bubbleSort(int arr[], int n) {
    int i, j, temp;
    for (i = 0; i < n - 1; i++) {
        for (j = 0; j < n - i - 1; j++) {
            // 比较操作,这是基本操作之一
            if (arr[j] > arr[j + 1]) {
                // 交换操作,也是基本操作
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

        在上述冒泡排序代码中,if (arr[j] > arr[j + 1])这一行是比较操作,当条件满足时执行的交换操作(temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp;),这两个操作是冒泡排序中执行次数最多的基本操作,随着数组规模n的增大,它们的执行次数决定了算法的时间复杂度。

2.分析执行次数

        由于我们要求的是“最坏的情况”,因此对于冒泡排序,最坏情况是初始序列完全逆序。

        对于长度为 n 的数组,第一轮比较时,需要比较 (n - 1) 次,因为要把最大的元素 “浮” 到数组末尾;第二轮比较时,由于最大的元素已经在正确位置,所以只需要比较 (n - 2) 次;以此类推,直到最后一轮只需要比较 1 次。总的比较次数为:

.

3.确定时间复杂度 

        由大 O 表示法的定义可知,这里的  就是常数因子, 就是低阶项,随着 n 的不断增大, 项的增长速度远远快于 n 项,常数因子  对函数增长速度的影响也可以忽略不计,因此二者均可省略。最终只保留  项,得到冒泡排序在最坏情况下的时间复杂度为 .

 四、例题

类型一:证明题

例1. 求证:

 例2. 求证:.

类型二:非递归算法的时间复杂度分析

例3. 分析以下算法的时间复杂度。

void fun(int n)
{
    int s = 0, i, j, k;

    for(i=0; i<=n; i++)
        for(j=0; j<=i; j++)
            for(k=0; k<=j; k++)
                s++;
}

(1)分析核心操作:代码主体部分由三个循环嵌套组成 ,“核心操作”是 “s++”;

(2)确定执行次数:

(3)确定时间复杂度:去掉常数因子和低阶项可得,该算法的时间复杂度为.

例4. 给出以下算法的时间复杂度。

void func(int n)
{
    int i = 1, k = 100;
    while(i <= n)
    {
        k++;
        i+=2;
    }
}

(1)分析核心操作:代码主体部分由 while 循环构成,“核心操作”为 “k++; i+=2;”;

(2)确定执行次数:设 while 循环语句执行的次数为 m,i 从 1 开始递增,最后取值为 1+2m,根据 while 循环结束条件 i <= n 可得 i = 1+2m <= n,即 .

(3)确定时间复杂度:去掉常数因子和低阶项可得,该算法的时间复杂度为.

类型三:递归算法的时间复杂度分析

例5. 求解汉诺塔问题的递归算法如下,分析其时间复杂度。

void Hanio(int n, char x, char y, char z)
{
	if(n==1)
	{
		printf("将盘片%d从%c搬到%c\n",n,x,z);
	}
	else
	{
		Hanio(n-1, x, y, z);
		printf("将盘片%d从%c搬到%c\n",n,x,z);
		Hanio(n-1, y, x, z);
	}
}

         这里给出汉诺塔问题的示意图(侵删):

   

        针对汉诺塔递归问题算法的代码,可做如下分析:

        当汉诺塔总共只有一层时,只要将该层从 A 移到 C 就可以了,所有代码只要执行一次;当层数大于一时(假设层数为 n,即问题规模为 n 时),第一步需要先将除了最大的盘以外的所有盘从 A 移到 B,接着将最大的盘从 A 移到 C,此时还需要进行一步操作,就是将 B 上所有的盘回归到 A 上,这样问题规模就降为(n-1),即现在只需要将(n-1)个盘从 A 移到 C,如此递归,直到 A 盘上只剩一个盘,此时问题规模降为 1,由此问题可以得到解决。

        设调用 Hanio(n, x, y, z) 的执行时间为 T(n),由其执行过程得到以下求执行时间的递推关系:

T(n) = O(1)                                                         当 n=1时

T(n) = 2T(n-1)+1                                                当 n>1时

问:为什么是2T(n-1)+1而不是T(n-1)+1?

答:因为在进行一次移动之后,需要将汉诺塔进行“相似还原”,这样才能得到一个和原问题“一摸一样”的问题,由此可以通过递归求解。

相关文章:

  • 【算法】十大排序算法(含时间复杂度、核心思想)
  • langfuse追踪Trace
  • 数据结构与算法--数字转字符串方法数
  • 数据结构之栈(C语言)
  • RabbitMQ实现路由模式发送接收消息
  • “零拷贝”(Zero-Copy)技术详解以及使用场景
  • 内网渗透-端口转发
  • 【NLP 40、文本生成任务】
  • 【AI大模型】搭建本地大模型GPT-NeoX:详细步骤及常见问题处理
  • 通往自主智能之路:探索自我成长的AI
  • 图像处理全流程指南(OpenCV 实现)
  • 2025-03-23 学习记录--C/C++-C语言 sprintf()实现将多个值按指定格式拼接成字符串
  • 国产达梦(DM)数据库的安装(Linux系统)
  • 设计模式:创建型、结构型、行为型
  • Linux 系统运行 Android 应用的几种方案
  • 为什么EasyExcel能处理大数据量而不内存溢出,EasyExcel原理
  • Linux__之__基于UDP的Socket编程网络通信
  • LLaMA-Factory微调大模型
  • 网络爬虫-4:jsonpath+实战
  • 一个轻量级的 SQLite ORM 工具包
  • 博柏利上财年营收下降17%,计划裁员1700人助推股价涨超18%
  • 海运港口股掀涨停潮!回应关税下调利好,有货代称美线舱位爆了
  • 多地警务新媒体整合:关停交警等系统账号,统一信息发布渠道
  • 周启鸣加盟同济大学,曾任香港浸会大学深圳研究院院长
  • 沈阳一超市疑借领养名义烹食流浪狗,当地市监局:已收到多起投诉
  • 万科:存续债券均正常付息兑付