dfs刷題排列問題 + 子集問題 + 組和問題總結

文章目錄

  • 一、排列問題
  • 全排列II
    • 題解
    • 代碼
  • 優美的排列
    • 題解
    • 代碼
  • 二、子集問題
  • 字母大小寫全排列
    • 題解
    • 代碼
  • 找出所有子集的異或總和再求和
    • 題解
    • 代碼
  • 三、組合問題
  • 電話號碼的字母組合
    • 題解
    • 代碼
  • 括號生成
    • 題解
    • 代碼
  • 組合
    • 題解
    • 代碼
  • 目標和
    • 題解
    • 代碼
  • 組合總和
    • 題解
    • 代碼
  • 總結

一、排列問題

全排列II

題目鏈接
在這里插入圖片描述

題解

1. 這題和全排列那題框架是一樣的,就是剪枝操作不一樣
2. 同一節點出現相同元素肯定會重復,所以把同一節點的相同元素剪掉
3. 同一個數只能出現一次,用check數組剪枝
分為兩種情況進行剪枝:
1、只關心不合法的分支:
不合法的進行跳過(剪枝)
check[i] == true || ( i != 0 &&nums[i] == nums[i-1] && check[i-1] == false)
這個點是已經使用過的,或者是這個點和前一個點是相同的并且前一個點沒有使用過,i != 0保證不越界
2、只關心合法的分支:
合法的分支才進行dfs
check[i] == false && (i == 0 || nums[i] != nums[i-1] || check[i] == true)
這個點沒有被使用過并且該點為第一個點肯定可以進行dfs,或者是該點和前一個點不相同也可以dfs,或者是該點和前一個點相同,但是前一個點上一層已經使用過了,這個點這層可以繼續使用,因為它們是用的不同位置

在這里插入圖片描述

代碼

class Solution 
{
public:vector<vector<int>> ret;vector<int> path;bool check[9];vector<vector<int>> permuteUnique(vector<int>& nums) {sort(nums.begin(),nums.end());dfs(nums);return ret;}void dfs(vector<int> nums){if(path.size() == nums.size()){ret.push_back(path);return;}// 如何把重復的數字剪掉for(int i = 0;i < nums.size();i++){// 合法的剪枝,不合法就不進行dfs// if((check[i] == false)&&// (i == 0||nums[i] != nums[i-1]||check[i-1] == true))// {//     check[i] = true;//     path.push_back(nums[i]);//     dfs(nums);//     // 恢復現場//     check[i] = false;//     path.pop_back();// }// 考慮不合法的剪枝,跳過不合法的剪枝if((check[i] == true)||(i != 0&&nums[i] == nums[i-1]&&check[i-1] == false))continue;check[i] = true;path.push_back(nums[i]);dfs(nums);// 恢復現場check[i] = false;path.pop_back();}}
};

優美的排列

題目鏈接
在這里插入圖片描述

題解

1. 畫出決策樹
2. 全局變量的設計:ret用來記錄優美排列的個數,check數組檢查是否可以剪枝,n設計成全局變量就不需要進行傳參了
3. 剪枝:第一種剪枝不能出現重復的數,第二種剪枝不滿足整除條件的
4. 回溯:如圖我們每個位置都要進行判斷,每個位置都會走一遍,遞歸完后進行恢復現場,把最后一位pop_back
5. 遞歸出口:當path路徑的長度等于n時為遞歸出口
6. for循環的i = 1開始是因為要遍歷所有的路徑,dfs中pos+1是因為此位置遍歷完會來到下一個位置進行遍歷,畫出決策樹就很清晰了

在這里插入圖片描述

代碼

class Solution
{
public:int n;int ret;bool check[16];vector<int> path;int countArrangement(int _n) {n = _n;dfs(1);return ret;}void dfs(int pos){if(path.size() == n){ret++;return;}for(int i = 1;i <= n;i++){if(pos % i == 0 || i % pos == 0){if(check[i] == false){check[i] = true;path.push_back(i);dfs(pos+1);// 恢復現場path.pop_back();check[i] = false;}} }}
};

二、子集問題

字母大小寫全排列

題目鏈接
在這里插入圖片描述

題解

1. 畫出決策樹
2. 全局變量:ret記錄最終的結果,path記錄每次的路徑
3. 剪枝:沒有剪枝
4. 回溯:到達葉子節點的時候記錄完進行回溯,pop_back最后一個位置的數來到上一層
5. 遞歸出口:pos位置為n時,是最后一個數據的下一個位置,為遞歸出口
6. 這題和選和不選基本上是一樣的,子集問題,pos這個位置變或者不變然后來到下一個位置,所以dfs(pos+1),變的情況為小寫字母時轉為大寫字母,大寫轉為小寫,不變就直接push

在這里插入圖片描述

代碼

class Solution 
{
public:vector<string> ret;string path;vector<string> letterCasePermutation(string s) {dfs(s,0);return ret;}void dfs(string s,int pos){// 為什么不能寫pos == s.size()if(pos == s.size()){ret.push_back(path);return;}char ch = s[pos];// 不變path.push_back(ch);dfs(s,pos+1);path.pop_back();// 變if(ch < '0' || ch > '9'){ch = change(ch);path.push_back(ch);dfs(s,pos+1);path.pop_back();}}char change(char ch){if(ch >= 'a' && ch <= 'z') ch -= 32;else ch += 32;return ch;}
};

找出所有子集的異或總和再求和

題目鏈接
在這里插入圖片描述

題解

1. 畫出決策樹
2. 全局變量:用sum記錄最終的結果,用path記錄一個集合的異或和
3. 剪枝:沒有剪枝
4. 回溯:每次異或當前元素就抵消掉這個元素了,然后回到上一層
5. 遞歸出口:沒有遞歸出口,每次把path加到sum中即可
6. for循環中每次從pos位置開始向后枚舉,避免重復,dfs(i+1),i+1就是數組中下一個位置的數,相當于剪枝了

在這里插入圖片描述

代碼

class Solution 
{
public:long long sum = 0;// 記錄全部路徑的異或和long long path = 0;// 記錄一條路徑的異或和int subsetXORSum(vector<int>& nums) {dfs(nums,0);return sum;}void dfs(vector<int> nums,int pos){sum += path;for(int i = pos;i < nums.size();i++){path ^= nums[i];dfs(nums,i+1);path ^= nums[i];// 不能使用pos代替i,如果進行回溯回來,// 進行循環,path始終異或nums[pos]}}
};

三、組合問題

電話號碼的字母組合

題目鏈接
在這里插入圖片描述

題解

1. 畫出決策樹
2. 全局變量:ret記錄最終的結果,path記錄每次的路徑,哈希表記錄下標的映射關系
3. 剪枝:沒有剪枝
4. 回溯:到達葉子節點時,pop_back最后一個元素,恢復現場
5. 遞歸出口:path的長度和給定的數組的長度相同
6. for循環把所有的數都枚舉出來了,所以i下標從0開始,dfs(pos+1),每個位置枚舉完跳到下一個位置繼續枚舉,這題主要是建立一個hash表記錄每次的電話號碼的數字對應的映射字符串

在這里插入圖片描述

代碼

class Solution 
{
public:vector<string> ret;string path;string hash[10] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};vector<string> letterCombinations(string digits) {if(digits.size() == 0) return ret;dfs(digits,0);   return ret;}void dfs(string digits,int pos){if(pos == digits.size()){ret.push_back(path);return;}for(int i = 0;i < hash[digits[pos] - '0'].size();i++){path.push_back(hash[digits[pos] - '0'][i]);dfs(digits,pos+1);// 恢復現場path.pop_back();}}
};

括號生成

題目鏈接
在這里插入圖片描述

題解

1. 畫出決策樹
2. 全局變量:path記錄每次的路徑,ret記錄最終的結果,left記錄左括號的數量,right記錄右括號的數量
3. 剪枝:第一種右括號的數量大于等于左括號的數量,第二中左括號的數量大于等于n的數量,開始的時候只能給左擴號,右括號剪枝,左右括號相等時,不能加入右括號,左括號大于n不符合結果,等于n時,再往下加就大于n,需要剪枝
4. 回溯:如果是左括號回溯,pop_back最后一個元素,left–,如果是右括號回溯,pop_back最后一個元素,right–
5. 遞歸出口:右括號的數量等于n時,為左右括號相等的最終結果

在這里插入圖片描述

代碼

class Solution 
{
public:vector<string> ret;string path;int left,right;vector<string> generateParenthesis(int n) {dfs(n);return ret;}void dfs(int n){if(right == n){ret.push_back(path);return ;}if(left < n){path.push_back('(');left++;dfs(n);path.pop_back();left--;}if(left > right){path.push_back(')');right++;dfs(n);path.pop_back();right--;}}
};

組合

題目鏈接
在這里插入圖片描述

題解

1. 畫出決策樹
2. 全局變量:path記錄每次的路徑,ret記錄最終的結果,k變為全局變量,不需要多傳一個參數
3. 剪枝:不能重復出現相同的數剪枝,長度不夠k也剪枝,這題可以從pos位置開始向后枚舉,dfs(i+1),i+1表示每次枚舉當前數的下一個位置的數,相當于進行了剪枝
4. 回溯:到達葉子節點后,每次pop_back最后一個數
5. 遞歸出口:path的長度等于k的大小

在這里插入圖片描述

代碼

class Solution 
{
public:vector<vector<int>> ret;vector<int> path;int k;vector<vector<int>> combine(int n, int _k) {k = _k;dfs(n,1);return ret;}void dfs(int n,int pos){if(path.size() == k){ret.push_back(path);return ;}for(int i = pos;i <= n;i++){path.push_back(i);dfs(n,i+1);path.pop_back();// 恢復現場}}
};

目標和

題目鏈接
在這里插入圖片描述

題解

1. 畫出決策樹
2. 全局變量:ret記錄最終的結果,target變為全局變量就少傳一個參數
3. 剪枝:沒有剪枝
4. 回溯:函數自動回溯,將path作為參數,記錄每條路徑的大小,回到上一層,path變為了沒有加減之前的path
5. 遞歸出口:最終的path等于target的大小,ret++
6. 這題就是每個數兩種情況,要么加,要么減

在這里插入圖片描述

代碼

class Solution 
{
public:int ret,target;int findTargetSumWays(vector<int>& nums, int _target) {target = _target;dfs(nums,0,0);return ret;}void dfs(vector<int>& nums,int pos,int path){if(pos == nums.size()){if(target == path)ret++;return;}// 加法,放在參數中函數幫我們自動恢復現場了dfs(nums,pos+1,path + nums[pos]);// 減法dfs(nums,pos+1, path - nums[pos]);}
};

組合總和

題目鏈接
在這里插入圖片描述

題解

解法一:每個pos位置填
1. 畫出決策樹
2. 全局變量:ret記錄最終的結果,path記錄每條路徑,target變為全局變量就不需要傳參target了
3. 剪枝:如果這條路徑的和大于target剪枝,如果選擇的路徑重復了剪枝,這種可以使用i = pos位置開始向后枚舉,dfs(i),i表示每次從當前這個數向后枚舉,不需要重復枚舉這個數前面的數,就避免了重復的路徑,達到了剪枝的效果
4. 回溯:sum在函數的參數中自動進行了回溯,pop_back最后一個元素,回到上一層
5. 遞歸出口:目標值等于記錄的路徑總和就找到一條路徑,加入ret中,并返回給上一層

在這里插入圖片描述

代碼

class Solution 
{
public:vector<vector<int>> ret;vector<int> path;int target;vector<vector<int>> combinationSum(vector<int>& candidates, int _target) {target = _target;dfs(candidates,0,0);return ret;}void dfs(vector<int> candidates,int pos,int sum){if(sum == target){ret.push_back(path);return;}// 枚舉完了所有的位置也不是target,這個總和比較小if(pos == candidates.size()) return;for(int i = pos;i < candidates.size();i++){if(sum > target) continue;path.push_back(candidates[i]);dfs(candidates,i,sum + candidates[i]);path.pop_back();}}
};

解法二:枚舉每個數的數量
1. 和解法一不一樣的地方就是dfs函數的實現不一樣

在這里插入圖片描述

class Solution 
{
public:vector<vector<int>> ret;vector<int> path;int target;vector<vector<int>> combinationSum(vector<int>& candidates, int _target) {target = _target;dfs(candidates,0,0);return ret;}void dfs(vector<int> candidates,int pos,int sum){if(sum == target){ret.push_back(path);return;}// 枚舉完了所有的位置也不是target,這個總和比較小if(pos == candidates.size() || sum > target) return;// 枚舉個數for(int k = 0;sum + k * candidates[pos] <= target;k++){if(k) path.push_back(candidates[pos]);dfs(candidates,pos+1,sum + k*candidates[pos]);}// 整塊恢復現場for(int k = 1;sum + k * candidates[pos] <= target;k++){path.pop_back();}}
};

總結

1. 最重要的就是畫出決策樹
2. 全局變量:一般是path記錄路徑,ret記錄各個路徑的結果
3. 剪枝:看題目分析和看決策樹
4. 回溯:一般是pop_back最后一個元素
5. 遞歸出口:看題目條件,或者是葉子節點

在這里插入圖片描述

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

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

相關文章

【Linux】VMware17 安裝 Ubuntu24.04 虛擬機

目錄 安裝教程 一、下載 Ubuntu 桌面版iso映像 二、安裝 VMware 三、安裝 Ubuntu 桌面版 VMware 創建虛擬機 掛載 Ubuntu ISO 安裝 Ubuntu 系統 安裝教程 一、下載 Ubuntu 桌面版iso映像 鏈接來自 清華大學開源軟件鏡像站 ISO文件地址&#xff1a;ubuntu-24.04.2-des…

CVPR2025 | 對抗樣本智能安全方向論文匯總 | 持續更新中~

匯總結果來源&#xff1a;CVPR 2025 Accepted Papers 若文中出現的 論文鏈接 和 GitHub鏈接 點不開&#xff0c;則說明還未公布&#xff0c;在公布后筆者會及時添加. 若筆者未及時添加&#xff0c;歡迎讀者告知. 文章根據題目關鍵詞搜索&#xff0c;可能會有遺漏. 若筆者出現…

PostgreSQL_數據回退,數據庫導出、導入

目錄 前置&#xff1a; 1 數據回退 1.1 代碼 1.2 pgAdmin4 中查看 1&#xff09;t_daily 2) t_stock_daily 2 數據庫導出、導入 前置&#xff1a; 本博文是一個系列。在本人“數據庫專欄”-》“PostgreSQL_”開頭的博文。 1 數據回退 上一節“PostgreSQL_數據下載并…

golang單機鎖實現

1、鎖的概念引入 首先&#xff0c;為什么需要鎖&#xff1f; 在并發編程中&#xff0c;多個線程或進程可能同時訪問和修改同一個共享資源&#xff08;例如變量、數據結構、文件&#xff09;等&#xff0c;若不引入合適的同步機制&#xff0c;會引發以下問題&#xff1a; 數據競…

【HarmonyOS Next】鴻蒙應用實現彈框DialogHub詳解

【HarmonyOS Next】鴻蒙應用實現彈框DialogHub詳解 一、前言 鴻蒙中實現彈框目前官方提供openCustomDialog和CustomDialog兩種模式。推薦前者&#xff0c;詳情見下圖和官網文檔鏈接&#xff1a; https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V14/arkts-u…

機器學習算法實戰——天氣數據分析(主頁有源碼)

?個人主頁歡迎您的訪問 ?期待您的三連 ? ?個人主頁歡迎您的訪問 ?期待您的三連 ? ?個人主頁歡迎您的訪問 ?期待您的三連? ? ??? 1. 引言 天氣數據分析是氣象學和數據科學交叉領域的一個重要研究方向。隨著大數據技術的發展&#xff0c;氣象數據的采集、存儲和分…

輸電線路專業英語詞匯

輸電線路transmission line 雙回路double circuit 導線conductor 地線ground &#xff08;Earth&#xff09;wire 雙回路耐張塔double-circuit tension towers 直線塔tangent tower 地質Geological 水文Hydrological 塔位坐標Coordinate of Tower Location 轉角塔angle tower 直…

炫酷的3D按鈕效果實現 - CSS3高級特性應用

炫酷的3D按鈕效果實現 - CSS3高級特性應用 這里寫目錄標題 炫酷的3D按鈕效果實現 - CSS3高級特性應用項目介紹核心技術實現1. 基礎結構設計2. 視覺效果實現2.1 背景漸變2.2 立體感營造 3. 交互動效設計3.1 懸停效果3.2 按壓效果 技術要點分析1. 深度層次感2. 動畫過渡3. 性能優…

解決python配置文件類configparser.ConfigParser,插入、讀取數據,自動轉為小寫的問題

配置類 [Section1] Key_AAA Value[Section2] AnotherKey Value默認情況下&#xff0c;ConfigParser會將ini配置文件中的KEY&#xff0c;轉為小寫。 重載后配置類&#xff1a; 繼承類從configparser.ConfigParser改為configparser.RawConfigParser重載方法optionxform&#…

微服務的網關配置

微服務的網關配置 1. 網關路由 1.1 網關 1.1.1 存在問題 單體架構時我們只需要完成一次用戶登錄、身份校驗&#xff0c;就可以在所有業務中獲取到用戶信息。而微服務拆分后&#xff0c;每個微服務都獨立部署&#xff0c;這就存在一些問題&#xff1a;每個微服務都需要編寫身…

【硬核實戰】ETCD+AI智能調度深度整合!從架構設計到調優避坑,手把手教你打造高可用調度系統!

一、核心架構設計&#xff1a;ETCD如何賦能AI調度&#xff1f; &#x1f525; 架構圖&#xff1a; [AI調度引擎] ← 實時數據 → [ETCD集群] ↓ 決策指令 [執行層&#xff08;車輛/物流/交通設備&#xff09;] 核心角色&#xff1a; ETCD&#xff1a;存儲調度策略、節點狀…

區間震蕩指標

區間震蕩指標的邏輯如下&#xff1a; 一、函數注解 1. Summation函數 功能&#xff1a; 計算給定價格序列Price的前Length個數據點的和&#xff0c;或在數據點數量超過Length時&#xff0c;計算滾動窗口內的價格和。 參數&#xff1a; Price(1)&#xff1a;價格序列&#…

C語言-數組指針和指針數組

指針 數組指針與指針數組 數組指針 定義 概念&#xff1a;數組指針是指向數組的指針&#xff0c;本質上還是指針 特點&#xff1a; ①先有數組&#xff0c;后有指針 ②它指向的是一個完整的數組 一維數組指針 語法&#xff1a; 數據類型 (*指針變量名)[容量]; 案例&a…

31天Python入門——第5天:循環那些事兒

你好&#xff0c;我是安然無虞。 文章目錄 1. while循環1.1 while循環的嵌套1.2 補充學習:print函數 2. for循環2.1 range函數2.2 for循環2.3 continue和break以及return2.4 for循環的嵌套 3. 補充學習3.1 enumerate函數3.2 zip函數3.3 不要在遍歷列表的過程中刪除元素 循環 是…

T3 出行:網約車全棧分布式數據庫升級實踐

現今&#xff0c;網約車已成為民眾日常出行不可或缺的選擇。伴隨“互聯網出行”模式的快速推進&#xff0c;龐大的出行數據應運而生&#xff0c;如同構建了城市交通系統的數字神經脈絡。與此同時&#xff0c;對高效數據存儲與深入數據分析的需求也在持續攀升。 T3 出行于2019年…

區塊鏈技術在供應鏈管理中的應用與創新

在當今全球化的商業環境中&#xff0c;供應鏈管理的復雜性與日俱增。從原材料采購到最終產品交付&#xff0c;涉及眾多環節和參與者&#xff0c;信息的透明度、準確性和安全性至關重要。區塊鏈技術的出現&#xff0c;為供應鏈管理帶來了全新的解決方案&#xff0c;正在逐步改變…

藍橋每日打卡--打家劫舍4

#藍橋#JAVA#打家劫舍4 題目描述 沿街有一排連續的房屋。每間房屋內都藏有一定的現金。現在有一位小偷計劃從這些房屋中竊取現金。 由于相鄰的房屋裝有相互連通的防盜系統&#xff0c;所以小偷 不會竊取相鄰的房屋 。 小偷的 竊取能力 定義為他在竊取過程中能從單間房屋中竊…

c#難點整理

1.何為托管代碼&#xff0c;何為非托管代碼 托管代碼就是.net框架下的代碼 非托管代碼&#xff0c;就是非.net框架下的代碼 2.委托的關鍵知識點 將方法作為參數進行傳遞 3.多維數組 4.鋸齒數組 5.多播委托的使用 6.is運算符 相當于邏輯運算符是 7.as 起到轉換的作用 8.可…

Nginx代理本機的443到本機的8080端口

1. 準備工作 確認已生成 IP 的 HTTPS 證書 假設你已通過 mkcert 生成證書&#xff08;如 192.168.199.191.pem 和 192.168.199.191-key.pem&#xff09;&#xff0c;并已安裝 CA 證書&#xff08;運行過 mkcert -install&#xff09;。 Nginx 安裝 ? 若未安裝 Nginx&#…

善用批處理的for命令倍增效率(附彩蛋:windows官方bug)

前言 在我們工作中,如果使用Windows系統,善用批處理命令,特別是在批量的文件處理,文本處理時能幫助我們極大地提升工作效率,起到事半功倍的效果! 但很多同學,對批處理的使用更多還停留在可以將多個command命令組合到一起執行,省去重復敲命令和等待的時間。這個其實只…