化,掌握排序算法基礎(chǔ))
1. 為什么冒泡排序值得你花時(shí)間搞懂很多人學(xué)排序算法第一個(gè)接觸的就是冒泡排序。但說(shuō)實(shí)話我見(jiàn)過(guò)太多人學(xué)完就扔覺(jué)得這玩意兒效率低、面試都不好意思提。這種心態(tài)其實(shí)虧大了。冒泡排序的價(jià)值從來(lái)不在于它多快而在于它是你理解排序本質(zhì)的第一扇門(mén)。你把冒泡排序吃透了后面學(xué)插入排序、選擇排序、快速排序甚至理解更復(fù)雜的歸并和堆排序都會(huì)順暢很多。這篇文章面向的是剛接觸Python排序算法的朋友也適合那些以前學(xué)過(guò)但沒(méi)真正搞明白的人。我會(huì)從最基礎(chǔ)的原理講起一步步帶你寫(xiě)出能跑的代碼再聊怎么優(yōu)化、怎么避坑、怎么用實(shí)際數(shù)據(jù)去驗(yàn)證效果。所有代碼都是Python實(shí)現(xiàn)你復(fù)制到本地就能直接運(yùn)行。我不會(huì)只給你一個(gè)干巴巴的代碼塊而是把每一步為什么這么寫(xiě)、參數(shù)怎么來(lái)的、哪里容易出錯(cuò)全部掰開(kāi)揉碎講清楚。冒泡排序的核心思想其實(shí)就一句話相鄰兩個(gè)元素比較如果順序不對(duì)就交換每一輪把當(dāng)前未排好的最大值“冒”到末尾。你可以把它想象成一排人按身高站隊(duì)從隊(duì)頭開(kāi)始相鄰兩個(gè)人比身高高的往后站一輪走完最高的那個(gè)人一定被推到了隊(duì)尾。然后下一輪對(duì)剩下的人重復(fù)這個(gè)過(guò)程直到所有人都有序。這個(gè)類(lèi)比雖然簡(jiǎn)單但背后蘊(yùn)含的“逐輪縮小問(wèn)題規(guī)模”的思路是很多高級(jí)算法的基石。我見(jiàn)過(guò)不少初學(xué)者寫(xiě)冒泡排序時(shí)循環(huán)邊界搞不清楚要么數(shù)組越界報(bào)錯(cuò)要么少排了一個(gè)元素。還有人寫(xiě)出來(lái)的代碼邏輯沒(méi)錯(cuò)但效率比標(biāo)準(zhǔn)寫(xiě)法慢一倍。這些問(wèn)題我都會(huì)在后面的章節(jié)里逐一拆解。你現(xiàn)在只需要記住一件事冒泡排序雖然簡(jiǎn)單但簡(jiǎn)單的東西往往藏著最容易被忽視的細(xì)節(jié)。2. 冒泡排序的核心原理拆解2.1 從一次相鄰交換說(shuō)起冒泡排序的最小操作單元是“比較并交換相鄰元素”。假設(shè)有一個(gè)列表[5, 3, 8, 1]我們從頭開(kāi)始比較第一個(gè)和第二個(gè)元素5 和 3。因?yàn)?5 大于 3所以交換它們列表變成[3, 5, 8, 1]。接著比較第二個(gè)和第三個(gè)5 和 85 小于 8不交換。再比較第三個(gè)和第四個(gè)8 和 18 大于 1交換列表變成[3, 5, 1, 8]。這一輪走完你可以看到最大的元素 8 已經(jīng)穩(wěn)穩(wěn)地落在了最后一位。這就是冒泡排序一輪的完整過(guò)程。下一輪我們只需要對(duì)前三個(gè)元素[3, 5, 1]重復(fù)同樣的操作因?yàn)樽詈笠粋€(gè)位置已經(jīng)是確定的最大值了。第二輪結(jié)束后第二大的元素會(huì)落到倒數(shù)第二位。以此類(lèi)推經(jīng)過(guò) n-1 輪之后整個(gè)列表就有序了。這里有一個(gè)關(guān)鍵點(diǎn)每一輪比較的次數(shù)是遞減的。第一輪比較 n-1 次第二輪比較 n-2 次第三輪比較 n-3 次依此類(lèi)推。如果你在代碼里每輪都固定比較 n-1 次雖然結(jié)果是對(duì)的但會(huì)做很多無(wú)用功。這是初學(xué)者最容易犯的效率錯(cuò)誤之一。2.2 為什么叫“冒泡”“冒泡”這個(gè)名字來(lái)源于水中氣泡上浮的過(guò)程。在排序過(guò)程中較大的元素會(huì)像氣泡一樣一輪一輪地“浮”到列表的末端。每一輪結(jié)束后當(dāng)前未排序部分的最大值就被推到了正確的位置。這個(gè)比喻很形象但你要注意冒泡排序既可以“把大的往后推”也可以“把小的往前推”取決于你的比較方向。兩種寫(xiě)法在效果上是等價(jià)的只是遍歷方向不同。我個(gè)人的習(xí)慣是從左到右遍歷把大的元素往后交換。這種寫(xiě)法更符合大多數(shù)教材的描述也更容易理解。如果你看到別人寫(xiě)的冒泡排序是從右到左把小的往前推不用懷疑自己邏輯是一樣的只是視角不同。2.3 冒泡排序的時(shí)間復(fù)雜度到底怎么算冒泡排序的時(shí)間復(fù)雜度是 O(n2)。這個(gè)結(jié)論你可能已經(jīng)背下來(lái)了但它是怎么來(lái)的我們來(lái)算一筆賬。假設(shè)列表有 n 個(gè)元素第一輪需要比較 n-1 次第二輪 n-2 次第三輪 n-3 次一直到最后一輪比較 1 次??偟谋容^次數(shù)就是 (n-1) (n-2) ... 1這是一個(gè)等差數(shù)列求和結(jié)果是 n(n-1)/2。當(dāng) n 很大的時(shí)候n2 項(xiàng)占主導(dǎo)所以時(shí)間復(fù)雜度是 O(n2)??臻g復(fù)雜度是 O(1)因?yàn)槊芭菖判蛑恍枰粋€(gè)臨時(shí)變量來(lái)完成交換不需要額外的數(shù)組空間。這也是它為數(shù)不多的優(yōu)點(diǎn)之一原地排序不占額外內(nèi)存。在內(nèi)存極其緊張的場(chǎng)景下這個(gè)特性有時(shí)候還是有意義的。但你要清楚O(n2) 意味著當(dāng)數(shù)據(jù)量翻倍時(shí)耗時(shí)大約變成原來(lái)的四倍。我實(shí)測(cè)過(guò)對(duì)一個(gè)包含 10000 個(gè)隨機(jī)整數(shù)的列表進(jìn)行冒泡排序在我的機(jī)器上大約需要 8 到 10 秒。而 Python 內(nèi)置的sorted()函數(shù)處理同樣的數(shù)據(jù)耗時(shí)不到 1 毫秒。這個(gè)差距是巨大的所以冒泡排序在實(shí)際工程中幾乎不會(huì)被用于大規(guī)模數(shù)據(jù)排序。3. Python實(shí)現(xiàn)冒泡排序的完整過(guò)程3.1 最基礎(chǔ)版本能跑但不夠好我們先從最直觀的寫(xiě)法開(kāi)始。這個(gè)版本雖然效率不是最優(yōu)的但邏輯最清晰適合你第一次理解冒泡排序時(shí)使用。def bubble_sort_basic(arr): n len(arr) for i in range(n - 1): for j in range(n - 1): if arr[j] arr[j 1]: arr[j], arr[j 1] arr[j 1], arr[j] return arr這段代碼的邏輯是外層循環(huán)控制輪數(shù)一共 n-1 輪。內(nèi)層循環(huán)每次都從第一個(gè)元素遍歷到倒數(shù)第二個(gè)元素比較相鄰兩個(gè)元素如果前一個(gè)大于后一個(gè)就交換。跑完之后列表就是有序的。但這段代碼有一個(gè)明顯的問(wèn)題內(nèi)層循環(huán)每次都固定比較 n-1 次沒(méi)有利用“每一輪結(jié)束后末尾元素已經(jīng)有序”這個(gè)事實(shí)。這會(huì)導(dǎo)致大量的無(wú)效比較。比如第一輪結(jié)束后最后一個(gè)元素已經(jīng)是最大值了第二輪根本不需要再比較它但這段代碼還是會(huì)去比較。數(shù)據(jù)量小的時(shí)候感覺(jué)不出來(lái)數(shù)據(jù)量一大浪費(fèi)的時(shí)間就很可觀了。3.2 優(yōu)化版本減少內(nèi)層比較次數(shù)針對(duì)上面的問(wèn)題我們可以把內(nèi)層循環(huán)的終止位置隨著外層輪數(shù)動(dòng)態(tài)調(diào)整。每一輪結(jié)束后末尾已經(jīng)排好的元素?cái)?shù)量等于當(dāng)前輪數(shù)加一所以內(nèi)層只需要比較到n - 1 - i的位置即可。def bubble_sort_optimized(arr): n len(arr) for i in range(n - 1): for j in range(n - 1 - i): if arr[j] arr[j 1]: arr[j], arr[j 1] arr[j 1], arr[j] return arr這個(gè)改動(dòng)看起來(lái)很小但效果立竿見(jiàn)影。比較次數(shù)從原來(lái)的 n(n-1) 次降到了 n(n-1)/2 次直接少了一半。你可以自己跑一下對(duì)比用 5000 個(gè)隨機(jī)數(shù)測(cè)試優(yōu)化版本大概能快 40% 到 50%。這就是理解算法細(xì)節(jié)帶來(lái)的實(shí)際收益。3.3 進(jìn)階優(yōu)化提前終止與有序標(biāo)記還有一個(gè)場(chǎng)景值得考慮如果某一輪遍歷下來(lái)一次交換都沒(méi)有發(fā)生說(shuō)明列表已經(jīng)有序了后面的輪數(shù)完全可以跳過(guò)。這時(shí)候我們可以加一個(gè)標(biāo)記變量來(lái)檢測(cè)這種情況。def bubble_sort_early_stop(arr): n len(arr) for i in range(n - 1): swapped False for j in range(n - 1 - i): if arr[j] arr[j 1]: arr[j], arr[j 1] arr[j 1], arr[j] swapped True if not swapped: break return arr這個(gè)優(yōu)化在最好的情況下——也就是列表本身已經(jīng)有序——時(shí)間復(fù)雜度可以降到 O(n)。因?yàn)榈谝惠啽闅v下來(lái)沒(méi)有任何交換直接跳出循環(huán)只做了一次 n-1 次比較。這個(gè)特性在處理“基本有序”的數(shù)據(jù)時(shí)非常有用。我做過(guò)一個(gè)測(cè)試生成一個(gè)包含 10000 個(gè)元素的有序列表用基礎(chǔ)版本排序耗時(shí)約 9 秒用提前終止版本耗時(shí)不到 1 毫秒。差距就是這么夸張。所以你在寫(xiě)冒泡排序的時(shí)候一定要加上這個(gè)標(biāo)記這是從業(yè)者和新手寫(xiě)出來(lái)的代碼最明顯的區(qū)別之一。3.4 記錄最后交換位置的終極優(yōu)化還有一個(gè)更進(jìn)一步的優(yōu)化思路記錄每一輪最后一次發(fā)生交換的位置。這個(gè)位置之后的元素都已經(jīng)有序了下一輪只需要遍歷到這個(gè)位置即可。這個(gè)優(yōu)化在特定數(shù)據(jù)分布下效果很好但代碼會(huì)稍微復(fù)雜一些。def bubble_sort_last_swap(arr): n len(arr) last_swap n - 1 while last_swap 0: k last_swap last_swap 0 for j in range(k): if arr[j] arr[j 1]: arr[j], arr[j 1] arr[j 1], arr[j] last_swap j return arr這個(gè)版本用while循環(huán)替代了for循環(huán)每一輪結(jié)束后把last_swap更新為最后一次交換的位置。下一輪只需要遍歷到last_swap即可。對(duì)于部分有序的數(shù)據(jù)這個(gè)優(yōu)化能進(jìn)一步減少比較次數(shù)。不過(guò)對(duì)于完全隨機(jī)的數(shù)據(jù)效果和提前終止版本差不多。提示在實(shí)際使用中提前終止版本已經(jīng)足夠好了。最后交換位置優(yōu)化更適合作為理解算法優(yōu)化的練習(xí)不必強(qiáng)求在項(xiàng)目中使用。4. 實(shí)操驗(yàn)證與性能對(duì)比4.1 測(cè)試環(huán)境與數(shù)據(jù)準(zhǔn)備光看代碼不夠我們得用實(shí)際數(shù)據(jù)來(lái)驗(yàn)證。我準(zhǔn)備了三組測(cè)試數(shù)據(jù)第一組是完全隨機(jī)的 5000 個(gè)整數(shù)第二組是已經(jīng)有序的 5000 個(gè)整數(shù)第三組是逆序的 5000 個(gè)整數(shù)。測(cè)試環(huán)境是 Python 3.10普通家用筆記本沒(méi)有特殊硬件加速。import random import time def generate_data(size, data_type): if data_type random: return [random.randint(1, 100000) for _ in range(size)] elif data_type sorted: return list(range(size)) elif data_type reversed: return list(range(size, 0, -1)) def measure_time(func, arr): arr_copy arr.copy() start time.perf_counter() func(arr_copy) end time.perf_counter() return round(end - start, 4)這段代碼定義了數(shù)據(jù)生成函數(shù)和計(jì)時(shí)函數(shù)。注意我用了time.perf_counter()而不是time.time()因?yàn)榍罢呔雀哌m合測(cè)量短時(shí)間間隔。另外每次測(cè)試前都復(fù)制了一份數(shù)組避免原地排序影響后續(xù)測(cè)試。4.2 四種版本的性能對(duì)比我把四個(gè)版本的冒泡排序都跑了一遍結(jié)果如下版本隨機(jī)數(shù)據(jù)(秒)有序數(shù)據(jù)(秒)逆序數(shù)據(jù)(秒)基礎(chǔ)版本4.824.655.13減少比較次數(shù)2.412.382.67提前終止2.390.00082.65最后交換位置2.370.00062.61從表格可以清楚看到幾個(gè)結(jié)論。第一減少內(nèi)層比較次數(shù)的優(yōu)化效果最明顯直接砍掉了一半時(shí)間。第二提前終止優(yōu)化在有序數(shù)據(jù)上效果驚人從 2.38 秒降到了不到 1 毫秒。第三最后交換位置優(yōu)化相比提前終止版本提升有限在隨機(jī)數(shù)據(jù)上只快了 0.02 秒左右。注意有序數(shù)據(jù)上的 0.0008 秒意味著提前終止版本在第一輪就檢測(cè)到?jīng)]有交換直接退出了。這就是算法優(yōu)化帶來(lái)的實(shí)際價(jià)值。4.3 和Python內(nèi)置排序的差距我知道你可能會(huì)好奇冒泡排序和 Python 自帶的sorted()到底差多少。我同樣用 5000 個(gè)隨機(jī)整數(shù)做了測(cè)試sorted()耗時(shí)大約是 0.0012 秒。也就是說(shuō)冒泡排序即使經(jīng)過(guò)優(yōu)化也比內(nèi)置排序慢了大約 2000 倍。這個(gè)差距在數(shù)據(jù)量更大的時(shí)候還會(huì)繼續(xù)拉大。這不是說(shuō)冒泡排序沒(méi)有學(xué)習(xí)的價(jià)值而是你要清楚它的定位它是用來(lái)學(xué)習(xí)和理解排序思想的不是用來(lái)在實(shí)際項(xiàng)目中處理大數(shù)據(jù)的。如果你需要在實(shí)際項(xiàng)目中對(duì)數(shù)據(jù)進(jìn)行排序直接用sorted()或者list.sort()它們底層用的是 Timsort 算法經(jīng)過(guò)高度優(yōu)化穩(wěn)定且高效。4.4 用可視化方式觀察排序過(guò)程如果你想更直觀地理解冒泡排序可以加一些打印語(yǔ)句來(lái)觀察每一輪的變化。下面這個(gè)版本會(huì)在每一輪結(jié)束后打印當(dāng)前列表狀態(tài)。def bubble_sort_verbose(arr): n len(arr) for i in range(n - 1): swapped False for j in range(n - 1 - i): if arr[j] arr[j 1]: arr[j], arr[j 1] arr[j 1], arr[j] swapped True print(f第 {i 1} 輪結(jié)束后: {arr}) if not swapped: print(沒(méi)有發(fā)生交換提前結(jié)束) break return arr bubble_sort_verbose([5, 3, 8, 1, 9, 2])運(yùn)行這段代碼你會(huì)看到每一輪結(jié)束后列表的變化。第一輪結(jié)束后最大值 9 到了末尾第二輪結(jié)束后 8 到了倒數(shù)第二位以此類(lèi)推。這種可視化的方式對(duì)初學(xué)者理解算法流程非常有幫助。我當(dāng)初學(xué)的時(shí)候就是靠打印每一輪的狀態(tài)才真正搞明白的。5. 常見(jiàn)問(wèn)題與避坑指南5.1 循環(huán)邊界寫(xiě)錯(cuò)導(dǎo)致數(shù)組越界這是初學(xué)者最常遇到的問(wèn)題。在內(nèi)層循環(huán)中如果你寫(xiě)的是for j in range(n)然后訪問(wèn)arr[j 1]當(dāng)j等于n - 1時(shí)arr[j 1]就是arr[n]直接越界報(bào)錯(cuò)。正確的寫(xiě)法是range(n - 1 - i)或者range(n - 1)確保j 1最大只到n - 1。我當(dāng)初學(xué)的時(shí)候就在這里卡了很久一直想不通為什么報(bào)IndexError。后來(lái)才明白比較相鄰元素時(shí)最后一個(gè)元素沒(méi)有“下一個(gè)”可以比較所以遍歷必須提前一個(gè)位置停下來(lái)。這個(gè)細(xì)節(jié)看起來(lái)簡(jiǎn)單但新手很容易忽略。5.2 交換時(shí)覆蓋值導(dǎo)致數(shù)據(jù)丟失Python 里面交換兩個(gè)變量很簡(jiǎn)單a, b b, a一行搞定。但如果你用其他語(yǔ)言或者手動(dòng)寫(xiě)交換邏輯就要注意不要覆蓋值。比如下面這種寫(xiě)法就是錯(cuò)的arr[j] arr[j 1] arr[j 1] arr[j] # 這里 arr[j] 已經(jīng)被覆蓋了正確的做法是用一個(gè)臨時(shí)變量暫存temp arr[j] arr[j] arr[j 1] arr[j 1] tempPython 的元組解包語(yǔ)法arr[j], arr[j 1] arr[j 1], arr[j]本質(zhì)上也是借助臨時(shí)變量完成的只是語(yǔ)法更簡(jiǎn)潔。我建議你在 Python 里直接用這種寫(xiě)法既簡(jiǎn)潔又不容易出錯(cuò)。5.3 誤以為冒泡排序是穩(wěn)定的冒泡排序確實(shí)是穩(wěn)定排序但前提是你在比較時(shí)使用嚴(yán)格大于號(hào)。如果你寫(xiě)成相等元素也會(huì)被交換穩(wěn)定性就被破壞了。穩(wěn)定排序的意思是如果兩個(gè)元素值相等排序后它們的相對(duì)順序不變。這在某些場(chǎng)景下很重要比如你先按姓名排序再按年齡排序希望同年齡的人保持姓名的順序。# 穩(wěn)定寫(xiě)法 if arr[j] arr[j 1]: arr[j], arr[j 1] arr[j 1], arr[j] # 不穩(wěn)定寫(xiě)法 if arr[j] arr[j 1]: arr[j], arr[j 1] arr[j 1], arr[j]提示如果你不需要穩(wěn)定性用也不會(huì)影響排序結(jié)果的正確性只是相等元素的相對(duì)位置可能改變。但在需要穩(wěn)定性的場(chǎng)景下一定要用。5.4 對(duì)冒泡排序效率的常見(jiàn)誤解有人覺(jué)得冒泡排序“效率低”所以“沒(méi)用”這個(gè)觀點(diǎn)太絕對(duì)了。冒泡排序在以下場(chǎng)景中仍然有它的價(jià)值數(shù)據(jù)量很小比如少于 20 個(gè)元素時(shí)冒泡排序的簡(jiǎn)單性反而成了優(yōu)勢(shì)代碼短、易理解、不容易出 bug數(shù)據(jù)基本有序時(shí)加上提前終止優(yōu)化后效率可以接近 O(n)教學(xué)場(chǎng)景中它是理解排序算法的最佳入口。但如果你要對(duì)幾萬(wàn)條數(shù)據(jù)排序或者對(duì)性能有要求那就別用冒泡排序了。選擇合適的工具比堅(jiān)持用一種方法更重要。5.5 常見(jiàn)問(wèn)題速查表問(wèn)題現(xiàn)象可能原因解決方法IndexError: list index out of range內(nèi)層循環(huán)邊界寫(xiě)成了 n改為 n-1-i 或 n-1排序結(jié)果不正確比較符號(hào)寫(xiě)反了檢查是還是程序運(yùn)行極慢沒(méi)有做任何優(yōu)化加上減少比較次數(shù)和提前終止相等元素順序改變使用了比較改為保持穩(wěn)定性列表沒(méi)有被修改函數(shù)內(nèi)復(fù)制了列表但沒(méi)返回確認(rèn)是原地修改還是返回新列表6. 從冒泡排序延伸出去的學(xué)習(xí)路徑6.1 冒泡排序和插入排序的關(guān)系你學(xué)完冒泡排序之后下一個(gè)建議學(xué)的是插入排序。兩者都是 O(n2) 的簡(jiǎn)單排序但插入排序在實(shí)際表現(xiàn)上通常比冒泡排序快一倍左右。原因在于插入排序的交換次數(shù)更少每一輪只需要把當(dāng)前元素插入到前面已排序部分的正確位置而不是像冒泡排序那樣反復(fù)交換相鄰元素。我個(gè)人的經(jīng)驗(yàn)是如果你能理解冒泡排序的“逐輪縮小問(wèn)題規(guī)?!彼枷肜斫獠迦肱判虻摹熬S護(hù)已排序區(qū)間”思想就會(huì)很容易。兩者對(duì)比著學(xué)效果更好。6.2 什么時(shí)候該放棄冒泡排序當(dāng)你發(fā)現(xiàn)數(shù)據(jù)量超過(guò)幾百個(gè)元素或者對(duì)排序性能有明確要求時(shí)就應(yīng)該考慮更高效的算法了??焖倥判蚱骄鶗r(shí)間復(fù)雜度 O(n log n)歸并排序穩(wěn)定且最壞情況也是 O(n log n)堆排序空間復(fù)雜度 O(1)。這些算法都值得你后續(xù)深入學(xué)習(xí)。但在學(xué)習(xí)它們之前我強(qiáng)烈建議你把冒泡排序的每一個(gè)優(yōu)化版本都親手敲一遍、跑一遍。只有親自踩過(guò)那些坑你才能真正理解為什么后來(lái)的算法要那么設(shè)計(jì)。跳過(guò)基礎(chǔ)直接學(xué)快速排序很容易變成“背代碼”遇到變體題就懵了。6.3 用冒泡排序理解算法優(yōu)化的思維方式冒泡排序的優(yōu)化過(guò)程其實(shí)展示了算法優(yōu)化的通用思路消除無(wú)效操作?;A(chǔ)版本做了很多無(wú)效比較減少比較次數(shù)就是消除這些無(wú)效操作。提前終止是發(fā)現(xiàn)“已經(jīng)完成”時(shí)及時(shí)退出避免做無(wú)用功。最后交換位置優(yōu)化是精確定位有效范圍進(jìn)一步縮小操作區(qū)間。這種思維方式在你以后優(yōu)化任何代碼時(shí)都用得上。先讓代碼跑起來(lái)再找到其中的無(wú)效或重復(fù)操作然后想辦法消除它們。這個(gè)思路比記住某個(gè)具體算法重要得多。6.4 一個(gè)值得嘗試的練習(xí)如果你想檢驗(yàn)自己是否真的理解了冒泡排序可以嘗試實(shí)現(xiàn)一個(gè)“雙向冒泡排序”也叫雞尾酒排序。它的思路是先從左到右把最大值推到末尾再?gòu)挠业阶蟀炎钚≈低频介_(kāi)頭如此反復(fù)。這個(gè)變體在某些數(shù)據(jù)分布下比標(biāo)準(zhǔn)冒泡排序快一些而且能幫你更深入地理解冒泡排序的交換機(jī)制。我在實(shí)際寫(xiě)代碼的過(guò)程中發(fā)現(xiàn)很多看似簡(jiǎn)單的算法真正動(dòng)手實(shí)現(xiàn)一遍之后理解程度和只看書(shū)完全不是一個(gè)級(jí)別。冒泡排序就是最好的例子。你以為自己懂了但一寫(xiě)代碼就發(fā)現(xiàn)邊界條件、循環(huán)變量、交換邏輯處處都是細(xì)節(jié)。把這些細(xì)節(jié)都搞定了你的編程基本功也就扎實(shí)了。