数据结构和算法之时间复杂度
文章目錄
- 前言
- 1 時(shí)間復(fù)雜度
- 1.1 分析方法
- 1.2 常見時(shí)間復(fù)雜度
- 2.3 最壞時(shí)間復(fù)雜度、最好時(shí)間復(fù)雜度、平均時(shí)間復(fù)雜度
- 2.空間復(fù)雜度
前言
? ? ? ?學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)和算法,并不是為了死記硬背幾個(gè)知識點(diǎn)。我們的目的是建立時(shí)間復(fù)雜度、空間復(fù)雜度意識,寫出高質(zhì)量的代碼,能夠設(shè)計(jì)基礎(chǔ)架構(gòu),提升編程技能,訓(xùn)練邏輯思維,積攢人生經(jīng)驗(yàn),以此獲得工作回報(bào),實(shí)現(xiàn)你的價(jià)值,完善你的人生。
? ? ? ?掌握了數(shù)據(jù)結(jié)構(gòu)與算法,你看待問題的深度,解決問題的角度就會(huì)完全不一樣。因?yàn)檫@樣的你,就像是站在巨人的肩膀上,拿著生存利器行走世界。數(shù)據(jù)結(jié)構(gòu)與算法,會(huì)為你的編程之路,甚至人生之路打開一扇通往新世界的大門。
對數(shù)據(jù)結(jié)構(gòu)和算法,要學(xué)習(xí)它的來歷、自身的特點(diǎn)、適合解決的問題和實(shí)際的應(yīng)用場景。
最常用的、最基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)與算法:
10 個(gè)數(shù)據(jù)結(jié)構(gòu):數(shù)組、鏈表、棧、隊(duì)列、散列表、二叉樹、堆、跳表、圖、Trie 樹;
10 個(gè)算法:遞歸、排序、二分查找、搜索、哈希算法、貪心算法、分治算法、回溯算法、動(dòng)態(tài)規(guī)劃、字符串匹配算法。
我們需要一個(gè)不用具體測試就可以粗略地估計(jì)算法的執(zhí)行效率的方法。這就是時(shí)間、空間復(fù)雜度分析方法,即大 O 復(fù)雜度表示法。
1 時(shí)間復(fù)雜度
? ? ? ?所有代碼的執(zhí)行時(shí)間 T(n) 與每行代碼的執(zhí)行次數(shù) n 成正比,因此我們用T(n) = O(f(n))來表示代碼執(zhí)行時(shí)間,即大 O 時(shí)間復(fù)雜度表示法。
? ? ? ?大O 時(shí)間復(fù)雜度實(shí)際上并不具體表示代碼真正的執(zhí)行時(shí)間,而是表示代碼執(zhí)行時(shí)間隨數(shù)據(jù)規(guī)模增長的變化趨勢。n 很大時(shí),你可以把它想象成 10000、100000。而公式中的低階、常量、系數(shù)三部分并不左右增長趨勢,所以都可以忽略。我們只需要記錄一個(gè)最大量級就可以了。
1.1 分析方法
其中第 2、3 行代碼都是常量級的執(zhí)行時(shí)間,與 n 的大小無關(guān),所以對于復(fù)雜度并沒有影響。循環(huán)執(zhí)行次數(shù)最多的是第 4、5 行代碼,所以這塊代碼要重點(diǎn)分析。前面我們也講過,這兩行代碼被執(zhí)行了 n 次,所以總的時(shí)間復(fù)雜度就是 O(n)。
? ? ? ?這個(gè)代碼分為三部分,分別是求 sum_1、sum_2、sum_3。我們可以分別分析每一部分的時(shí)間復(fù)雜度,然后把它們放到一塊兒,再取一個(gè)量級最大的作為整段代碼的復(fù)雜度。
? ? ? ?第一段的時(shí)間復(fù)雜度是多少呢?這段代碼循環(huán)執(zhí)行了 100 次,所以是一個(gè)常量的執(zhí)行時(shí)間,跟 n 的規(guī)模無關(guān)。這里再強(qiáng)調(diào)一下,即便這段代碼循環(huán) 10000 次、100000 次,只要是一個(gè)已知的數(shù),跟 n 無關(guān),照樣也是常量級的執(zhí)行時(shí)間。當(dāng) n 無限大的時(shí)候,就可以忽略。盡管對代碼的執(zhí)行時(shí)間會(huì)有很大影響,但是回到時(shí)間復(fù)雜度的概念來說,它表示的是一個(gè)算法執(zhí)行效率與數(shù)據(jù)規(guī)模增長的變化趨勢,所以不管常量的執(zhí)行時(shí)間多大,我們都可以忽略掉。因?yàn)樗旧韺υ鲩L趨勢并沒有影響。
? ? ? ?那第二段代碼和第三段代碼的時(shí)間復(fù)雜度是 O(n) 和 O(n2)。綜合這三段代碼的時(shí)間復(fù)雜度,我們?nèi)∑渲凶畲蟮牧考?。所以整段代碼的時(shí)間復(fù)雜度就為 O(n2)。也就是說:總的時(shí)間復(fù)雜度就等于量級最大的那段代碼的時(shí)間復(fù)雜度 。
嵌套代碼的復(fù)雜度等于嵌套內(nèi)外代碼復(fù)雜度的乘積
int cal(int n) {int ret = 0; int i = 1;for (; i < n; ++i) {ret = ret + f(i);} } int f(int n) {int sum = 0;int i = 1;for (; i < n; ++i) {sum = sum + i;} return sum;}? ? ? ?我們單獨(dú)看 cal() 函數(shù)。假設(shè) f() 只是一個(gè)普通的操作,那第 4~6 行的時(shí)間復(fù)雜度就是T1(n) = O(n)。但 f() 函數(shù)本身不是一個(gè)簡單的操作,它的時(shí)間復(fù)雜度是 T2(n) = O(n),所以,整個(gè) cal() 函數(shù)的時(shí)間復(fù)雜度就是,T(n) = T1(n) * T2(n) = O(n*n) = O(n2)。
1.2 常見時(shí)間復(fù)雜度
常量級 O(1)
只要代碼的執(zhí)行時(shí)間不隨 n 的增大而增長,這樣代碼的時(shí)間復(fù)雜度我們都記作 O(1)?;蛘哒f一般情況下,只要算法中不存在循環(huán)語句、遞歸語句,即使有成千上萬行的代碼,其時(shí)間復(fù)雜度也是Ο(1)。
對數(shù)階 O(logn)
對數(shù)階時(shí)間復(fù)雜度非常常見,同時(shí)也是最難分析的一種時(shí)間復(fù)雜度。
其實(shí)相當(dāng)于求解 2x=n 中 x的值, 也就是x=log2n,所以這段代碼的時(shí)間復(fù)雜度就是 O(log2n)。
i=1;while (i <= n) {i = i * 3; }? ? ? ?很簡單就能看出來,這段代碼的時(shí)間復(fù)雜度為 O(log3n)。實(shí)際上,不管是以 2 為底、以 3 為底,還是以 10 為底,我們可以把所有對數(shù)階的時(shí)間復(fù)雜度都記為 O(logn)。為什么呢?我們知道,對數(shù)之間是可以互相轉(zhuǎn)換的,log3n 就等于 log32 * log2n,所以 O(log3n) = O(C * log2n),其中 C=log32 是一個(gè)常量?;谖覀兦懊娴囊粋€(gè)理論:在采用大 O 標(biāo)記復(fù)雜度的時(shí)候,可以忽略系數(shù),即 O(Cf(n)) = O(f(n))。所以,O(log2n) 就等于 O(log3n)。因此,在對數(shù)階時(shí)間復(fù)雜度的表示方法里,我們忽略對數(shù)的“底”,統(tǒng)一表示為 O(logn)。
O(n)
O(nlogn)
像快排的時(shí)間復(fù)雜度就是O(nlogn)。
n2、n3、nk等k次方階
指數(shù)階O(2n) 、 階乘階O(n!)
這兩個(gè)屬于非多項(xiàng)式量級,我們把時(shí)間復(fù)雜度為非多項(xiàng)式量級的算法問題叫作 NP(Non-Deterministic Polynomial,非確定多項(xiàng)式)問題。非多項(xiàng)式時(shí)間復(fù)雜度的算法其實(shí)是非常低效的算法,一般很少遇到。
O(m+n)、O(m*n)
從下面代碼中可以看出,m 和 n 是表示兩個(gè)數(shù)據(jù)規(guī)模。我們無法事先評估 m 和 n 誰的量級大,所以我們在表示復(fù)雜度的時(shí)候,就不能簡單地利用加法法則,省略掉其中一個(gè)。所以下面代碼的時(shí)間復(fù)雜度就是 O(m+n)。
2.3 最壞時(shí)間復(fù)雜度、最好時(shí)間復(fù)雜度、平均時(shí)間復(fù)雜度
? ? ? ?顧名思義,最好情況時(shí)間復(fù)雜度就是,在最理想的情況下,執(zhí)行這段代碼的時(shí)間復(fù)雜度。同理,最壞情況時(shí)間復(fù)雜度就是,在最糟糕的情況下,執(zhí)行這段代碼的時(shí)間復(fù)雜度。
? ? ? ?而平均時(shí)間復(fù)雜度實(shí)際上在概率論中,應(yīng)該叫加權(quán)平均時(shí)間復(fù)雜度或者期望時(shí)間復(fù)雜度。很多時(shí)候,我們使用一個(gè)復(fù)雜度就可以滿足需求了。只有同一塊代碼在不同的情況下,時(shí)間復(fù)雜度有量級的差距,我們才會(huì)使用這三種復(fù)雜度表示法來區(qū)分。
? ? ? ?均攤時(shí)間復(fù)雜度,對一個(gè)數(shù)據(jù)結(jié)構(gòu)進(jìn)行一組連續(xù)操作中,大部分情況下時(shí)間復(fù)雜度都很低,只有個(gè)別情況下時(shí)間復(fù)雜度比較高,而且這些操作之間存在前后連貫的時(shí)序關(guān)系,這個(gè)時(shí)候,我們就可以將這一組操作放在一塊兒分析,看是否能將較高時(shí)間復(fù)雜度那次操作的耗時(shí)平攤到其他那些時(shí)間復(fù)雜度比較低的操作上。而且,在能夠應(yīng)用均攤時(shí)間復(fù)雜度分析的場合,一般均攤時(shí)間復(fù)雜度就等于最好情況時(shí)間復(fù)雜度。
2.空間復(fù)雜度
表示算法的存儲(chǔ)空間與數(shù)據(jù)規(guī)模之間的增長關(guān)系
void print(int n) {int i = 0;int[] a = new int[n];for (i; i <n; ++i) {a[i] = i * i;}for (i = n-1; i >= 0; --i) {print out a[i]} }? ? ? ?跟時(shí)間復(fù)雜度分析一樣,我們可以看到第 2 行代碼中,我們申請了一個(gè)空間存儲(chǔ)變量 i,但是它是常量階的,跟數(shù)據(jù)規(guī)模 n 沒有關(guān)系,所以我們可以忽略。第 3 行申請了一個(gè)大小為 n 的 int 類型數(shù)組,除此之外,剩下的代碼都沒有占用更多的空間,所以整段代碼的空間復(fù)雜度就是 O(n)。
? ? ? ?我們常見的空間復(fù)雜度就是 O(1)、O(n)、O(n2),像 O(logn)、O(nlogn) 這樣的對數(shù)階復(fù)雜度平時(shí)都用不到。
總結(jié)
以上是生活随笔為你收集整理的数据结构和算法之时间复杂度的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JVM监控工具有哪些
- 下一篇: (二)链表