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

【数据结构与算法篇】之时间复杂度与空间复杂度


❤️博客主页: 小镇敲码人
🍏 欢迎关注:👍点赞 👂🏽留言 😍收藏
🌞友友们暑假快乐,好久不见呀!!!💖💖💖
🍉 有人曾经问过我这样一个问题,“人终其一身,执着追求的东西究竟是什么?”我是这样回答的,”我们终其一生都在寻找着那个和我们灵魂极其契合的人,可最后才发现,那个人只有也只能是自己,学会在无聊时消遣自己是非常重要的“,所以无论目前屏幕上的你深处怎样的绝境之中,请不要放弃自己,好好的爱自己就是度过绝境的最佳武器!!!😸😸😸

一、时间复杂度

1.1时间复杂度的定义

         在计算机与科学中,算法的时间复杂度(time complexity)是一个函数,它定性的描述算法的运行时间,表示一个程序来回执行的次数,但不定量。这个函数的自变量是算法输入值的字符串的长度,即 N N N。时间复杂度常用大O符号表述,只包括那个幂次最高的项数。使用这种方式时,时间复杂度可被称为是渐近的,也就是 lim ⁡ N → ∞ O ( N ) \lim\limits_{N\rarr\infin}O(N) NlimO(N)时的情况。例如,如果一个算法对于任何大小为 N N N的输入,它至多需要 5 N 3 N^3 N3 + 3 N 3N 3N 的时间运行完毕,那么它的渐近时间复杂度是 O ( N 3 ) O(N^3) O(N3)

  • 注意:时间复杂度不计算时间,只计算程序的执行次数。

常见的时间复杂度包括:

  • 常数时间复杂度( O ( 1 ) ) O(1)) O(1)):算法的执行时间与输入规模无关,常数级别的执行时间。
  • 线性时间复杂度( O ( N ) O(N) O(N)):算法的执行时间与输入规模线性相关,随着输入规模的增加而线性增长。
  • 对数时间复杂度( O ( l o g N ) O(log N) O(logN)):算法的执行时间与输入规模的对数关系,通常是二分查找等算法的时间复杂度。
  • 平方时间复杂度( O ( N 2 ) O(N^2) O(N2)):算法的执行时间与输入规模的平方相关,通常是嵌套循环等算法的时间复杂度。
  • 线性对数时间复杂度 ( O ( N ∗ l o g N ) ) (O(N*log N)) (O(NlogN)) :它表示随着输入规模 N 的增加,算法的执行时间以 N 乘以 log N 的速度增长,线性对数时间复杂度常常出现在一些高效的排序算法(如快速排序和归并排序)以及一些分治算法中。

注意:嵌套循环的算法时间复杂度不一定是 O ( N 2 ) O(N^2) O(N2)

通过分析算法的时间复杂度,我们可以更好地理解算法的性能特点,并进行算法的选择和优化。

1.2 常见的时间复杂度的计算

1.2.1 常数时间复杂度( O ( 1 ) ) O(1)) O(1))

    int main(){int a = 0;a += 1;printf("%d",&a);return 0;}

这段代码执行了四次操作。我们逐行分析代码的执行过程:

1.int a = 0;:这行代码初始化了整数变量a,将其赋值为 0。
2. a += 1;:这行代码将变量 a的值增加 1,现在 a 的值为 1。
3.printf("%d", &a);:这行代码使用 printf 函数打印变量 a 的值的内存地址,格式化输出为十进制整数。
4. return 0;:在 main 函数的结尾,使用 return 语句将返回值设为 0。
因此,代码执行了四次操作。

  • 若对于一个算法 T ( n ) T(n) T(n)的上界与输入大小无关,则称其具有常数时间,记作O(1)时间,故而这个算法的时间复杂度为 O ( 1 ) O(1) O(1)

1.2.2 线性时间复杂度( O ( N ) O(N) O(N)

int main()
{int n = 0;scanf("%d",&n);int sum = 1;for(int i = 1;i < n;;i++)sum *= i;printf("%d",sum);return 0;
}

这段代码的执行过程如下:

  1. int n = 0;:这行代码声明了一个整数变量 n,并将其初始化为 0。
  2. scanf("%d",&n);:这行代码使用 scanf 函数从标准输入中读取一个整数,并将其赋值给变量 n。
  3. int sum = 1;:这行代码声明了一个整数变量 sum,并将其初始化为 1。
  4. for(int i = 1;i < n;i++):这行代码开始一个 for 循环,循环变量 i 的初始值为 1,循环条件为 i < n。
  5. sum *= i;:这行代码将变量 sum 与变量 i 相乘,并将结果赋值给 sum。
  6. printf("%d",sum);:这行代码使用 printf 函数打印变量 sum 的值。
  7. return 0;:在 main 函数的结尾,使用 return 语句将返回值设为 0。
  8. for循环中,变量 i 的初始值为 1,每次循环 i 的值递增 1,直到达到 n 的值。因此,for 循环的执行次数为 n - 1。在循环中,执行了一次乘法操作 sum *= i,共执行了 n - 1 次。
  • 故而程序的执行次数为: T ( N ) = 5 + 2 ∗ ( N − 1 ) = 2 N + 3 , T(N)=5+2*(N-1)=2N+3, TN=5+2(N1)=2N+3, lim ⁡ N → ∞ T ( N ) = O ( N ) \lim\limits_{N\rarr\infin}T(N)=O(N) NlimT(N)=O(N)

1.2.3 对数时间复杂度( O ( l o g N ) O(log N) O(logN)

#include <stdio.h>void logarithmicAlgorithm(int n) {int i = 1;while (i < n) {printf("%d\n", i);i *= 2;  // 对数时间复杂度的关键步骤}
}int main() {int n = 16;logarithmicAlgorithm(n);return 0;
}

这段代码的执行过程如下:

  1. main函数中,声明一个整数变量 n 并赋值为 16。
  2. 调用logarithmicAlgorithm()函数,并将变量 n 作为参数传递给该函数。
  3. 进入logarithmicAlgorithm函数。
  4. logarithmicAlgorithm 函数中,声明一个整数变量 i 并赋值为 1。
  5. 进入 while 循环,检查条件 i < n 是否满足。由于此时 i 的初始值为 1,且 1 小于 16,条件成立,进入循环体。
  6. 在循环体内,使用printf 函数打印变量 i 的值。
  7. 执行i *= 2,将 i 的值乘以 2。
  8. 回到循环的开头,再次检查条件 i < n。如果条件仍然成立,继续执行循环体;如果条件不成立,跳出循环。
  9. 当 i 的值达到或超过 n(即 16)时,条件 i < n 不再满足,跳出循环。
  10. 退出logarithmicAlgorithm函数。
  11. 回到 main函数,继续执行后续的代码。执行return 0; 语句,结束程序。
  • while循环的判断执行次数为x, 2 = N , ⇒ 2 x − 1 = l o g 2 N + 1 2 = N,\rArr 2^{x-1} = log_2N+1 2=N,2x1=log2N+1
    while循环内的执行次数为 2 ∗ l o g 2 N 2*log_2N 2log2N
  • 故而程序的执行次数为: T ( N ) = 7 + 3 ∗ l o g 2 N , T(N)= 7+3 * log_2N, TN=7+3log2N, lim ⁡ N → ∞ T ( N ) = O ( l o g N ) \lim\limits_{N\rarr\infin}T(N)=O(logN) NlimT(N)=O(logN)

1.2.4 平方时间复杂度( O ( N 2 ) O(N^2) O(N2)

#include <stdio.h>void quadraticAlgorithm(int n) {for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {printf("(%d, %d)\n", i, j);}}
}int main() {int n = 3;quadraticAlgorithm(n);return 0;
}

这段代码的执行过程如下:

  1. main函数中声明一个整数变量n,并赋值为3。
  2. 调用quadraticAlgorithm()函数,并把变量n作为形参传递给函数。
  3. 进入函数quadraticAlgorithm()
  4. 外部for循环的判断执行次数为n+1,外部循环循环一次内部for循环判断的执行次数为n+1,printf语句的执行次数为n。

故而外部for循环的执行次数就为n+1,内部for循环的总执行次数就为n*(n+1),内部for循环的printf语句的总执行次数为 n 2 n^2 n2

  1. 退出函数quadraticAlgorithm()
  2. 继续执行main函数中的return 0;语句,结束程序。
  • 故程序的执行次数为: T ( N ) = 1 + 1 + 1 + ( N + 1 ) + N ∗ ( N + 1 ) + N ∗ N + 1 + 1 = 2 N 2 + 2 N + 6 , T(N) =1+1+1+(N+1)+N*(N+1)+N*N+1+1=2N^2+2N+6, T(N)=1+1+1+(N+1)+N(N+1)+NN+1+1=2N2+2N+6, lim ⁡ N → ∞ T ( N ) = O ( N 2 ) \lim\limits_{N\rarr\infin}T(N)=O(N^2) NlimT(N)=O(N2)

1.3 常见的函数的时间复杂度


以下图片整理了一些常用的时间复杂度类。表中, p o l y ( x ) = x O ( 1 ) poly(x) = xO(1) poly(x)=xO(1),也就是 x 的多项式。也可以访问网页版点击此处跳转
在这里插入图片描述在这里插入图片描述

二、空间复杂度

2.1 空间复杂度的定义

      在计算机科学中,一个算法或程序的空间复杂度定性地描述该算法或程序运行所需要的存储空间大小。空间复杂度是相应计算问题的输入值的长度的函数,它表示一个算法完全执行所需要的存储空间大小。和时间复杂度类似,空间复杂度通常也使用大O记号来渐进地表示。例如: O ( n ) 、 O ( n α ) 、 O ( n ∗ l o g n ) 、 O ( 2 n ) O(n)、{\displaystyle O(n^{\alpha })}、O(n*logn)、O(2^n) O(n)O(nα)O(nlogn)O(2n)
      空间复杂度用于评估算法在执行过程中所需的额外空间或内存的量级或增长趋势。它主要关注算法在运行过程中所使用的额外存储空间,包括算法使用的数据结构、临时变量、递归调用等。

  • 注意:空间复杂度不计算空间,只计算变量的个数

常见的空间复杂度包括:

  • 常数空间复杂度 ( O ( 1 ) ) (O(1)) (O(1)):算法使用固定的额外空间,不随输入的增加而变化。
  • 线性空间复杂度 ( O ( n ) ) (O(n)) (O(n)):算法使用的额外空间与输入规模成线性关系。
  • 平方空间复杂度 ( O ( n 2 ) ) (O(n^2)) (O(n2)):算法使用的额外空间与输入规模成平方关系。
  • 对数空间复杂度 ( O ( l o g n ) ) (O(logn)) (O(logn)):算法使用的额外空间与输入规模成对数关系。

2.2 常见的空间复杂度的计算

  • 就是去算变量和函数调用开辟的栈帧的个数之和

2.2.1 常数空间复杂度 ( O ( 1 ) ) (O(1)) (O(1))

void printHello() {printf("Hello, world!\n");
}
  • 函数调用一次,开辟一个栈帧,空间复杂度为 O ( 1 ) O(1) O(1)

2.2.2 线性空间复杂度 ( O ( n ) ) (O(n)) (O(n))

void printNumbers(int n) {int *arr =(int*)malloc(n * sizeof(int));// 执行其他操作...free(arr);
}
  • 开辟了一个动态数组,变量的个数与 n n n成正比,取极限,和时间复杂度一样,常数可以忽略,空间复杂度为 O ( n ) O(n) O(n)

2.2.3 平方空间复杂度 ( O ( n 2 ) ) (O(n^2)) (O(n2))

void printPairs(int n) {int **matrix = (int**)malloc(n * sizeof(int *));for (int i = 0; i < n; i++) {matrix[i] = (int*)malloc(n * sizeof(int));}// 执行其他操作...for (int i = 0; i < n; i++) {free(matrix[i]);}free(matrix);
}
  • 开辟了一个二维的动态数组,变量的个数与 n 2 n^2 n2成正比,取极限,忽略常数,空间复杂度为 O ( n 2 ) O(n^2) O(n2)

2.2.4 对数空间复杂度 ( O ( l o g n ) ) (O(logn)) (O(logn))

// 递归二分查找函数
int binarySearch(int arr[], int low, int high, int target) {if (low <= high) {int mid = low + (high - low) / 2;if (arr[mid] == target) {return mid;} else if (arr[mid] < target) {return binarySearch(arr, mid + 1, high, target);} else {return binarySearch(arr, low, mid - 1, target);}}return -1; // 如果找不到目标元素,返回-1
}

    数组是提前排好序的递增数组,假设同时存在的栈帧最多为x个,因为只有找不到或者找到了target函数才会返回,此时区间长度要么是1,要么没找到不构成区间了,函数调用产生的栈帧才会开始销毁,有 2 x = n → x = l o g 2 n 2^x=n\to x=log_2n 2x=nx=log2n,也就是最多开辟了 l o g 2 n log_2n log2n个栈帧。

  • 故取极限,空间复杂度 T ( N ) 为 T(N)为 T(N) lim ⁡ N → ∞ T ( N ) = O ( l o g N ) \lim\limits_{N\rarr\infin}T(N)=O(logN) NlimT(N)=O(logN)

相关文章:

【数据结构与算法篇】之时间复杂度与空间复杂度

【数据结构与算法篇】之时间复杂度与空间复杂度 一、时间复杂度1.1时间复杂度的定义1.2 常见的时间复杂度的计算1.2.1 常数时间复杂度&#xff08; O ( 1 ) ) O(1)) O(1))1.2.2 线性时间复杂度&#xff08; O ( N ) O(N) O(N)&#xff09;1.2.3 对数时间复杂度&#xff08; O (…...

硬件性能 - 网络瓶颈分析

简介 本文章主要通过Linux命令查看网络信息、判断是否出现网络瓶颈等简单分析方法。其他硬件性能分析如下&#xff1a; 1. 硬件性能 - CPU瓶颈分析 2. 硬件性能 - 掌握内存知识 3. 硬件性能 - 磁盘瓶颈分析 目录 1. 监控命令 sar 2. 带宽利用率 3. 网络延迟 4. 网络连接数 …...

stm32驱动MCP2515芯片,项目已通过测试

最近公司做一个项目&#xff0c;需要3路can通道&#xff0c;但是stm32看了很久&#xff0c;最多也就只有2个can&#xff0c;所以找到了一款MCP2515芯片&#xff0c;可以用spi驱动can。 已经实现了can的发送和接收&#xff0c;接收采用的是外部中断接收的方式。和单片机本身带的…...

Nginx部署前后端分离项目

dev.env.js解释 //此文件时开发环境配置文件 use strice//使用严格模式 const merge require(webpacl-merge)//合并对象 const prodEnv require(./prod.env)//导出 module.exports merge(prodEnv,{//合并两个配置文件对象并生成一个新的配置文件&#xff0c;如果合并的过程…...

pytorch多分类问题 CrossEntropyLoss()函数的输入size/shape不一致问题

在使用pytorch实现一个多分类任务的时候&#xff0c;许多多分类任务在训练过程中都会有如下的代码&#xff1a; criterion nn.CrossEntropyLoss() loss criterion(output, target) # output.size : [batch_size, class_num] # target.size : [batch_size]许多的初学者会卡在…...

硬盘或者U盘提示需要格式化的解决办法

插入硬盘之后提示&#xff1a; 使用驱动器 G:中的光盘之前需要将其格式化 是否要将其格式化? 如下图所示 顿时慌了啊&#xff0c;里面还有比较重要的东西呢&#xff0c;这一下子完蛋&#xff1f; 遇事找某宝&#xff0c;上面估计有这种技术服务。果然有这一类的技术服务&…...

Clip-Path

前言 借助clip-path,我们可以实现一些复杂的animation动画效果,我们先来简单概述一下它的特性,如MDN所描述的。 The clip-path CSS property creates a clipping region that sets what part of an element should be shown. Parts that are inside the region are shown, whi…...

Matlab绘图系列教程-Matlab 34 种绘图函数示例(下)

Matlab绘图系列教程&#xff1a;揭秘高质量科学图表的绘制与优化 文章目录 Matlab绘图系列教程&#xff1a;揭秘高质量科学图表的绘制与优化第一部分&#xff1a;入门指南1.1 简介关于本教程的目的与范围Matlab绘图在科学研究中的重要性 1.2 准备工作安装Matlab及其工具箱 1.3 …...

【Vue+Django】Training Management Platform Axios并发请求 - 20230703

需求陈述 由于API是特定单位/特定类别/特定教学方式的数据&#xff0c;故汇总数据需要循环请求不同单位/不同类别/不同教学方式。 技术要点 1.axios并发请求 2.JS for循环 3.Vue数组中出现 ob :Observer无法取值问题的解决方法 4.将数据转化为数组 5.一次请求所有数据后&…...

smart Spring:自定义注解、拦截器的使用(更新中...)

文章目录 〇、使用自定义注解的好处和工作原理一、如何使用自定义注解1.自定义一个注解2.在类、属性、方法上进行使用3.元注解 二、使用拦截器的好处和工作原理三、如何使用拦截器参考 本博客源码&#xff1a; 〇、使用自定义注解的好处和工作原理 自定义注解是Java语言提供的…...

php导出pdf

插件官网&#xff1a;TCPDF 博主用的是tp6框架 、tcpdf插件 composer require tecnickcom/tcpdf --ignore-platform-reqs 后面是忽略平台要求的参数 ---------------中文乱码start------------------ 关于中文乱码问题&#xff1a; 网上说的下载字体放入fonts 利用tools…...

【ECMAScript6_2】字符串

1、字符的Unicode表示法 ES6 加强了对 Unicode 的支持&#xff0c;允许采用\uxxxx形式表示一个字符&#xff0c;其中xxxx表示字符的 Unicode 码点。&#xff08;\u0000-\uFFFF&#xff09; 码点超过取值范围之后不能正确解读&#xff0c;但是只要给码点加上{}就可以正确解读。 …...

37.RocketMQ之Broker消息存储源码分析

highlight: arduino-light 消息存储文件 rocketMQ的消息持久化在我们在搭建集群时都特意指定的文件存储路径,进入指定的store目录下就可以看到。 下面介绍各文件含义 CommitLog 存储消息的元数据。produce发出的所有消息都会顺序存入到CommitLog文件当中。 CommitLog由多个文件…...

RabbitMq应用延时消息

一.建立绑定关系 package com.lx.mq.bind;import com.lx.constant.MonitorEventConst; import lombok.extern.slf4j.Slf4j; import org.springframework.amqp.core.*; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annota…...

【WEB自动化测试】- 浏览器操作方法

一、常用方法 1. maximize_window() 最大化窗口 (重点) 说明&#xff1a;如果能够在打开页面时&#xff0c;全屏显示页面&#xff0c;就能尽最大可能加载更多的页面&#xff0c;提高可定位性 2. set_window_size(width, height) 设置浏览器窗口的大小 (了解) 场景&#xff1…...

VSCode设置鼠标滚轮滑动设置字体大小

1&#xff1a;打开"文件->首选项->设置 2 :打开settings.json文件 英文版这里有个坑 一般点击我下图右上角那个{ } 就可以打开了 在 设置的json 文件中加入如下 “editor.mouseWheelZoom”: true { “editor.mouseWheelZoom”: true, “json.schemas”: [ ]}...

Spring MVC是什么?详解它的组件、请求流程及注解

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 作者会持续更新网络知识和python基础知识&#xff0c;期待你的关注 前言 本文将讲解Spring MVC是什么&#xff0c;它的优缺点与九大组件&#xff0c;以及它的请求流程与常用的注解。 目录 一、Spring MVC是什…...

基于Spring Boot的广告公司业务管理平台设计与实现(Java+spring boot+MySQL)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的广告公司业务管理平台设计与实现&#xff08;Javaspring bootMySQL&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 后端&#xff1a;Java springboot框架 …...

docker 基本命令安装流程

docker 基本命令安装流程 1.更新Ubuntu的apt源索引 $ sudo apt-get update2.安装包允许apt通过HTTPS使用仓库 $ sudo dpkg --configure -a $ sudo apt-get install apt-transport-https ca-certificates curl software-properties-common3.添加Docker官方GPG key $ curl -f…...

尚硅谷大数据Flink1.17实战教程-笔记02【Flink部署】

尚硅谷大数据技术-教程-学习路线-笔记汇总表【课程资料下载】视频地址&#xff1a;尚硅谷大数据Flink1.17实战教程从入门到精通_哔哩哔哩_bilibili 尚硅谷大数据Flink1.17实战教程-笔记01【Flink概述、Flink快速上手】尚硅谷大数据Flink1.17实战教程-笔记02【Flink部署】尚硅谷…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

Vue记事本应用实现教程

文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展&#xff1a;显示创建时间8. 功能扩展&#xff1a;记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互

物理引擎&#xff08;Physics Engine&#xff09; 物理引擎 是一种通过计算机模拟物理规律&#xff08;如力学、碰撞、重力、流体动力学等&#xff09;的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互&#xff0c;广泛应用于 游戏开发、动画制作、虚…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

k8s从入门到放弃之Ingress七层负载

k8s从入门到放弃之Ingress七层负载 在Kubernetes&#xff08;简称K8s&#xff09;中&#xff0c;Ingress是一个API对象&#xff0c;它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress&#xff0c;你可…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

GruntJS-前端自动化任务运行器从入门到实战

Grunt 完全指南&#xff1a;从入门到实战 一、Grunt 是什么&#xff1f; Grunt是一个基于 Node.js 的前端自动化任务运行器&#xff0c;主要用于自动化执行项目开发中重复性高的任务&#xff0c;例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...