leetcode熱題——組合

組合

題目描述

給定兩個整數 n 和 k,返回范圍 [1, n] 中所有可能的 k 個數的組合。

你可以按 任何順序 返回答案。

示例 1:
輸入:n = 4, k = 2 輸出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4] ]

示例 2:
輸入:n = 1, k = 1 輸出:[ [1] ]

提示:
1 <= n <= 20
1 <= k <= n

思路構建

本題直接的解法當然是利用for循環暴力搜索,例如 k=2時,用兩個for循環就能得到結果,如下所示:

int n = 4;
for (int i = 1; i <= n; i++) {for (int j = i + 1; j <= n; j++) {cout << i << " " << j << endl;}
}

但一旦當k變得很大時就必須嵌套很多for循環,這顯然是不顯示的,而回溯法就用遞歸來解決嵌套層數的問題。

為了方便理解,我們可以將遞歸過程轉化為一個樹結構,如下所示:?

每次從集合中選取元素,可選擇的范圍隨著選擇的進行而收縮,調整可選擇的范圍。
圖中可以發現n相當于樹的寬度,k相當于樹的深度。
圖中每次搜索到了葉子節點,我們就找到了一個結果。

解題步驟

按照回溯三部曲的模板套路來進行:

1.遞歸函數的返回值以及參數
定義兩個全局變量,一個用來存放符合條件單一結果,一個用來存放符合條件結果的集合。

vector<vector<int>> result; // 存放符合條件結果的集合
vector<int> path; // 用來存放符合條件結果

然后還需要一個參數,為int型變量startIndex,這個參數用來記錄本層遞歸的中,集合從哪里開始遍歷(集合就是[1,...,n] )。

startIndex 可以防止出現重復的組合。

vector<vector<int>> result; // 存放符合條件結果的集合
vector<int> path; // 用來存放符合條件單一結果
void backtracking(int n, int k, int startIndex)

2.回溯函數終止條件
path這個數組的大小如果達到k,說明我們找到了一個子集大小為k的組合了,在圖中path存的就是根節點到葉子節點的路徑。

此時用result二維數組,把path保存起來,并終止本層遞歸。

if (path.size() == k) {result.push_back(path);return;
}

3.單層搜索的過程
回溯法的搜索過程就是一個樹型結構的遍歷過程,可以看出for循環用來橫向遍歷,遞歸的過程是縱向遍歷。

for循環每次從startIndex開始遍歷,然后用path保存取到的節點i。

for (int i = startIndex; i <= n; i++) { // 控制樹的橫向遍歷path.push_back(i); // 處理節點backtracking(n, k, i + 1); // 遞歸:控制樹的縱向遍歷,注意下一層搜索要從i+1開始path.pop_back(); // 回溯,撤銷處理的節點
}

可以看出backtracking(遞歸函數)通過不斷調用自己一直往深處遍歷,總會遇到葉子節點,遇到了葉子節點就要返回。

backtracking的下面部分就是回溯的操作了,撤銷本次處理的結果。

執行過程示例

為了方便理解遞歸過程,以 n=4, k=2 為例,一步步拆解遞歸流程

第一層遞歸(startIndex=1,需選第 1 個元素)

for (i=1; i<=4; i++) {i=1:path.push_back(1) → path = [1]遞歸調用 backtracking(4, 2, 2)  // 下一層從 2 開始,選第 2 個元素(遞歸返回后)path.pop_back() → path = []i=2:path.push_back(2) → path = [2]遞歸調用 backtracking(4, 2, 3)(返回后)path.pop_back() → path = []i=3:path.push_back(3) → path = [3]遞歸調用 backtracking(4, 2, 4)(返回后)path.pop_back() → path = []i=4:path.push_back(4) → path = [4]遞歸調用 backtracking(4, 2, 5)(返回后)path.pop_back() → path = []
}

第二層遞歸(startIndex=2,需選第 2 個元素)

// 此時 path = [1],需選第 2 個元素,startIndex=2
for (i=2; i<=4; i++) {i=2:path.push_back(2) → path = [1,2](長度=k=2,加入結果集)遞歸終止,返回上層path.pop_back() → path = [1]i=3:path.push_back(3) → path = [1,3](加入結果集)返回上層,path.pop_back() → path = [1]i=4:path.push_back(4) → path = [1,4](加入結果集)返回上層,path.pop_back() → path = [1]
}

通過上述流程,最終會生成所有符合條件的組合:[1,2], [1,3], [1,4], [2,3], [2,4], [3,4]。

C++ 代碼實現

class Solution {
private:vector<vector<int>> result; // 存放符合條件結果的集合vector<int> path; // 用來存放符合條件結果void backtracking(int n, int k, int startIndex) {if (path.size() == k) {result.push_back(path);return;}for (int i = startIndex; i <= n; i++) {path.push_back(i); // 處理節點backtracking(n, k, i + 1); // 遞歸path.pop_back(); // 回溯,撤銷處理的節點}}
public:vector<vector<int>> combine(int n, int k) {backtracking(n, k, 1);return result;}
};

復雜度分析

時間復雜度: O(n * 2^n)
空間復雜度: O(n)

剪枝優化

原代碼的 for 循環遍歷范圍是 i = startIndex 到 i <= n,但部分循環是無效的(剩余元素不足以選夠 k 個),可通過剪枝減少不必要的迭代。

剪枝原理
假設當前 path 中已有 m 個元素(m = path.size()),還需要選擇 k - m 個元素。 剩余可選元素從 i 到 n,共 n - i + 1 個元素。 為了能選夠 k - m 個元素,需滿足:n - i + 1 >= k - m → 變形得 i <= n - (k - m) + 1。

若 i 超過這個值,即使選擇 i,剩余元素也不夠,無需繼續循環。

來舉一個例子,n = 4,k = 4的話,那么第一層for循環的時候,從元素2開始的遍歷都沒有意義了,如下圖所示:?

所以,可以剪枝的地方就在遞歸中每一層的for循環所選擇的起始位置。

如果for循環選擇的起始位置之后的元素個數 已經不足 我們需要的元素個數了,那么就沒有必要搜索了。

優化過程

  • 已經選擇的元素個數:path.size();

  • 還需要的元素個數為: k - path.size();

  • 在集合n中至多要從該起始位置 : n - (k - path.size()) + 1,開始遍歷

為什么有個+1呢,因為包括起始位置,我們要是一個左閉的集合。

舉個例子,n = 4,k = 3, 目前已經選取的元素為0(path.size為0),n - (k - 0) + 1 即 4 - ( 3 - 0) + 1 = 2。

所以優化之后的for循環是

for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) // i為本次搜索的起始位置

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

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

相關文章

暑期算法訓練.13

目錄 57 力扣14最長公共前綴 57.1 題目解析&#xff1a; 57.2 算法思路 57.3 代碼演示&#xff1a; ?編輯 57.4 總結反思&#xff1a; 58 力扣 5最長回文字符串 58.1 題目解析&#xff1a; ?編輯 58.2 算法思路&#xff1a; 58.3 代碼演示&#xff1a; ?編輯 …

四、Portainer圖形化管理實戰與Docker鏡像原理

作者&#xff1a;IvanCodes 日期&#xff1a;2025年8月2日 專欄&#xff1a;Docker教程 一、Portainer 安裝與基礎使用教程 Portainer 是一個輕量級、功能強大的Docker圖形化管理界面 (GUI)。它能讓你通過簡單的Web界面來管理和監控你的Docker容器、鏡像、卷、網絡等資源&…

網絡爬蟲(python)入門

一、網絡爬蟲介紹 網絡爬蟲&#xff08;Web Crawler&#xff09;是一種自動抓取互聯網信息的程序&#xff0c;它能夠高效地從海量網頁中提取有價值的數據。作為數據采集的利器&#xff0c;爬蟲技術在數據分析、搜索引擎、價格監控等領域有著廣泛應用。本文將帶你全面了解Pytho…

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

【Python系列Bug修復PyCharm控制臺pip install報錯】如何解決pip安裝報錯ModuleNotFoundError: No module named ‘plotnine’問題 一、摘要 在使用 PyCharm 進行 Python 開發時&#xff0c;常常需要通過 pip install 安裝第三方包。某天&#xff0c;你在終端或 PyCharm 控制…

語校網收錄東京語言學校150所:數據結構建模與工程實現全解

語校網收錄東京語言學校150所&#xff1a;數據結構建模與工程實現全解 一、為什么語言學校的信息抓取如此困難&#xff1f; 在日語教育領域&#xff0c;“語言學校”是一類極度碎片化的機構體系&#xff0c;尤其在東京地區&#xff0c;2025年時點上已合法設立的語言學校已超1…

【按下電源鍵后,電腦里發生了什么?——BIOS:啟動世界的“第一把鑰匙”】

當你按下電源鍵的瞬間&#xff0c;電腦從一片死寂中“蘇醒”。但你是否想過&#xff1a;是什么讓屏幕亮起、風扇轉動、硬件逐一激活&#xff1f; 這背后&#xff0c;有一個隱藏在主板上的“小程序”在默默掌控全局——它就是 BIOS&#xff08;Basic Input/Output System&#x…

局域網五子棋工具 多人對戰無限制

軟件介紹 今天推薦一款經典的PC端五子棋游戲——GoBang&#xff0c;綠色免安裝版本&#xff0c;完全免費&#xff0c;即開即用&#xff0c;輕松享受對弈樂趣。 游戲模式 軟件提供三種對戰模式&#xff1a;人人對戰、人機對抗以及局域網聯機游戲&#xff0c;滿足不同玩家的社…

分布式彈幕系統設計

需求:分布式彈幕廣播分布式方案1:適用redis 發布訂閱來進行不同ws服務器之間的通信優點:適用小系統方案2:對ws服務器進行一致性hash獲取ws服務的接入點優點:大型系統缺點:視頻連接不均勻挑戰點:廣播速度聚合廣播和線程池來進行優化

夢幻花瓣雨

1. 花瓣設計四種花瓣類型&#xff1a;創建了四種不同形狀和顏色的花瓣&#xff08;粉紅、淡紫、淺粉和藍綠色&#xff09;自然形態&#xff1a;使用CSS漸變和復雜邊框半徑模擬真實花瓣的不規則形狀柔和陰影&#xff1a;為花瓣添加微妙的陰影增強立體感2. 動畫效果物理模擬&…

React 閉包陷阱及解決方案與 React 16/17/18 版本區別

一、React 閉包陷阱詳解1. 什么是閉包陷阱React 閉包陷阱是指在函數組件中使用 Hook&#xff08;特別是 useEffect 和 useCallback&#xff09;時&#xff0c;由于閉包特性導致訪問到舊的 state 或 props 值&#xff0c;而非最新值的現象。2. 典型場景示例function Counter() {…

[BJDCTF2020]EasySearch

首先嘗試了一下sql注入&#xff0c;但是沒有找到不同回顯。直接用sqlmap掃描一下&#xff0c;因為這邊用的是POST請求&#xff0c;所以需要抓包將請求復制到txt文件中然后使用命令sqlmap -p bp.txt。也沒有發現注入漏洞。 再進行目錄掃描試試&#xff1a; [02:33:43] 403 - …

【Linux】基本指令的使用 and 面試常問

1、man 指令使用方法&#xff1a;man Linux指令。功能&#xff1a;相當于字典&#xff0c;查找指令的用法。常用選項&#xff1a;-k&#xff1a;根據關鍵字搜索聯機幫助。num&#xff1a;只在第num章節查找。-a&#xff1a;將所有章節的都顯示出來&#xff0c;比如man printf它…

零基礎 “入坑” Java--- 十六、字符串String 異常

文章目錄一、String1.字符串的不可變性2.字符串的修改3.StringBuilder和StringBuffer4.【字符串練習】4.1 字符串中的第一個唯一字符4.2 字符串最后一個單詞的長度4.3 驗證回文串二、異常1.初識異常2.異常的分類3.異常的處理4.異常處理流程總結5.自定義異常在上一章節中&#x…

梯度下降在大模型訓練中的作用與實現

梯度下降&#xff08;Gradient Descent&#xff09;是深度學習中最核心的優化算法之一。大模型&#xff08;如GPT、BERT&#xff09;在訓練時需要優化數十億甚至上千億的參數&#xff0c;而梯度下降及其變體&#xff08;如SGD、Adam&#xff09;正是實現這一優化的關鍵工具。它…

【JVS更新日志】開源框架、APS排產、企業計劃、物聯網、邏輯引擎7.30更新說明!

項目介紹 JVS是企業級數字化服務構建的基礎腳手架&#xff0c;主要解決企業信息化項目交付難、實施效率低、開發成本高的問題&#xff0c;采用微服務配置化的方式&#xff0c;提供了低代碼數據分析物聯網的核心能力產品&#xff0c;并構建了協同辦公、企業常用的管理工具等&…

Eclipse中導入新項目,右鍵項目沒有Run on Server,Tomcat的add and remove找不到項目

原因分析沒有勾選Dynamic Web Module、Java、JavaScriptDynamic Web Module版本問題解決方法Eclipse中右鍵項目選擇Properties左側點擊project facets勾選Dynamic Web Module、Java、JavaScript&#xff0c;注意Dynamic Web Module版本問題,要和tomcat版本對應。- Dynamic Web …

IntelliJ IDEA 2025系列通用軟件安裝教程(Windows版)

前言 JetBrains系列開發工具&#xff08;如IntelliJ IDEA、PyCharm、WebStorm等&#xff09;是程序員們非常喜愛的集成開發環境。2025年最新版本帶來了更多強大的功能和改進。本教程將詳細介紹如何在Windows系統上安裝JetBrains 2025系列軟件。 最近挖到一個寶藏級人工智能學習…

烏鶇科技前端二面

1. 你能給我介紹一下你參與的重要項目&#xff0c;并重點介紹一下做的內容?通俗解釋&#xff1a; 挑一個你覺得最拿得出手、技術含量最高的項目&#xff0c;說說這個項目是干什么的&#xff08;比如一個電商網站、一個后臺管理系統&#xff09;&#xff0c;你在里面具體負責了…

《c++面向對象入門與實戰》筆記

前年的書&#xff0c;翻出來整理一下7章.指針指針 sizeof為4*指針 sizeof為 所指類型的sizeof注意free后置空&#xff0c;避免野指針11章.類

easyExcel生成多個sheet的動態表頭的實現

在使用 EasyExcel 實現“多個 Sheet 且每個 Sheet 表頭是動態的”需求時&#xff0c;思路如下&#xff1a;? 實現思路概述 EasyExcel 的 ExcelWriter 支持多個 Sheet 寫入。每個 Sheet&#xff1a; 使用 WriteSheet 創建&#xff1b;可以綁定一個動態生成的表頭 List<List&…