自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

前端代碼質(zhì)量-圈復(fù)雜度原理和實踐

開發(fā) 前端
本文的主角 圈復(fù)雜度 重磅登場,本文將從圈復(fù)雜度原理出發(fā),介紹圈復(fù)雜度的計算方法、如何降低代碼的圈復(fù)雜度,如何獲取圈復(fù)雜度,以及圈復(fù)雜度在公司項目的實踐應(yīng)用。

[[279168]]

寫程序時時刻記著,這個將來要維護(hù)你寫的程序的人是一個有嚴(yán)重暴力傾向,并且知道你住在哪里的精神變態(tài)者。

1. 導(dǎo)讀

你們是否也有過下面的想法?

  •  重構(gòu)一個項目還不如新開發(fā)一個項目...
  •  這代碼是誰寫的,我真想...

你們的項目中是否也存在下面的問題?

  •  單個項目也越來越龐大,團(tuán)隊成員代碼風(fēng)格不一致,無法對整體的代碼質(zhì)量做全面的掌控
  •  沒有一個準(zhǔn)確的標(biāo)準(zhǔn)去衡量代 碼結(jié)構(gòu)復(fù)雜的程度,無法量化一個項目的代碼質(zhì)量
  •  重構(gòu)代碼后無法立即量化重構(gòu)后代碼質(zhì)量是否提升

針對上面的問題,本文的主角 圈復(fù)雜度 重磅登場,本文將從圈復(fù)雜度原理出發(fā),介紹圈復(fù)雜度的計算方法、如何降低代碼的圈復(fù)雜度,如何獲取圈復(fù)雜度,以及圈復(fù)雜度在公司項目的實踐應(yīng)用。

2. 圈復(fù)雜度

2.1 定義

圈復(fù)雜度 (Cyclomatic complexity) 是一種代碼復(fù)雜度的衡量標(biāo)準(zhǔn),也稱為條件復(fù)雜度或循環(huán)復(fù)雜度,它可以用來衡量一個模塊判定結(jié)構(gòu)的復(fù)雜程度,數(shù)量上表現(xiàn)為獨(dú)立現(xiàn)行路徑條數(shù),也可理解為覆蓋所有的可能情況最少使用的測試用例數(shù)。簡稱 CC 。其符號為 VG 或是 M 。

圈復(fù)雜度 在 1976 年由 Thomas J. McCabe, Sr. 提出。

圈復(fù)雜度大說明程序代碼的判斷邏輯復(fù)雜,可能質(zhì)量低且難于測試和維護(hù)。程序的可能錯誤和高的圈復(fù)雜度有著很大關(guān)系。

2.2 衡量標(biāo)準(zhǔn)

代碼復(fù)雜度低,代碼不一定好,但代碼復(fù)雜度高,代碼一定不好。

圈復(fù)雜度 代碼狀況 可測性 維護(hù)成本
1 - 10 清晰、結(jié)構(gòu)化
10 - 20 復(fù)雜
20 - 30 非常復(fù)雜
>30 不可讀 不可測 非常高

3. 計算方法

3.1 控制流程圖

控制流程圖,是一個過程或程序的抽象表現(xiàn),是用在編譯器中的一個抽象數(shù)據(jù)結(jié)構(gòu),由編譯器在內(nèi)部維護(hù),代表了一個程序執(zhí)行過程中會遍歷到的所有路徑。它用圖的形式表示一個過程內(nèi)所有基本塊執(zhí)行的可能流向, 也能反映一個過程的實時執(zhí)行過程。

下面是一些常見的控制流程:

3.2 節(jié)點(diǎn)判定法

有一個簡單的計算方法,圈復(fù)雜度實際上就是等于判定節(jié)點(diǎn)的數(shù)量再加上1。向上面提到的:if else 、switch case 、 for循環(huán)、三元運(yùn)算符等等,都屬于一個判定節(jié)點(diǎn),例如下面的代碼:

 

  1. function testComplexity(*param*) {  
  2.     let result = 1 
  3.     if (param > 0) {  
  4.         result--;  
  5.     }  
  6.     for (let i = 0; i < 10; i++) {  
  7.         result += Math.random();  
  8.     }  
  9.     switch (parseInt(result)) {  
  10.         case 1:  
  11.             result += 20;  
  12.             break;  
  13.         case 2:  
  14.             result += 30;  
  15.             break;  
  16.         default:  
  17.             result += 10;  
  18.             break;  
  19.     }  
  20.     return result > 20 ? result : result;  

上面的代碼中一共有1個if語句,一個for循環(huán),兩個case語句,一個三元運(yùn)算符,所以代碼復(fù)雜度為 4+1+1=6。另外,需要注意的是 || 和 && 語句也會被算作一個判定節(jié)點(diǎn),例如下面代碼的代碼復(fù)雜為3:

 

  1. function testComplexity(*param*) {  
  2.     let result = 1 
  3.     if (param > 0 && param < 10) {  
  4.         result--;  
  5.     }  
  6.     return result;  

3.3 點(diǎn)邊計算法

 

  1. M = E − N + 2P 
  •  E:控制流圖中邊的數(shù)量
  •  N:控制流圖中的節(jié)點(diǎn)數(shù)量
  •  P:獨(dú)立組件的數(shù)目

前兩個,邊和節(jié)點(diǎn)都是數(shù)據(jù)結(jié)構(gòu)圖中最基本的概念:

P代表圖中獨(dú)立組件的數(shù)目,獨(dú)立組件是什么意思呢?來看看下面兩個圖,左側(cè)為連通圖,右側(cè)為非連通圖:

  •  連通圖:對于圖中任意兩個頂點(diǎn)都是連通的

一個連通圖即為圖中的一個獨(dú)立組件,所以左側(cè)圖中獨(dú)立組件的數(shù)目為1,右側(cè)則有兩個獨(dú)立組件。

對于我們的代碼轉(zhuǎn)化而來的控制流程圖,正常情況下所有節(jié)點(diǎn)都應(yīng)該是連通的,除非你在某些節(jié)點(diǎn)之前執(zhí)行了 return,顯然這樣的代碼是錯誤的。所以每個程序流程圖的獨(dú)立組件的數(shù)目都為1,所以上面的公式還可以簡化為 M = E − N + 2 。

4. 降低代碼的圈復(fù)雜度

我們可以通過一些代碼重構(gòu)手段來降低代碼的圈復(fù)雜度。

重構(gòu)需謹(jǐn)慎,示例代碼僅僅代表一種思想,實際代碼要遠(yuǎn)遠(yuǎn)比示例代碼復(fù)雜的多。

4.1 抽象配置

通過抽象配置將復(fù)雜的邏輯判斷進(jìn)行簡化。例如下面的代碼,根據(jù)用戶的選擇項執(zhí)行相應(yīng)的操作,重構(gòu)后降低了代碼復(fù)雜度,并且如果之后有新的選項,直接加入配置即可,而不需要再去深入代碼邏輯中進(jìn)行改動:

4.2 單一職責(zé) - 提煉函數(shù)

單一職責(zé)原則(SRP):每個類都應(yīng)該有一個單一的功能,一個類應(yīng)該只有一個發(fā)生變化的原因。

在 JavaScript 中,需要用到的類的場景并不太多,單一職責(zé)原則則是更多地運(yùn)用在對象或者方法級別上面。

函數(shù)應(yīng)該做一件事,做好這件事,只做這一件事。 — 代碼整潔之道

關(guān)鍵是如何定義這 “一件事” ,如何將代碼中的邏輯進(jìn)行抽象,有效的提煉函數(shù)有利于降低代碼復(fù)雜度和降低維護(hù)成本。

4.3 使用 break 和 return 代替控制標(biāo)記

我們經(jīng)常會使用一個控制標(biāo)記來標(biāo)示當(dāng)前程序運(yùn)行到某一狀態(tài),很多場景下,使用 break 和 return 可以代替這些標(biāo)記并降低代碼復(fù)雜度。

4.4 用函數(shù)取代參數(shù)

setField 和 getField 函數(shù)就是典型的函數(shù)取代參數(shù),如果么有 setField、getField 函數(shù),我們可能需要一個很復(fù)雜的 setValue、getValue 來完成屬性賦值操作:

4.5 簡化條件判斷 - 逆向條件

某些復(fù)雜的條件判斷可能逆向思考后會變的更簡單。

4.6 簡化條件判斷 -合并條件

將復(fù)雜冗余的條件判斷進(jìn)行合并。

4.7 簡化條件判斷 - 提取條件

將復(fù)雜難懂的條件進(jìn)行語義化提取。

5. 圈復(fù)雜度檢測方法

5.1 eslint規(guī)則

eslint提供了檢測代碼圈復(fù)雜度的rules:

我們將開啟 rules 中的 complexity 規(guī)則,并將圈復(fù)雜度大于 0 的代碼的 rule severity 設(shè)置為 warn 或 error 。   

  1. rules: {  
  2.        complexity: [  
  3.            'warn',  
  4.            { max: 0 }  
  5.        ]  
  6.    } 

這樣 eslint 就會自動檢測出所有函數(shù)的代碼復(fù)雜度,并輸出一個類似下面的 message。

 

  1. Method 'testFunc' has a complexity of 12. Maximum allowed is 0  
  2. Async function has a complexity of 6. Maximum allowed is 0.  
  3. ... 

5.2 CLIEngine

我們可以借助 eslint 的 CLIEngine ,在本地使用自定義的 eslint 規(guī)則掃描代碼,并獲取掃描結(jié)果輸出。

初始化 CLIEngine :

 

  1. const eslint = require('eslint');  
  2. const { CLIEngine } = eslint;  
  3. const cli = new CLIEngine({  
  4.     parserOptions: {  
  5.         ecmaVersion: 2018,  
  6.     },  
  7.     rules: {  
  8.         complexity: [  
  9.             'error',  
  10.             { max: 0 }  
  11.         ]  
  12.     }  
  13. }); 

使用 executeOnFiles 對指定文件進(jìn)行掃描,并獲取結(jié)果,過濾出所有 complexity 的 message 信息。

 

  1. const reports = cli.executeOnFiles(['.']).results;  
  2. for (let i = 0; i < reports.length; i++) {  
  3.     const { messages } = reports[i];  
  4.     for (let j = 0; j < messages.length; j++) {  
  5.         const { message, ruleId } = messages[j];  
  6.         if (ruleId === 'complexity') {  
  7.              console.log(message);  
  8.         }  
  9.     }  

5.3 提取message

通過 eslint 的檢測結(jié)果將有用的信息提取出來,先測試幾個不同類型的函數(shù),看看 eslint 的檢測結(jié)果:

 

  1. function func1() {  
  2.     console.log(1);  
  3.  
  4. const func2 = () => {  
  5.     console.log(2);  
  6. };  
  7. class TestClass {  
  8.     func3() {  
  9.         console.log(3);  
  10.     }  
  11.  
  12. async function func4() {  
  13.     console.log(1);  

執(zhí)行結(jié)果:

 

  1. Function 'func1' has a complexity of 1. Maximum allowed is 0.  
  2. Arrow function has a complexity of 1. Maximum allowed is 0.  
  3. Method 'func3' has a complexity of 1. Maximum allowed is 0.  
  4. Async function 'func4' has a complexity of 1. Maximum allowed is 0. 

可以發(fā)現(xiàn),除了前面的函數(shù)類型,以及后面的復(fù)雜度,其他都是相同的。

函數(shù)類型:

  •  Function :普通函數(shù)
  •  Arrow function : 箭頭函數(shù)
  •  Method : 類方法
  •  Async function : 異步函數(shù)

截取方法類型:

 

  1. const REG_FUNC_TYPE = /^(Method |Async function |Arrow function |Function )/g;  
  2. function getFunctionType(message) {  
  3.     let hasFuncType = REG_FUNC_TYPE.test(message);  
  4.     return hasFuncType && RegExp.$1;  

將有用的部分提取出來:

 

  1. const MESSAGE_PREFIX = 'Maximum allowed is 1.' 
  2. const MESSAGE_SUFFIX = 'has a complexity of ' 
  3. function getMain(message) {  
  4.     return message.replace(MESSAGE_PREFIX, '').replace(MESSAGE_SUFFIX, '');  

提取方法名稱:

 

  1. function getFunctionName(message) {  
  2.     const main = getMain(message);  
  3.     let test = /'([a-zA-Z0-9_$]+)'/g.test(main);  
  4.     return test ? RegExp.$1 : '*';  

截取代碼復(fù)雜度:

 

  1. function getComplexity(message) {  
  2.     const main = getMain(message);  
  3.     (/(\d+)\./g).test(main);  
  4.     return +RegExp.$1;  

除了 message ,還有其他的有用信息:

  •  函數(shù)位置:獲取 messages 中的 line 、column 即函數(shù)的行、列位置
  •  當(dāng)前文件名稱:reports 結(jié)果中可以獲取當(dāng)前掃描文件的絕對路徑 filePath ,通過下面的操作獲取真實文件名:

 

  1. filePath.replace(process.cwd(), '').trim() 
  •  復(fù)雜度等級,根據(jù)函數(shù)的復(fù)雜度等級給出重構(gòu)建議:
圈復(fù)雜度 代碼狀況 可測性 維護(hù)成本
1 - 10 清晰、結(jié)構(gòu)化
10 - 20 復(fù)雜
20 - 30 非常復(fù)雜
>30 不可讀 不可測 非常高
圈復(fù)雜度 代碼狀況
1 - 10 無需重構(gòu)
11 - 15 建議重構(gòu)
>15 強(qiáng)烈建議重構(gòu)

6.架構(gòu)設(shè)計

將代碼復(fù)雜度檢測封裝成基礎(chǔ)包,根據(jù)自定義配置輸出檢測數(shù)據(jù),供其他應(yīng)用調(diào)用。

上面的展示了使用 eslint 獲取代碼復(fù)雜度的思路,下面我們要把它封裝為一個通用的工具,考慮到工具可能在不同場景下使用,例如:網(wǎng)頁版的分析報告、cli版的命令行工具,我們把通用的能力抽象出來以 npm包 的形式供其他應(yīng)用使用。

在計算項目代碼復(fù)雜度之前,我們首先要具備一項基礎(chǔ)能力,代碼掃描,即我們要知道我們要對項目里的哪些文件做分析,首先 eslint 是具備這樣的能力的,我們也可以直接用 glob 來遍歷文件。但是他們都有一個缺點(diǎn),就是 ignore 規(guī)則是不同的,這對于用戶來講是有一定學(xué)習(xí)成本的,因此我這里把手動封裝代碼掃描,使用通用的 npm ignore 規(guī)則,這樣代碼掃描就可以直接使用 .gitignore這樣的配置文件。另外,代碼掃描作為代碼分析的基礎(chǔ)能力,其他代碼分析也是可以公用的。

  •  基礎(chǔ)能力
    •   代碼掃描能力
    •   復(fù)雜度檢測能力
    •   ...
  •  應(yīng)用
    •   命令行工具
    •   代碼分析報告
    •   ...

7. 基礎(chǔ)能力 - 代碼掃描

本文涉及的 npm 包和 cli命令源碼均可在我的開源項目 awesome-cli中查看。

awesome-cli 是我新建的一個開源項目:有趣又實用的命令行工具,后面會持續(xù)維護(hù),敬請關(guān)注,歡迎 star。

代碼掃描(c-scan)源碼:https://github.com/ConardLi/a...

代碼掃描是代碼分析的底層能力,它主要幫助我們拿到我們想要的文件路徑,應(yīng)該滿足我們以下兩個需求:

  •  我要得到什么類型的文件
  •  我不想要哪些文件

7.1 使用

 

  1. npm i c-scan --save  
  2. const scan = require('c-scan');  
  3. scan({  
  4.     extensions:'**/*.js',  
  5.     rootPath:'src',  
  6.     defalutIgnore:'true',  
  7.     ignoreRules:[],  
  8.     ignoreFileName:'.gitignore'  
  9. }); 

7.2 返回值

符合規(guī)則的文件路徑數(shù)組:

7.3 參數(shù)

  •  extensions
    •   掃描文件擴(kuò)展名
    •   默認(rèn)值:**/*.js
  •  rootPath
    •   掃描文件路徑
    •   默認(rèn)值:.
  •  defalutIgnore
    •   是否開啟默認(rèn)忽略(glob規(guī)則)
    •   glob ignore規(guī)則為內(nèi)部使用,為了統(tǒng)一ignore規(guī)則,自定義規(guī)則使用gitignore規(guī)則
    •   默認(rèn)值:true
    •   默認(rèn)開啟的 glob ignore 規(guī)則:

 

  1. const DEFAULT_IGNORE_PATTERNS = [  
  2.     'node_modules/**',  
  3.     'build/**',  
  4.     'dist/**',  
  5.     'output/**',  
  6.     'common_build/**'  
  7. ]; 
  •  ignoreRules
    •   自定義忽略規(guī)則(gitignore規(guī)則)
    •   默認(rèn)值:[]
  •  ignoreFileName
    •   自定義忽略規(guī)則配置文件路徑(gitignore規(guī)則)
    •   默認(rèn)值:.gitignore
    •   指定為null則不啟用ignore配置文件

7.4 核心實現(xiàn)

基于 glob ,自定義 ignore 規(guī)則進(jìn)行二次封裝。

 

  1. /**  
  2.  * 獲取glob掃描的文件列表  
  3.  * @param {*} rootPath 跟路徑  
  4.  * @param {*} extensions 擴(kuò)展  
  5.  * @param {*} defalutIgnore 是否開啟默認(rèn)忽略  
  6.  */  
  7. function getGlobScan(rootPath, extensions, defalutIgnore) {  
  8.     return new Promise(resolve => {  
  9.         glob(`${rootPath}${extensions}`,  
  10.             { dot: true, ignore: defalutIgnore ? DEFAULT_IGNORE_PATTERNS : [] },  
  11.             (err, files) => {  
  12.                 if (err) {  
  13.                     console.log(err);  
  14.                     process.exit(1);  
  15.                 }  
  16.                 resolve(files);  
  17.             });  
  18.     });  
  19.  
  20. /**  
  21.  * 加載ignore配置文件,并處理成數(shù)組  
  22.  * @param {*} ignoreFileName   
  23.  */  
  24. async function loadIgnorePatterns(ignoreFileName) { 
  25.      const ignorePath = path.resolve(process.cwd(), ignoreFileName);  
  26.     try {  
  27.         const ignores = fs.readFileSync(ignorePath, 'utf8');  
  28.         return ignores.split(/[\n\r]|\n\r/).filter(pattern => Boolean(pattern));  
  29.     } catch (e) {  
  30.         return [];  
  31.     }  
  32.  
  33. /**  
  34.  * 根據(jù)ignore配置過濾文件列表  
  35.  * @param {*} files   
  36.  * @param {*} ignorePatterns   
  37.  * @param {*} cwd   
  38.  */  
  39. function filterFilesByIgnore(files, ignorePatterns, ignoreRules, cwd = process.cwd()) {  
  40.     const ig = ignore().add([...ignorePatterns, ...ignoreRules]);  
  41.     const filtered = files  
  42.         .map(raw => (path.isAbsolute(raw) ? raw : path.resolve(cwd, raw)))  
  43.         .map(raw => path.relative(cwd, raw))  
  44.         .filter(filePath => !ig.ignores(filePath))  
  45.         .map(raw => path.resolve(cwd, raw));  
  46.     return filtered;  

8. 基礎(chǔ)能力 - 代碼復(fù)雜度檢測

代碼復(fù)雜度檢測(c-complexity)源碼:https://github.com/ConardLi/a...

代碼檢測基礎(chǔ)包應(yīng)該具備以下幾個能力:

  •  自定義掃描文件夾和類型
  •  支持忽略文件
  •  定義最小提醒代碼復(fù)雜度

8.1 使用

 

  1. npm i c-complexity --save  
  2. const cc = require('c-complexity');  
  3. cc({},10); 

8.2 返回值

  •  fileCount:文件數(shù)量
  •  funcCount:函數(shù)數(shù)量
  •  result:詳細(xì)結(jié)果
    •   funcType:函數(shù)類型
    •   funcName;函數(shù)名稱
    •   position:詳細(xì)位置(行列號)
    •   fileName:文件相對路徑
    •   complexity:代碼復(fù)雜度
    •   advice:重構(gòu)建議

8.3 參數(shù)

  •  scanParam
    •   繼承自上面代碼掃描的參數(shù)
  •  min
    •   最小提醒代碼復(fù)雜度,默認(rèn)為1

9. 應(yīng)用 - 代碼復(fù)雜度檢測工具

代碼復(fù)雜度檢測(conard cc)源碼:https://github.com/ConardLi/a...

9.1 指定最小提醒復(fù)雜度

可以觸發(fā)提醒的最小復(fù)雜度。

  •  默認(rèn)為 10
  •  通過命令 conard cc --min=5 自定義

9.2 指定掃描參數(shù)

自定義掃描規(guī)則

  •  掃描參數(shù)繼承自上面的 scan param
  •  例如: conard cc --defalutIgnore=false

10. 應(yīng)用 - 代碼復(fù)雜度報告

部分截圖來源于我們內(nèi)部的項目質(zhì)量監(jiān)控平臺,圈復(fù)雜度作為一項重要的指標(biāo),對于衡量項目代碼質(zhì)量起著至關(guān)重要的作用。

代碼復(fù)雜復(fù)雜度變化趨勢

定時任務(wù)爬取代碼每日的代碼復(fù)雜度、代碼行數(shù)、函數(shù)個數(shù),通過每日數(shù)據(jù)繪制代碼復(fù)雜度和代碼行數(shù)變化趨勢折線圖。

通過 [ 復(fù)雜度 / 代碼行數(shù) ] 或 [ 復(fù)雜度 / 函數(shù)個數(shù) ] 的變化趨勢,判斷項目發(fā)展是否健康。

  •     比值若一直在上漲,說明你的代碼在變得越來越難以理解。這不僅使我們面臨意外的功能交互和缺陷的風(fēng)險,由于我們在具有或多或少相關(guān)功能的模塊中所面臨的過多認(rèn)知負(fù)擔(dān),也很難重用代碼并進(jìn)行修改和測試。(下圖1)
  •     若比值在某個階段發(fā)生突變,說明這段期間迭代質(zhì)量很差。(下圖2)   

  • 復(fù)雜度曲線圖可以很快的幫你更早的發(fā)現(xiàn)上面這兩個問題,發(fā)現(xiàn)它們后,你可能需要重構(gòu)代碼。復(fù)雜性趨勢對于跟蹤你的代碼重構(gòu)也很有用。復(fù)雜性趨勢的下降趨勢是一個好兆頭。這要么意味著您的代碼變得更簡單(例如,把 if-else 被重構(gòu)為多態(tài)解決方案),要么代碼更少(將不相關(guān)的部分提取到了其他模塊中)。(下圖3)
  •  代碼重構(gòu)后,你還需要繼續(xù)探索復(fù)雜度變化趨勢。經(jīng)常發(fā)生的事情是,我們花費(fèi)大量的時間和精力來重構(gòu),無法解決根本原因,很快復(fù)雜度又滑回了原處。(下圖4)你可能覺得這是個例,但是有研究標(biāo)明,在分析了數(shù)百個代碼庫后,發(fā)現(xiàn)出現(xiàn)這種情況的頻率很高。因此,時刻觀察代碼復(fù)雜度變化趨勢是有必要的。

代碼復(fù)雜度文件分布

統(tǒng)計各復(fù)雜度分布的函數(shù)數(shù)量。

代碼復(fù)雜度文件詳情

計算每個函數(shù)的代碼復(fù)雜度,從高到低依次列出高復(fù)雜度的文件分布,并給出重構(gòu)建議。

實際開發(fā)中并不一定所有的代碼都需要被分析,例如打包產(chǎn)物、靜態(tài)資源文件等等,這些文件往往會誤導(dǎo)我們的分析結(jié)果,現(xiàn)在分析工具會默認(rèn)忽略一些規(guī)則,例如:.gitignore文件、static目錄等等,實際這些規(guī)則還需要根據(jù)實際項目的情況去不斷完善,使分析結(jié)果變得更準(zhǔn)確。

 

 

責(zé)任編輯:龐桂玉 來源: segmentfault
相關(guān)推薦

2020-12-30 09:20:27

代碼

2022-08-16 09:04:23

代碼圈圈復(fù)雜度節(jié)點(diǎn)

2023-03-03 08:43:08

代碼重構(gòu)系統(tǒng)

2023-10-05 11:08:53

2024-04-25 08:33:25

算法時間復(fù)雜度空間復(fù)雜度

2013-08-01 13:18:41

代碼

2021-01-05 10:41:42

算法時間空間

2022-08-25 11:00:19

編程系統(tǒng)

2021-10-15 09:43:12

希爾排序復(fù)雜度

2009-07-09 10:45:16

C#基本概念復(fù)雜度遞歸與接口

2022-05-28 16:08:04

前端

2018-12-18 10:11:37

軟件復(fù)雜度軟件系統(tǒng)軟件開發(fā)

2015-10-13 09:43:43

復(fù)雜度核心

2019-12-24 09:46:00

Linux設(shè)置密碼

2024-07-30 10:55:25

2020-02-06 13:59:48

javascript算法復(fù)雜度

2021-04-25 14:29:02

數(shù)據(jù)結(jié)構(gòu)動態(tài)數(shù)組時間復(fù)雜度

2011-04-12 16:40:40

C++復(fù)雜度

2021-03-10 07:20:43

if-else靜態(tài)代碼

2014-07-01 15:49:33

數(shù)據(jù)結(jié)構(gòu)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號