日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

数据结构与算法笔记(一)—— 引入概念、时间复杂度

發(fā)布時(shí)間:2025/3/21 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构与算法笔记(一)—— 引入概念、时间复杂度 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、前沿

我們?yōu)槭裁匆獙W(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)和算法?

我們舉一個(gè)可能不太恰當(dāng)?shù)睦?
如果將最終寫(xiě)好運(yùn)行的程序比作戰(zhàn)場(chǎng),我們碼農(nóng)便是指揮作戰(zhàn)的將軍,而我們所寫(xiě)的代碼便是士兵和武器。

那么數(shù)據(jù)結(jié)構(gòu)和算法是什么? 答曰:兵法!

我們可以不看兵法在戰(zhàn)場(chǎng)上肉搏,如此,可能會(huì)勝利,可能會(huì)失敗。即使勝利,可能也會(huì)付出巨大的代價(jià)。我們寫(xiě)程序亦然:沒(méi)有看過(guò)數(shù)據(jù)結(jié)構(gòu)和算法,有時(shí)面對(duì)問(wèn)題可能會(huì)沒(méi)有任何思路,不知如何下手去解決;大部分時(shí)間可能解決了問(wèn)題,可是對(duì)程序運(yùn)行的效率和開(kāi)銷(xiāo)沒(méi)有意識(shí),性能低下;有時(shí)會(huì)借助別人開(kāi)發(fā)的利器暫時(shí)解決了問(wèn)題,可是遇到性能瓶頸的時(shí)候,又不知該如何進(jìn)行針對(duì)性的優(yōu)化。
如果我們??幢?#xff0c;便可做到胸有成竹,有時(shí)會(huì)事半功倍!同樣,如果我們??磾?shù)據(jù)結(jié)構(gòu)與算法,我們寫(xiě)程序時(shí)也能游刃有余、明察秋毫,遇到問(wèn)題時(shí)亦能入木三分、迎刃而解。
故,數(shù)據(jù)結(jié)構(gòu)和算法是一名程序開(kāi)發(fā)人員的必備基本功,不是一朝一夕就能練成絕世高手的。冰凍三尺非一日之寒,需要我們平時(shí)不斷的主動(dòng)去學(xué)習(xí)積累。


二、數(shù)據(jù)結(jié)構(gòu)

我們?nèi)绾斡肞ython中的類(lèi)型來(lái)保存一個(gè)班的學(xué)生信息?如果想要快速的通過(guò)學(xué)生姓名獲取其信息呢?

實(shí)際上當(dāng)我們?cè)谒伎歼@個(gè)問(wèn)題的時(shí)候,我們已經(jīng)用到了數(shù)據(jù)結(jié)構(gòu)。列表和字典都可以存儲(chǔ)一個(gè)班的學(xué)生信息,但是想要在列表中獲取一名同學(xué)的信息時(shí),就要遍歷這個(gè)列表,其時(shí)間復(fù)雜度為O(n),而使用字典存時(shí),可將學(xué)生姓名作為字典的鍵,學(xué)生信息作為值,進(jìn)而查詢(xún)時(shí)不需要遍歷便可快速獲取到學(xué)生信息,其時(shí)間復(fù)雜度為O(1)。

我們?yōu)榱私鉀Q問(wèn)題,需要將數(shù)據(jù)保存下來(lái),然后根據(jù)數(shù)據(jù)的存儲(chǔ)方式來(lái)設(shè)計(jì)算法實(shí)現(xiàn)進(jìn)行處理,那么數(shù)據(jù)的存儲(chǔ)方式不同就會(huì)導(dǎo)致需要不同的算法進(jìn)行處理。我們希望算法解決問(wèn)題的效率越快越好,于是我們就需要考慮數(shù)據(jù)究竟如何保存的問(wèn)題,這就是數(shù)據(jù)結(jié)構(gòu)。

在上面的問(wèn)題中我們可以選擇Python中的列表或字典來(lái)存儲(chǔ)學(xué)生信息。列表和字典就是Python內(nèi)建幫我們封裝好的兩種數(shù)據(jù)結(jié)構(gòu)。

2.1、概念

數(shù)據(jù)是一個(gè)抽象的概念,將其進(jìn)行分類(lèi)后得到程序設(shè)計(jì)語(yǔ)言中的基本類(lèi)型。如: int,float,char等。數(shù)據(jù)元素之間不獨(dú)立的,存在特定的關(guān)系,這些關(guān)系便是結(jié)構(gòu)。數(shù)據(jù)結(jié)構(gòu)指數(shù)據(jù)對(duì)象中數(shù)據(jù)元素之間的關(guān)系。

Python給我們提供了很多現(xiàn)成的數(shù)據(jù)結(jié)構(gòu)類(lèi)型,這些系統(tǒng)自己定義好的,不需要我們自己去定義的數(shù)據(jù)結(jié)構(gòu)叫做Python的內(nèi)置數(shù)據(jù)結(jié)構(gòu),比如列表、元組、字典。而有些數(shù)據(jù)組織方式,Python系統(tǒng)里面沒(méi)有直接定義,需要我們自己去定義實(shí)現(xiàn)這些數(shù)據(jù)的組織方式,這些數(shù)據(jù)組織方式稱(chēng)之為Python的擴(kuò)展數(shù)據(jù)結(jié)構(gòu),比如棧,隊(duì)列等。


2.2、算法與數(shù)據(jù)結(jié)構(gòu)的區(qū)別

數(shù)據(jù)結(jié)構(gòu)只是靜態(tài)的描述了數(shù)據(jù)元素之間的關(guān)系。

高效的程序需要在數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ)上設(shè)計(jì)和選擇算法。

程序 = 數(shù)據(jù)結(jié)構(gòu)+算法

總結(jié):算法是為了解決實(shí)際問(wèn)題而設(shè)計(jì)的,數(shù)據(jù)結(jié)構(gòu)是算法需要處理的問(wèn)題載體


2.3、抽象數(shù)據(jù)類(lèi)型(Abstract Data Type)

抽象數(shù)據(jù)類(lèi)型(ADT)的含義是指一個(gè)數(shù)學(xué)模型以及定義在此數(shù)學(xué)模型上的一組操作。即把數(shù)據(jù)類(lèi)型和數(shù)據(jù)類(lèi)型上的運(yùn)算捆在一起,進(jìn)行封裝。引入抽象數(shù)據(jù)類(lèi)型的目的是把數(shù)據(jù)類(lèi)型的表示和數(shù)據(jù)類(lèi)型上運(yùn)算的實(shí)現(xiàn)與這些數(shù)據(jù)類(lèi)型和運(yùn)算在程序中的引用隔開(kāi),使它們相互獨(dú)立。

最常用的數(shù)據(jù)運(yùn)算有五種:

  • 插入
  • 刪除
  • 修改
  • 查找
  • 排序

三、算法的提出

3.1、算法的概念

算法是計(jì)算機(jī)處理信息的本質(zhì),因?yàn)橛?jì)算機(jī)程序本質(zhì)上是一個(gè)算法來(lái)告訴計(jì)簿機(jī)確切的步驟來(lái)執(zhí)行一個(gè)指定的任務(wù)。一般地,當(dāng)算法在處理信息時(shí),會(huì)從輸入設(shè)備或數(shù)據(jù)的存儲(chǔ)地址讀取數(shù)據(jù),把結(jié)果寫(xiě)入輸出設(shè)備或某個(gè)存儲(chǔ)地址供以后再調(diào)用。

算法是獨(dú)立存在的一種解決問(wèn)題的方法和思想。

對(duì)于算法而言,實(shí)現(xiàn)的語(yǔ)言并不重要,重要的是思想。

算法可以有不同的語(yǔ)言描述實(shí)現(xiàn)版本(如C描述、C++描述、Python描述等),我們現(xiàn)在是在用Python語(yǔ)言進(jìn)行描述實(shí)現(xiàn)。


3.2、算法的五大特性

  • 輸入:算法具有0個(gè)或多個(gè)輸入
  • 輸出:算法至少有1個(gè)或多個(gè)輸出
  • 有窮性:算法在有限的步驟之后會(huì)自動(dòng)結(jié)束而不會(huì)無(wú)限循環(huán),并且每一個(gè)步驟可以在可接受的時(shí)間內(nèi)完成
  • 確定性:算法中的每一步都有確定的含義,不會(huì)出現(xiàn)二義性
  • 可行性:算法的每一步都是可行的,也就是說(shuō)每一步都能夠執(zhí)行有限的次數(shù)完成

  • 四、算法效率衡量

    我們先來(lái)看一道題:

    如果a+b+c=1000,且 a^2 + b^2 =c^2 (a,b,c為自然數(shù)),如何求出所有a、b、c可能的組合? import time # 方法一 start_time = time.time() for a in range(0,1001):for b in range(0,1001):for c in range(0,1001):if a**2+b**2==c**2 and a+b+c==1000:print('a,b,c: %d,%d,%d'%(a,b,c)) end_time = time.time() print('time:%d',(end_time-start_time)) #方法二 start_time = time.time() for a in range(0,1001):for b in range(0,1001):c = 1000-a-bif a**2+b**2==c**2:print('a,b,c: %d,%d,%d'%(a,b,c)) end_time = time.time() print('time:%d',(end_time-start_time))

    執(zhí)行時(shí)間反應(yīng)算法效率

    對(duì)于同一問(wèn)題,我們給出了兩種解決算法,在兩種算法的實(shí)現(xiàn)中,我們對(duì)程序執(zhí)行的時(shí)間進(jìn)行了測(cè)算,發(fā)現(xiàn)兩段程序執(zhí)行的時(shí)間相差懸殊,由此我們可以得出結(jié)論:實(shí)現(xiàn)算法程序的執(zhí)行時(shí)間可以反應(yīng)出算法的效率,即算法的優(yōu)劣。


    單靠時(shí)間值絕對(duì)可信嗎?

    假設(shè)我們將第二次嘗試的算法程序運(yùn)行在一臺(tái)配置古老性能低下的計(jì)算機(jī)中,情況會(huì)如何?很可能運(yùn)行的時(shí)間并不會(huì)比在我們的電腦中運(yùn)行算法一的快多少。
    單純依靠運(yùn)行的時(shí)間來(lái)比較算法的優(yōu)劣并不一定是客觀(guān)準(zhǔn)確的!
    程序的運(yùn)行離不開(kāi)計(jì)算機(jī)環(huán)境(包括硬件和操作系統(tǒng)),這些客觀(guān)原因會(huì)影響程序運(yùn)行的速度并反應(yīng)在程序的執(zhí)行時(shí)間上。那么如何才能客觀(guān)的評(píng)判一個(gè)算法的優(yōu)劣呢?


    4.1、時(shí)間復(fù)雜度與“大O記法”

    我們假定計(jì)算機(jī)執(zhí)行算法每一個(gè)基本操作的時(shí)間是固定的一個(gè)時(shí)間單位,那么有多少個(gè)基本操作就代表會(huì)花費(fèi)多少時(shí)間單位。算然對(duì)于不同的機(jī)器環(huán)境而言,確切的單位時(shí)間是不同的,但是對(duì)于算法進(jìn)行多少個(gè)基本操作〈即花費(fèi)多少時(shí)間單位)在規(guī)模數(shù)量級(jí)上卻是相同的,由此可以忽略機(jī)器環(huán)境的影響而客觀(guān)的反應(yīng)算法的時(shí)間效率。

    對(duì)于算法的時(shí)間效率,我們可以用“大O記法"來(lái)表示。
    “大o記法”:對(duì)于單調(diào)的整數(shù)函數(shù)f,如果存在一個(gè)整數(shù)函數(shù)g和實(shí)常數(shù)c>0,使得對(duì)于充分大的n總有f(n)<=c*g(n),就說(shuō)函數(shù)g是f的一個(gè)漸近函數(shù)(忽略常數(shù)),記為f(n)=O(g(n))。也就是說(shuō),在趨向無(wú)窮的極限意義下,函數(shù)f的薈長(zhǎng)速度受到函數(shù)g的約束,亦即函數(shù)f與函數(shù)g的特征相似。

    時(shí)間復(fù)雜度:假設(shè)存在函數(shù)g,使得算法A處理規(guī)模為n的問(wèn)題示例所用時(shí)間為T(mén)(n)=O(g(n)),則稱(chēng)O(g(n))為算法A的漸近時(shí)間復(fù)雜度,簡(jiǎn)稱(chēng)時(shí)間復(fù)雜度,記為T(mén)(n)


    4.2、如何理解“大o記法”

    對(duì)于算法進(jìn)行特別具體的細(xì)教分析雖然很好,但在實(shí)踐中的實(shí)際價(jià)值有限。對(duì)于算法的時(shí)間性質(zhì)和空間性質(zhì),最重要的是其數(shù)量級(jí)和趨勢(shì),這些是分析算法效率的主要部分。而計(jì)量算法基本操作數(shù)量的規(guī)模函數(shù)中那些常量因子可以忽略不計(jì)。例如,可以認(rèn)為3n^2和 100n2屬于同一個(gè)量級(jí),如果兩個(gè)算法處理同樣規(guī)模實(shí)例的代價(jià)分別為這兩個(gè)函數(shù),就認(rèn)為它們的效率“差不多",都為n2級(jí)。


    4.3、最壞時(shí)間復(fù)雜度

    分析算法時(shí),存在幾種可能的考慮:

    • 算法完成工作最少需要多少基本操作,即最優(yōu)時(shí)間復(fù)雜度
    • 算法完成工作最多需要多少基本操作,即最壞時(shí)間復(fù)雜度
    • 算法完成工作平均需要多少基本操作,即平均時(shí)間復(fù)雜度

    對(duì)于最優(yōu)時(shí)間復(fù)雜度,其價(jià)值不大,因?yàn)樗鼪](méi)有提供什么有用信息,其反映的只是最樂(lè)觀(guān)最理想的情況,沒(méi)有參考價(jià)值。

    對(duì)于最壞時(shí)間復(fù)雜度,提供了一種保證,表明算法在此種程度的基本操作中一定能完成工作。

    對(duì)于平均時(shí)間復(fù)雜度,是對(duì)算法的一個(gè)全面評(píng)價(jià),因此它完整全面的反映了這個(gè)算法的性質(zhì)。但另一方面,這種衡量并沒(méi)有保證,不是每個(gè)計(jì)算都能在這個(gè)基本操作內(nèi)完成。而且,對(duì)于平均情況的計(jì)算,也會(huì)因?yàn)閼?yīng)用算法的實(shí)例分布可能并不均勻而難以計(jì)算。

    因此,我們主要關(guān)注算法的最壞情況,亦即最壞時(shí)間復(fù)雜度。


    4.3、時(shí)間復(fù)雜度的幾條基本計(jì)算規(guī)則

  • 基本操作,即只有常數(shù)項(xiàng),認(rèn)為其時(shí)間復(fù)雜度為O(1)
  • 順序結(jié)構(gòu),時(shí)間復(fù)雜度按加法進(jìn)行計(jì)算
  • 循環(huán)結(jié)構(gòu),時(shí)間復(fù)雜度按乘法進(jìn)行計(jì)算
  • 分支結(jié)構(gòu),時(shí)間復(fù)雜度取最大值
  • 判斷一個(gè)算法的效率時(shí),往往只需要關(guān)注操作數(shù)量的最高次項(xiàng),其它次要項(xiàng)和常數(shù)項(xiàng)可以忽略6. 在沒(méi)有特殊說(shuō)明時(shí),我們所分析的算法的時(shí)間復(fù)雜度都是指最壞時(shí)間復(fù)雜度

  • 4.4、常見(jiàn)時(shí)間復(fù)雜度

    注意:經(jīng)常將log2 n(以2為底的對(duì)數(shù))簡(jiǎn)寫(xiě)成logn

    常見(jiàn)時(shí)間復(fù)雜度之間的關(guān)系

    所消耗的時(shí)間從小到大:
    O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n ) < O(n!) < O(n^n)


    五、Python內(nèi)置類(lèi)型性能分析

    5.1、timeit模塊

    timeit模塊可以用來(lái)測(cè)試一小段Python代碼的執(zhí)行速度。

    class timeit.Timer(stmt='pass', setup='pass', timer=<timer function>)

    Timer是測(cè)量小段代碼執(zhí)行速度的類(lèi)。
    參數(shù):

    • stmt:要測(cè)試的代碼語(yǔ)句(statment) ;
    • setup:運(yùn)行代碼時(shí)需要的設(shè)置;
    • timer:一個(gè)定時(shí)器函數(shù),與平臺(tái)有關(guān)。
    timeit.Timer.timeit(number=100000)

    Timer類(lèi)中測(cè)試語(yǔ)句執(zhí)行速度的對(duì)象方法。
    參數(shù):

    • number:測(cè)試代碼時(shí)的測(cè)試次數(shù),默認(rèn)為1000000次。

    返回值:

    • 返回執(zhí)行代碼的平均耗時(shí),一個(gè)float類(lèi)型的秒數(shù)。

    5.2、list的操作時(shí)間復(fù)雜度

    from timeit import Timerdef t1():li =[]for i in range(1000):li.append(i)def t2():li =[]for i in range(1000):li = li + [i]def t3():li =[i for i in range(1000)]def t4():li=list(range(1000))def t5():li=[]for i in range(1000):li.extend([i])timer1 = Timer("t1()", "from __main__ import t1") print("append: ",timer1.timeit(1000))timer2 = Timer("t2()","from __main__ import t2") print("+:",timer2.timeit(1000))timer3 = Timer("t3()","from __main__ import t3") print("[i for i in range]: ",timer3.timeit( 1000))timer4 = Timer("t4()","from __main__ import t4") print("list(range()):",timer4.timeit(1000))timer5 = Timer("t5()","from __main__ import t5") print("extend: ",timer5.timeit(1000))------------結(jié)果----------- append: 0.1197639 +: 1.3471121 [i for i in range]: 0.029948899999999945 list(range()): 0.012717199999999984 extend: 0.1123786

    5.3、dict的操作時(shí)間復(fù)雜度

    總結(jié)

    以上是生活随笔為你收集整理的数据结构与算法笔记(一)—— 引入概念、时间复杂度的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。