數據結構:冒泡排序 (Bubble Sort)

目錄

從最簡單的操作開始

如何利用這個原子操作實現一個具體的小目標?

我們來手動模擬一下:

如何從一個小目標擴展到最終目標?

代碼的逐步完善

第一階段:定義函數框架和我們需要的“原子操作”

第二階段:實現“多趟”的邏輯(外層循環)

第三階段:實現每一趟的“比較交換”邏輯(內層循環)

思考與優化


從最簡單的操作開始

📌我們的目標: 將一個無序的數組,例如 [5, 1, 4, 2, 8],變成有序的 [1, 2, 4, 5, 8]

面對這個任務,最直接、最“笨”的思考方式是什么?不是去想什么宏大的整體策略,而是思考:

我能做的最小的、能讓數組變得“更”有序一點點的操作是什么?

這個最小的操作就是:只看相鄰的兩個元素

比如,我們先看 [5, 1]。在最終排好序的數組里,1 肯定在 5 的前面。而現在它們的順序是錯的。怎么辦?很簡單:把它們換過來!?

[5, 1, 4, 2, 8] -> 交換 51 -> [1, 5, 4, 2, 8]

這個 “比較相鄰元素,如果順序錯了就交換” 的操作,就是冒泡排序算法最核心的原子操作。它非常簡單,但通過重復這個簡單的操作,我們就能完成整個排序的宏偉目標。


如何利用這個原子操作實現一個具體的小目標?

我們不能漫無目的地到處交換。我們需要一個策略。與其想著如何把整個數組排好序,不如先定一個更小、更容易實現的目標:

“我們能否通過這個原子操作,把數組中最大的那個元素,放到它最終應該在的位置上?”

答案是可以的。最大的元素最終應該在數組的最右邊。我們怎么把它“弄”過去呢?

我們可以從數組的左邊開始,一路向右,不停地執行我們的“原子操作”。

我們來手動模擬一下:

假設數組是 arr = [5, 1, 4, 2, 8],長度 n = 5

1. 比較 arr[0]arr[1]:

  • [ **5, 1** , 4, 2, 8]

  • 5 > 1,順序錯了,交換。

  • 數組變為: [ **1, 5** , 4, 2, 8]

2. 比較 arr[1]arr[2]:

  • [1, **5, 4** , 2, 8]

  • 5 > 4,順序錯了,交換。

  • 數組變為: [1, **4, 5** , 2, 8]

3. 比較 arr[2]arr[3]:

  • [1, 4, **5, 2** , 8]

  • 5 > 2,順序錯了,交換。

  • 數組變為: [1, 4, **2, 5** , 8]

4. 比較 arr[3]arr[4]:

  • [1, 4, 2, **5, 8** ]

  • 5 < 8,順序是正確的,什么都不做。

  • 數組保持: [1, 4, 2, 5, 8]

經過這樣從左到右的一整輪操作,我們觀察結果 [1, 4, 2, 5, 8]。我們成功了嗎?

是的!最大的元素 8 已經被我們“護送”到了數組的最末端,也就是它最終應該在的位置。

這個過程就像水中的氣泡,最大的那個總是最先“咕嚕咕嚕”地冒到水面。這就是“冒泡排序”這個名字的由來。我們把這樣一整輪從頭到尾的比較交換過程,稱為一趟 (Pass)


如何從一個小目標擴展到最終目標?

我們已經完成了一趟,成功地將n個元素中最大的一個歸位了。現在數組是 [1, 4, 2, 5, | 8] (用 | 把已歸位的元素隔開)。

接下來怎么辦?

很簡單,我們把已經歸位的 8 忽略掉,把前面的 [1, 4, 2, 5] 看作是一個規模更小的新問題

我們只需要對這個新的、長度為 n-1 的數組,重復剛才一模一樣的操作

👉 我們來模擬第二趟:

  1. 比較 arr[0]arr[1]: [ **1, 4** , 2, 5, | 8] -> 1 < 4,不交換。

  2. 比較 arr[1]arr[2]: [1, **4, 2** , 5, | 8] -> 4 > 2,交換 -> [1, 2, 4, 5, | 8]

  3. 比較 arr[2]arr[3]: [1, 2, **4, 5** , | 8] -> 4 < 5,不交換。

第二趟結束后,數組變為 [1, 2, 4, | 5, 8]。看,現在次大的元素 5 也歸位了!

📈 這個邏輯可以一直重復下去。

  • 第三趟,對 [1, 2, 4] 操作,會把 4 歸位。

  • 第四趟,對 [1, 2] 操作,會把 2 歸位。

  • 當只剩下 1 的時候,它自然就在正確的位置了。

我們需要多少趟呢?對于一個有n個元素的數組,最多需要 n-1 趟,就能把所有元素都排好序。

這個“重復執行”的思路,在編程里天然就對應著循環?。


代碼的逐步完善

現在,我們把上面的推導翻譯成代碼。

第一階段:定義函數框架和我們需要的“原子操作”

我們知道肯定需要一個排序函數,并且排序過程中必然要用到“交換”這個原子操作。

// C/C++ 中,要在函數內部修改外部變量的值,需要使用指針
void swap(int* a, int* b) {int temp = *a;*a = *b;*b = temp;
}// 冒泡排序函數的整體框架
// arr 是要排序的數組,n 是數組的長度
void bubbleSort(int arr[], int n) {// 排序邏輯將在這里實現
}

第二階段:實現“多趟”的邏輯(外層循環)

根據我們的推導,需要重復執行 n-1 趟排序。所以我們需要一個循環來控制這個“趟數”。

void bubbleSort(int arr[], int n) {// 這個外層循環控制總共需要多少“趟” (Pass)// i 表示已經有多少個元素在數組末尾歸位了for (int i = 0; i < n - 1; ++i) {// 在這里實現每一趟具體的比較和交換邏輯}
}

第三階段:實現每一趟的“比較交換”邏輯(內層循環)

在每一趟中,我們從數組的第一個元素開始,向后兩兩比較。 關鍵點:比較到哪里為止?

  • 第一趟 (i=0),n個元素都未排序,要比較 n-1 次,檢查到 arr[n-2]arr[n-1] 為止。

  • 第二趟 (i=1),最后1個元素已歸位,只需要處理前面 n-1 個元素,比較 n-2 次,檢查到 arr[n-3]arr[n-2] 為止。

  • i 趟,最后 i 個元素已歸位,比較范圍是 n-1-i 次。

這個邏輯正好可以用另一個循環,也就是內層循環來實現。

#include <iostream> // 為了方便打印結果void swap(int* a, int* b) {int temp = *a;*a = *b;*b = temp;
}void bubbleSort(int arr[], int n) {// 外層循環,控制“趟數”for (int i = 0; i < n - 1; ++i) {// 內層循環,控制每一趟中的“比較與交換”// 范圍是 n - 1 - i,因為每過一趟,末尾就多一個排好的數for (int j = 0; j < n - 1 - i; ++j) {// 這就是我們的“原子操作”if (arr[j] > arr[j+1]) {swap(&arr[j], &arr[j+1]);}}// 我們可以加一句打印,來觀察每一趟之后的結果std::cout << "第 " << i + 1 << " 趟排序后: ";for(int k=0; k<n; ++k) std::cout << arr[k] << " ";std::cout << std::endl;}
}

至此,一個可以正確工作的冒泡排序算法就完成了。它完美地復現了我們從第一性原理出發的推導過程。

復雜度分析

最好情況(數組有序):只需要一趟掃描,比較 n?1?次,沒有交換 → 時間復雜度: O(n)

最壞情況(數組逆序):每次都需要比較并交換 →?時間復雜度: O(n^2)

空間復雜度:冒泡排序是 原地排序,只需常數個輔助空間 →?O(1)

穩定性

冒泡排序是 穩定的

  • 原因:只有在 a[j] > a[j+1] 時才交換,如果相等,不動。

  • 因此相同元素的前后順序不會被破壞。


思考與優化

我們的算法已經能用了,但它是不是最聰明的?有沒有什么情況下它做了“無用功”?

💡設想一個情況:arr = [1, 2, 3, 5, 4]

  • 第一趟后,45交換,數組變為 [1, 2, 3, 4, 5]

  • 此時數組已經完全有序了。

但是,我們上面的代碼并不知道這一點。它會繼續傻傻地執行第二趟、第三趟、第四趟,雖然在這些趟中一次交換都不會發生。這顯然是浪費。

優化的第一性原理: 如果我們發現某一趟從頭到尾走下來,一次交換都沒有發生,這說明了什么?

這說明數組中每一個元素都已經不大于它的后一個元素了,也就是說,整個數組已經完全有序了!

那么,我們就可以提前結束排序,而不必執行后面多余的趟數。

最終階段:完善代碼,加入優化

我們可以在每一趟開始前,設置一個標志位 swapped = false。如果在這一趟中發生了交換,就把它設為 true

一趟結束后,檢查這個標志位。如果它仍然是 false,說明沒發生任何交換,我們就可以直接 break 退出外層循環。

// 優化后的冒泡排序
void bubbleSortOptimized(int arr[], int n) {// 外層循環,控制“趟數”for (int i = 0; i < n - 1; ++i) {bool swapped = false; // 設立標志位// 內層循環for (int j = 0; j < n - 1 - i; ++j) {if (arr[j] > arr[j+1]) {swap(&arr[j], &arr[j+1]);swapped = true; // 只要發生一次交換,就將標志位置為true}}// 檢查標志位:如果在一整趟中都沒有發生交換,說明已經有序if (swapped == false) {std::cout << "在第 " << i + 1 << " 趟后提前結束。" << std::endl;break; // 退出外層循環}std::cout << "第 " << i + 1 << " 趟排序后: ";for(int k=0; k<n; ++k) std::cout << arr[k] << " ";std::cout << std::endl;}
}

這樣,我們就從最基本的“交換相鄰錯誤元素”這個原子操作出發,通過“完成小目標 -> 重復操作 -> 發現冗余 -> 加入判斷”這一系列邏輯嚴謹的推導,最終構建出了一個完整且經過優化的冒泡排序算法。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/920915.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/920915.shtml
英文地址,請注明出處:http://en.pswp.cn/news/920915.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

教育項目管理工具新趨勢:可視化與自動化如何提升效率?

課程項目不同于普通商業項目&#xff0c;它涉及 “教研設計→內容開發→師資準備→市場推廣→學員服務” 全鏈路&#xff0c;環節多、角色雜、周期跨度大。傳統的 Excel 表格、口頭溝通不僅難以追蹤進度&#xff0c;更易造成信息斷層。而看板工具憑借 “可視化流程、輕量化協作…

計算兩個二值圖像的交集計算交點數量的基礎上,進一步使用 DBSCAN 算法對交點進行聚

好的&#xff0c;如果你需要在計算交點數量的基礎上&#xff0c;進一步使用 DBSCAN 算法對交點進行聚類&#xff0c;以合并距離較近的點&#xff0c;可以按照以下步驟實現&#xff1a; 計算交點&#xff1a;使用 cv2.bitwise_and 計算兩個二值圖像的交集&#xff0c;并提取交點…

Linux中的IP命令詳解

華子目錄 1.ip命令是什么1.1ip命令的由來1.2ip命令的安裝包1.2ip選項&#xff08;基本不用&#xff09; 2.查看網絡信息2.1顯示全部網絡接口信息2.2顯示單個網絡接口信息2.3顯示單個接口狀態2.4查看路由表2.5查看arp緩存 3.設置網卡ip地址3.1啟用或停用網卡3.2設置默認網關3.3新…

如何解決pip安裝報錯ModuleNotFoundError: No module named ‘tox’問題

【Python系列Bug修復PyCharm控制臺pip install報錯】如何解決pip安裝報錯ModuleNotFoundError: No module named ‘tox’問題 摘要 在使用 PyCharm 2025 控制臺執行 pip install 命令時&#xff0c;開發者經常會遇到如下錯誤&#xff1a; ModuleNotFoundError: No module nam…

拆分TypeScript項目的學習收獲:處理編譯緩存和包緩存,引用本地項目,使用相對路徑

最近需要將工作中的一個TS包拆出一部分代碼&#xff0c;以便在多個團隊和項目中共享。原以為這會是一項特別簡單的工作&#xff0c;但是也花了兩天才大致拆成功。因此記錄一下&#xff0c;也給有類似需求的同學一點經驗。 所拆項目的大致功能&#xff1a;整個項目的結構大致分為…

瑞芯微RK3576平臺FFmpeg硬件編解碼移植及性能測試實戰攻略

本文介紹瑞芯微RK3576平臺&#xff0c;FFmpeg硬件編解碼移植及性能測試方法。 FFmpeg簡介與實測數據 FFmpeg簡介 FFmpeg是一套多媒體框架&#xff0c;能夠解碼、編碼、轉碼、復用、解復用、流、過濾和播放數字音頻、視頻&#xff0c;提供了錄制、轉換以及流化音視頻的完整解…

【網絡安全入門基礎教程】網絡安全零基礎學習方向及需要掌握的技能

最近總有同學問我&#xff0c;0基礎怎么學網絡安全&#xff1f;0基礎可以轉行做網絡安全嗎&#xff1f;網絡安全有哪些學習方向&#xff1f;每個方向需要掌握哪些技能&#xff1f;今天給大家簡單寫一下。 我的回答是先了解&#xff0c;再入行。 具體怎么做呢&#xff1f; 首…

Altium Designer中的Net-Tie:解決多網絡合并與電氣隔離的利器

Altium Designer中的Net-Tie:解決多網絡合并與電氣隔離的利器 在復雜的PCB設計中,我們常常會遇到一些特殊的電氣連接需求。例如,需要將兩個或多個邏輯上獨立但物理上需要連接的網絡(如不同電源域的GND)在特定點進行連接(單點連接),同時又要保持其網絡標識的獨立性。 …

計算機畢設項目 基于Python與機器學習的B站視頻熱度分析與預測系統 基于隨機森林算法的B站視頻內容熱度預測系統

&#x1f495;&#x1f495;作者&#xff1a;計算機源碼社 &#x1f495;&#x1f495;個人簡介&#xff1a;本人八年開發經驗&#xff0c;擅長Java、Python、PHP、.NET、Node.js、Spark、hadoop、Android、微信小程序、爬蟲、大數據、機器學習等&#xff0c;大家有這一塊的問題…

百勝軟件×OceanBase深度合作,賦能品牌零售數字化實踐降本增效

8月28日&#xff0c;由OceanBase主辦的“2025零售數據底座創新大會”在上海舉行。大會重磅發布了由愛分析、OceanBase攜手王歆、沈剛兩位行業專家聯合編制的《零售一體化云數據庫白皮書》。白皮書系統梳理了從“大促流量應對”到“AI應用落地”的全流程方法論&#xff0c;并為不…

2025年Java在中國開發語言排名分析報告

引言 在軟件定義世界的2025年&#xff0c;編程語言的戰略價值已超越工具屬性&#xff0c;成為產業數字化轉型的核心支撐與開發者思維模式的延伸載體。TIOBE指數作為全球技術市場變化的重要晴雨表&#xff0c;通過追蹤工程師分布、課程設置、供應商動態及搜索引擎數據&#xff0…

TDengine 日期時間函數 DAYOFWEEK 使用手冊

DAYOFWEEK 函數使用手冊 函數描述 DAYOFWEEK 函數用于返回指定日期是一周中的第幾天。該函數遵循標準的星期編號約定&#xff0c;返回值范圍為 1-7&#xff0c;其中&#xff1a; 1 星期日 (Sunday)2 星期一 (Monday)3 星期二 (Tuesday)4 星期三 (Wednesday)5 星期四 (T…

從RNN到BERT

目錄 序列模型簡介RNN循環神經網絡LSTM長短期記憶網絡Transformer架構BERT模型詳解實踐項目 序列模型簡介 什么是序列數據&#xff1f; 序列數據是按照特定順序排列的數據&#xff0c;其中元素的順序包含重要信息。常見的序列數據包括&#xff1a; 文本&#xff1a;單詞或字…

橢圓曲線的數學基礎

一、引言 橢圓曲線密碼學&#xff08;Elliptic Curve Cryptography, ECC&#xff09;是現代公鑰密碼學的核心工具之一。 相比傳統的 RSA&#xff0c;ECC 可以用 更短的密鑰長度 提供 同等甚至更高的安全性&#xff0c;因此被廣泛應用于區塊鏈、TLS、移動設備加密等場景。 要理解…

從能耗黑洞到精準智控:ASCB2智慧空開重構高校宿舍用電能效模型

隨著智慧校園建設不斷推進&#xff0c;校園宿舍的用電管理面臨著安全性、智能化與可視化的多重挑戰。傳統用電監控手段在數據采集、實時控制和故障響應方面存在明顯不足。安科瑞ASCB2系列物聯網斷路器通過集成多種智能感知、保護控制與通信手段&#xff0c;為高校宿舍提供了一種…

前端學習——JavaScript基礎

前面我們已經學習了前端代碼的骨架——HTML和前端美化工具——CSS。但是作為界面與客戶進行交互我們還需要一個語言工具——JavaScript。 因此實際上HTML、CSS、JavaScript三者是這樣的關系&#xff1a; HTML: 網頁的結構(骨) CSS: 網頁的表現(皮) JavaScript: 網頁的行為(魂) …

Ubuntu下的壓縮及解壓縮

一、Linxu 下常用的壓縮格式 Linux 下常用的壓縮擴展名有&#xff1a;.tar 、.tar.bz2、 .tar.gz 。 二、Windows 下 7ZIP 軟件的安裝 因為 Linux 下很多文件是 .bz2 &#xff0c; .gz 結尾的壓縮文件&#xff0c;因此需要在 windows 下安裝 7ZIP 軟件。 7-Zip 三、Ubuntu…

金融數據安全

安全框架金融數據生命周期是指金融業機構在開展業務和進行經營管理的過程中&#xff0c;對金融數據進行采集、 傳輸、存儲、使用、刪除、銷毀的整個過程。數據生命周期安全框架,遵循數據安全原則&#xff0c;以 數據安全分級為基礎&#xff0c;建立覆蓋數據生命周期全過程的安全…

Unity抖音小游戲快捷立項準備/改動

本文由 NRatel 歷史筆記整理而來&#xff0c;如有錯誤歡迎指正。 1、熟讀抖音接入文檔&#xff0c;記錄要點 Unity 小游戲接入指南_抖音開放平臺 2、創建Git倉庫&#xff0c;開通成員權限 美術目錄&#xff0c;對程序、美術、策劃全開 程序目錄&#xff0c;對程序全開、對部…

Labview使用modbus或S7與PLC通信

一、modbus 1.使用VI Package Manager (VIPM)安裝modbus庫 2.安裝好后如下顯示會有Modbus Library 3.Master API作為客戶端&#xff0c;如下有一個例程 4.Slave API作為服務端&#xff0c;如下有一個例程 上述兩個例程是通過IP 127.0.0.1可以互相通信的。數據是一直存在服務端…