【每日算法】專題十三_隊列 + 寬搜(bfs)

1. 算法思路

BFS 算法核心思路

BFS(廣度優先搜索)使用? 隊列(Queue)按層級順序遍歷圖或樹的節點。以下是 C++ 實現的核心思路和代碼模板:

算法框架

#include <queue>
#include <vector>
#include <unordered_set>  // 用于圖的去重// 樹的 BFS 層序遍歷
vector<vector<int>> bfs(TreeNode* root) {vector<vector<int>> result;if (!root) return result;queue<TreeNode*> q;q.push(root);  // 根節點入隊while (!q.empty()) {int level_size = q.size();  // 當前層的節點數vector<int> current_level;// 處理當前層的所有節點for (int i = 0; i < level_size; i++) {TreeNode* node = q.front();q.pop();  // 隊首出隊current_level.push_back(node->val);// 將子節點入隊if (node->left) q.push(node->left);if (node->right) q.push(node->right);}result.push_back(current_level);  // 添加當前層到結果}return result;
}

關鍵點

  1. 隊列操作

    • push():將節點加入隊尾
    • front():獲取隊首元素
    • pop():移除隊首元素
    • empty():判斷隊列是否為空
  2. 層級控制

    • 通過?level_size?記錄當前層的節點數,確保每層節點被單獨處理。
  3. 圖的 BFS(需去重)

?

void graphBFS(vector<vector<int>>& graph, int start) {queue<int> q;unordered_set<int> visited;q.push(start);visited.insert(start);while (!q.empty()) {int node = q.front();q.pop();// 處理當前節點cout << node << " ";// 遍歷所有鄰接節點for (int neighbor : graph[node]) {if (visited.find(neighbor) == visited.end()) {visited.insert(neighbor);q.push(neighbor);}}}
}

?

應用場景

  1. 二叉樹層序遍歷
  2. 無權圖最短路徑
int shortestPath(vector<vector<int>>& graph, int start, int target) {queue<pair<int, int>> q;  // {節點, 距離}unordered_set<int> visited;q.push({start, 0});visited.insert(start);while (!q.empty()) {auto [node, dist] = q.front();q.pop();if (node == target) return dist;for (int neighbor : graph[node]) {if (!visited.count(neighbor)) {visited.insert(neighbor);q.push({neighbor, dist + 1});}}}return -1;  // 無法到達
}
  1. 狀態擴展問題(如迷宮)

總結

  • 隊列選擇:C++ 中使用?std::queue?或?std::deque
  • 去重機制:圖的 BFS 必須用?unordered_set?避免重復訪問。
  • 層級處理:通過記錄每層節點數實現逐層遍歷。

2. 例題

2.1 N 叉數的層序遍歷

429. N 叉樹的層序遍歷 - 力扣(LeetCode)

?

核心思路:隊列驅動的層序遍歷

這段代碼實現了?N 叉樹的層序遍歷,核心思路是利用隊列的?FIFO(先進先出)特性?逐層處理節點。具體步驟如下:

  1. 初始化隊列:將根節點放入隊列。
  2. 循環處理每一層
    • 記錄當前層節點數?sz(即隊列當前長度)。
    • 創建當前層的臨時數組?tmp
    • 遍歷當前層所有節點
      • 從隊列取出節點,將值加入?tmp
      • 將該節點的所有子節點依次入隊(確保下一層節點被加入隊列尾部)。
    • 將當前層結果?tmp?加入最終結果?ret
  3. 返回結果:當隊列為空時,所有層處理完畢。

關鍵點

  • 層級控制:通過?sz?鎖定當前層的節點數,確保每次循環處理完一層的所有節點。
  • 隊列的作用
    • 出隊:處理當前層的節點。
    • 入隊:按順序存儲下一層的節點。
  • 適用于 N 叉樹:通過?children?數組處理任意數量的子節點。

復雜度分析

  • 時間復雜度:O (n),每個節點僅入隊 / 出隊一次。
  • 空間復雜度:O (n),最壞情況下隊列同時存儲一層的所有節點(例如滿二叉樹的最后一層)。
    vector<vector<int>> levelOrder(Node* root) {vector<vector<int>> ret;queue<Node*> q;if(root == nullptr) return ret;q.push(root);while(q.size()){int sz = q.size();vector<int> tmp;for(int i = 0; i < sz; ++i){Node* t = q.front();q.pop();tmp.push_back(t->val);for(Node* child : t->children)q.push(child);}ret.push_back(tmp);}return ret;}

2.2 二叉樹的鋸齒層序遍歷

103. 二叉樹的鋸齒形層序遍歷 - 力扣(LeetCode)

?

核心思路:基于隊列的 zigzag 層序遍歷(之字形遍歷)

這段代碼實現了二叉樹的?之字形層序遍歷(即相鄰層的節點順序相反:第一層從左到右,第二層從右到左,第三層再從左到右,以此類推),核心思路是利用?隊列控制層級?+?雙端隊列(deque)控制每層順序。具體步驟如下:

  1. 初始化隊列:將根節點入隊,用?flag?標記當前層的遍歷方向(初始為?true,表示從左到右)。
  2. 循環處理每一層
    • 記錄當前層節點數?sz(鎖定當前層的節點范圍)。
    • 創建雙端隊列?tmp(支持頭部和尾部插入,靈活控制順序)。
    • 遍歷當前層所有節點
      • 從隊列取出節點。
      • 根據?flag?決定插入方向:
        • flag = true?時,從尾部插入(push_back),保持左到右順序。
        • flag = false?時,從頭部插入(push_front),實現右到左順序。
      • 將節點的左右子節點依次入隊(確保下一層節點按正常順序存儲)。
    • 轉換并保存當前層結果:將?tmp?轉為?vector<int>?后加入最終結果?ret
    • 翻轉方向標記flag = !flag,下一層使用相反順序。
  3. 返回結果:隊列為空時,所有層處理完畢。

關鍵點

  • 隊列的作用:控制層級順序,確保按層依次處理節點(與普通層序遍歷一致)。
  • 雙端隊列的作用:通過頭部 / 尾部插入,在不改變子節點入隊順序的前提下,靈活反轉當前層的輸出順序。
  • 方向標記?flag:切換相鄰層的遍歷方向,實現 “之字形” 效果。

復雜度分析

  • 時間復雜度:O (n),每個節點僅入隊 / 出隊一次,雙端隊列的插入操作是 O (1)。
  • 空間復雜度:O (n),隊列和雙端隊列最多同時存儲一層的所有節點。
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {vector<vector<int>> ret;queue<TreeNode*> q;if(root == nullptr) return ret;q.push(root);bool flag = true;while(q.size()){int sz = q.size();deque<int> tmp;for(int i = 0; i < sz; ++i){TreeNode* t = q.front();q.pop();if(flag) // 左向右tmp.push_back(t->val);else // 右向左tmp.push_front(t->val);if(t->left)q.push(t->left);if(t->right)q.push(t->right);}ret.push_back(vector<int>{tmp.begin(), tmp.end()});flag = !flag;} return ret;}

2.3 二叉樹最大寬度

662. 二叉樹最大寬度 - 力扣(LeetCode)

核心思路:二叉樹最大寬度計算(層序遍歷 + 節點編號)

這段代碼實現了計算二叉樹的最大寬度(即任意層的最左節點和最右節點之間的跨度,包含空節點),核心思路是通過層序遍歷 + 節點編號來確定每層的寬度。具體步驟如下:

  1. 隊列初始化:用?vector<pair<TreeNode*, unsigned int>>?存儲節點及其編號(根節點編號為?0)。編號規則為:若父節點編號為?x,則其左子節點編號為?2x,右子節點編號為?2x+1

  2. 循環處理每一層

    • 計算當前層寬度:當前層的寬度為隊列中首尾節點的編號之差 + 1(即?x2 - x1 + 1),更新全局最大寬度?ret
    • 生成下一層節點:遍歷當前層所有節點,將每個節點的子節點(若存在)及其編號加入臨時隊列?tmp
      • 左子節點:編號為?2 * x
      • 右子節點:編號為?2 * x + 1
    • 更新隊列:用臨時隊列?tmp?替換當前隊列?q,進入下一層循環。
  3. 返回結果:遍歷完所有層后,ret?即為二叉樹的最大寬度。

關鍵點

  • 節點編號:通過編號計算每層寬度,避免直接統計節點數(處理空節點的關鍵)。
  • 層序遍歷:確保按層處理節點,計算每層的首尾跨度。
  • 容器選擇
    • 方法一:用?vector?存儲每層節點,遍歷后整體替換(更高效)。
    • 方法二:用?erase?刪除隊首元素(性能較差,因?vector?的頭部刪除是 O (n))。
  • 數據類型:使用?unsigned int?避免編號溢出(某些極端二叉樹的編號可能非常大)。

復雜度分析

  • 時間復雜度:O (n),每個節點僅處理一次。
  • 空間復雜度:O (n),隊列最多存儲一層的所有節點。

?

    int widthOfBinaryTree(TreeNode* root) {int ret = 0;vector<pair<TreeNode*, unsigned int>> q; // 用vector可以進行下標訪問,取第一個和最后一個數if(root == nullptr) return ret;q.push_back({root, 0});while(q.size()){// 計算每層的最大寬度auto& [y1, x1] = q[0];auto& [y2, x2] = q.back();ret = max(ret, (int)(x2 - x1 + 1));// 層序遍歷// 法一vector<pair<TreeNode*, unsigned int>> tmp;for(auto& [y3, x3] : q){if(y3->left)tmp.push_back({y3->left, 2 * x3});if(y3->right)tmp.push_back({y3->right, 2 * x3 + 1});}// 法二,注意的是頻繁的頭部刪除應該選用deque做容器n(1)//int sz = q.size();// while(sz--)// {//     auto [y3, x3] = q[0];//     q.erase(q.begin());//     if(y3->left)//         q.push_back({y3->left, 2 * x3});//     if(y3->right)//         q.push_back({y3->right, 2 * x3 + 1});// }q = tmp;}return ret;}

?

2.4 在每個樹行中找最大值

515. 在每個樹行中找最大值 - 力扣(LeetCode)

核心思路:二叉樹每層最大值(層序遍歷 + 分組統計)

這段代碼實現了找出二叉樹每一層的最大值,核心思路是利用隊列進行層序遍歷,并在每一層遍歷時維護當前層的最大值。具體步驟如下:

  1. 隊列初始化:將根節點加入雙端隊列?q

  2. 循環處理每一層

    • 初始化當前層最大值?ma?為?INT_MIN
    • 鎖定當前層節點數?sz(即隊列當前長度)。
    • 遍歷當前層所有節點
      • 取出隊首節點,更新?ma?為?ma?和當前節點值的較大值。
      • 將該節點的左右子節點(若存在)加入隊列尾部。
      • 從隊列頭部移除當前節點(確保處理完所有當前層節點)。
    • 當前層遍歷結束后,將?ma?加入結果數組?ret
  3. 返回結果:遍歷完所有層后,ret?即為每層的最大值數組。

關鍵點

  • 層級控制:通過?sz?鎖定當前層的節點數,確保每次循環處理完一層的所有節點。
  • 最大值維護:在遍歷每層節點時,動態更新當前層的最大值?ma
  • 隊列操作
    • 入隊:將下一層的節點加入隊列尾部。
    • 出隊:從隊列頭部取出并移除當前層的節點。

復雜度分析

  • 時間復雜度:O (n),每個節點僅入隊 / 出隊一次。
  • 空間復雜度:O (n),最壞情況下隊列同時存儲一層的所有節點(例如滿二叉樹的最后一層)。
    vector<int> largestValues(TreeNode* root) {vector<int> ret;if(root == nullptr) return ret;deque<TreeNode*> q;q.push_back(root);while(q.size()){int ma = INT_MIN;// deque<TreeNode*> tmp;// for(auto n : q)// {//     ma = max(ma, n->val);//     if(n->left)//         tmp.push_back(n->left);//     if(n->right)//         tmp.push_back(n->right);// }// q = tmp;int sz = q.size();while(sz--){ma = max(ma, (q.front()->val));// TreeNode* t = q.top();// q.pop();if(q.front()->left)q.push_back(q.front()->left);if(q.front()->right)q.push_back(q.front()->right);q.pop_front();}ret.push_back(ma);}return ret;}

?

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

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

相關文章

【動手實驗】發送接收窗口對 TCP傳輸性能的影響

環境準備 服務器信息 兩臺騰訊云機器 t04&#xff08;172.19.0.4&#xff09;、t11&#xff08;172.19.0.11&#xff09;&#xff0c;系統為 Ubuntu 22.04&#xff0c;內核為 5.15.0-139-generic。默認 RT 在 0.16s 左右。 $ ping 172.19.0.4 PING 172.19.0.4 (172.19.0.4) …

28、鴻蒙Harmony Next開發:不依賴UI組件的全局氣泡提示 (openPopup)和不依賴UI組件的全局菜單 (openMenu)、Toast

目錄 不依賴UI組件的全局氣泡提示 (openPopup) 彈出氣泡 創建ComponentContent 綁定組件信息 設置彈出氣泡樣式 更新氣泡樣式 關閉氣泡 在HAR包中使用全局氣泡提示 不依賴UI組件的全局菜單 (openMenu) 彈出菜單 創建ComponentContent 綁定組件信息 設置彈出菜單樣…

讓老舊醫療設備“聽懂”新語言:CAN轉EtherCAT的醫療行業應用

在醫療影像設備的智能化升級中&#xff0c;通信協議的兼容性常成為工程師的“痛點”。例如&#xff0c;某醫院的移動式X射線機采用CAN協議控制機械臂&#xff0c;而主控系統基于EtherCAT架構。兩者協議差異導致數據延遲高達5ms&#xff0c;影像定位精度下降&#xff0c;甚至影響…

ubuntu基礎搭建

ubuntu上docker的搭建 https://vulhub.org/zh 網站最下面找到開始使用&#xff0c;有搭建的命令//安裝docker&#xff0c;連接失敗多試幾次 curl -fsSL https://get.docker.com | sh //驗證Docker是否正確安裝&#xff1a; docker version //還要驗證Docker Compose是否可用&am…

動態規劃 + DFS + 記憶化!Swift 解 LeetCode 329 的實戰筆記

文章目錄摘要描述題解答案題解代碼分析代碼解析示例測試及結果時間復雜度空間復雜度總結摘要 這篇文章帶你用 Swift 實戰一道非常經典的 DFS 記憶化搜索題目 —— LeetCode 329《矩陣中的最長遞增路徑》。看似一個簡單的“走格子”游戲&#xff0c;實則考察了搜索順序、剪枝策…

046_局部內部類與匿名內部類

一、局部內部類&#xff08;Local Inner Class&#xff09; 1.1 定義與基本概念 局部內部類是定義在方法、構造器或代碼塊內部的類&#xff0c;其作用域僅限于所在的局部范圍&#xff08;定義它的方法、構造器或代碼塊&#xff09;&#xff0c;超出該范圍則無法訪問。 它的核心…

Jenkins Pipeline 中使用 JsonSlurper 報錯:cannot find current thread

Jenkins Pipeline 中使用 JsonSlurper 報錯&#xff1a;cannot find current thread&#x1f31f; 背景? 問題重現&#x1f9e0; 原因解析&#xff1a;CPS 與非 CPS 安全方法沖突? 解決方案一&#xff1a;使用 NonCPS 注解&#xff08;經典方案&#xff09;? 解決方案二&…

Go 語言循環語句詳解

Go 語言循環語句詳解 在編程語言中&#xff0c;循環語句是實現重復執行某些代碼塊的關鍵元素。Go 語言作為現代編程語言之一&#xff0c;提供了多種循環結構來滿足不同的編程需求。本文將詳細講解 Go 語言中的循環語句&#xff0c;包括 for、while 和 goto 語句&#xff0c;幫助…

day30——零基礎學嵌入式之進程間通信1.0

一、進程間通信7種方式1.傳統的進程間通信方式&#xff08;1&#xff09;管道①無名管道&#xff1a;②有名管道&#xff1a;&#xff08;2&#xff09;③信號&#xff08;3&#xff09;system Ⅴ 》系統Ⅴ 進程間通信方式 inner Process Comunication④共享內存 &#xff…

408考研逐題詳解:2010年第33題——網絡體系結構

2010年第33題 下列選項中&#xff0c;不屬于網絡體系結構所描述的內容是&#xff08; &#xff09; A. 網絡的層次 \qquad B. 每層使用的協議 \qquad C. 協議的內部實現細節 \qquad D. 每層必須完成的功能 解析 本題屬于計算機網絡基礎知識的范疇&#xff0c;考查網絡體系結構…

VR 遠程系統的沉浸式協作體驗?

在傳統的遠程協作中&#xff0c;團隊成員往往通過二維的視頻畫面進行交流&#xff0c;這種方式雖然能實現基本的溝通&#xff0c;但缺乏真實感和互動性。而 VR 遠程系統的出現&#xff0c;徹底改變了這一局面。戴上 VR 設備&#xff0c;員工們仿佛置身于同一個真實的辦公室空間…

記錄DataGrip 2025.1.3破解失敗后,無法重啟問題修復

記錄DataGrip 2025.1.3破解失敗后&#xff0c;無法重啟問題修復安裝過程復盤異常場景解決方式總結安裝過程 在官網下載了最新版本2025.1.3。安裝成功后&#xff0c;使用30天試用方式&#xff0c;打開datagrip。 復盤異常場景 網上搜索破解教程進行破解。找了一個需要現在ja…

私有服務器AI智能體搭建配置選擇記錄

在搭建私有服務器上的AI智能體時&#xff0c;需要從多個方面進行選擇和規劃&#xff0c;以確保系統性能、安全性、可擴展性等方面滿足需求。1. 硬件選擇 服務器配置&#xff1a; CPU&#xff1a;選擇高性能多核CPU&#xff08;如Intel Xeon或AMD EPYC系列&#xff09;&#xff…

SDC Specical check setting的描述 - false path

在上一篇文中描述了SDC的基本語法&#xff0c;其中關于時序異常約束并沒有進行詳細的描述&#xff0c;但是在正常的設計中&#xff0c;一般這種異常的設置反而是需要特別關注的&#xff0c;主要包括&#xff1a;1. 虛假路徑- false path不需要滿足任何時序要求的路徑&#xff1…

【Python練習】048. 編寫一個函數,實現簡單的命令行接口,接受用戶輸入并響應

048. 編寫一個函數,實現簡單的命令行接口,接受用戶輸入并響應 在 Python 中,可以通過 input() 函數創建一個簡單的命令行接口,接受用戶輸入并根據輸入內容進行響應。 示例代碼 def simple_command_line_interface():"""實現一個簡單的命令行接口,接受用…

軟件工廠語境下的知識系統選型:兼顧合規性與集成深度

在過去幾十年間&#xff0c;制造業從“工匠手作”邁向“工業流水線”&#xff0c;完成了生產效率的巨大飛躍。當軟件開發也面臨交付復雜性、合規要求與協作成本不斷上升的現實&#xff0c;“軟件工廠”的理念逐步興起。 在這場“開發現代化”的轉型中&#xff0c;知識管理被重新…

C語言-一維數組,二維數組

數組 數組的引入如果要在程序中保存一個人的年齡&#xff1f;如何保存&#xff1f; 答&#xff1a;創建一個基于int類型的變量&#xff0c;舉例&#xff1a;int age 22如果要在程序中保存一個人的三門課的成績&#xff1f;如何保存&#xff1f; 答&#xff1a;創建三個基于flo…

如何區別HTML和HTML5?

要區分 HTML&#xff08;通常指 HTML4 及更早版本&#xff09;和 HTML5&#xff0c;主要可以從以下關鍵方面進行比較&#xff1a;一、文檔聲明區別 <!-- HTML4 文檔聲明 --> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http:/…

Java實戰:實時聊天應用開發(附GitHub鏈接)

一、前置技術項目介紹&#xff1a; 項目為局域網溝通軟件&#xff0c;類似內網通&#xff0c;核心功能包括昵稱輸入、聊天界面展示在線人數&#xff08;實時更新&#xff09;、群聊&#xff0c;也可擴展私聊、登錄注冊、聊天記錄存儲等功能&#xff0c;結尾附GitHub鏈接。項目涉…

linux 的list_for_each_entry

linux的宏定義提高了代碼的簡潔性&#xff0c;但有時候的命名不夠完美。比如list_for_each_entry&#xff0c;看名字只知道是遍歷list&#xff0c;但一看里面的三個變量參數&#xff0c;有點懵逼。/*** list_for_each_entry - iterate over list of given type* pos: …