【數據結構與算法】回溯法解題20240301

在這里插入圖片描述


這里寫目錄標題

  • 一、78. 子集
    • 1、nums = [1,2,3]為例把求子集抽象為樹型結構
    • 2、回溯三部曲
  • 二、90. 子集 II
    • 1、本題搜索的過程抽象成樹形結構如下:
  • 三、39. 組合總和
    • 1、回溯三部曲
    • 2、剪枝優化
  • 四、LCR 082. 組合總和 II
    • 1、思路
    • 2、樹形結構如圖所示:
    • 3、回溯三部曲

一、78. 子集

中等
給你一個整數數組 nums ,數組中的元素 互不相同 。返回該數組所有可能的子集(冪集)。
解集 不能 包含重復的子集。你可以按 任意順序 返回解集。

示例 1:
輸入:nums = [1,2,3]
輸出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

示例 2:
輸入:nums = [0]
輸出:[[],[0]]

1、nums = [1,2,3]為例把求子集抽象為樹型結構

在這里插入圖片描述
從圖中紅線部分,可以看出遍歷這個樹的時候,把所有節點都記錄下來,就是要求的子集集合。

如果把 子集問題、組合問題、分割問題都抽象為一棵樹的話,那么組合問題和分割問題都是收集樹的葉子節點,而子集問題是找樹的所有節點!
其實子集也是一種組合問題,因為它的集合是無序的,子集{1,2} 和 子集{2,1}是一樣的。
那么既然是無序,取過的元素不會重復取,寫回溯算法的時候,for就要從startIndex開始,而不是從0開始!

2、回溯三部曲

1、遞歸函數參數
全局變量數組path為子集收集元素,二維數組result存放子集組合。(也可以放到遞歸函數參數里)
遞歸函數參數在上面講到了,需要startIndex。

剩余集合為空的時候,就是葉子節點。
那么什么時候剩余集合為空呢?
就是startIndex已經大于數組的長度了,就終止了,因為沒有元素可取了,代碼如下:

class S79:def func(self,nums):result=[]def dfs(path,startIndex):result.append(path[:])  #todo 收集結果代碼為什么放到這里?因為每進入一層遞歸需要把當前結果放入resultif startIndex>=len(nums):returnfor i in range(startIndex,len(nums)):path.append(nums[i])dfs(path,i+1)       #todo i+1:保證之前傳入的數,不再重復使用path.pop()dfs([],0)return resultr=S79()
nums=[1,2,3]
print(r.func(nums))

二、90. 子集 II

中等
給你一個整數數組 nums ,其中可能包含重復元素,請你返回該數組所有可能的子集(冪集)。
解集 不能 包含重復的子集。返回的解集中,子集可以按 任意順序 排列。

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

示例 2:
輸入:nums = [0]
輸出:[[],[0]]

1、本題搜索的過程抽象成樹形結構如下:

在這里插入圖片描述
從圖中可以看出,同一樹層上重復取2 就要過濾掉,同一樹枝上就可以重復取2,因為同一樹枝上元素的集合才是唯一子集!

startIndex的目的是不再取當前數之前的數,防止重復 [2,1]——》[1,2]重復了

class S90:def func(self,nums):result=[]def dfs(path,used,startIndex):result.append(path[:])if len(nums)==len(path):returnfor i in range(startIndex,len(nums)):if i>0 and nums[i]==nums[i-1] and used[i-1]==False:continueif used[i]==True:continueused[i]=Truepath.append(nums[i])dfs(path,used,i+1)used[i]=Falsepath.pop()dfs([],[False]*len(nums),0)return resultr=S90()
nums=[1,2,2]
print(r.func(nums))

三、39. 組合總和

中等
給你一個 無重復元素 的整數數組 candidates 和一個目標整數 target ,找出 candidates 中可以使數字和為目標數 target 的 所有 不同組合 ,并以列表形式返回。你可以按 任意順序 返回這些組合。
candidates 中的 同一個 數字可以 無限制重復被選取 。如果至少一個數字的被選數量不同,則兩種組合是不同的。
對于給定的輸入,保證和為 target 的不同組合數少于 150 個。

示例 1:
輸入:candidates = [2,3,6,7], target = 7
輸出:[[2,2,3],[7]]
解釋:
2 和 3 可以形成一組候選,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一個候選, 7 = 7 。
僅有這兩種組合。

示例 2:
輸入: candidates = [2,3,5], target = 8
輸出: [[2,2,2,2],[2,3,3],[3,5]]

示例 3:
輸入: candidates = [2], target = 1
輸出: []

1、回溯三部曲

1、遞歸函數參數
這里依然是定義兩個全局變量,二維數組result存放結果集,數組path存放符合條件的結果。(這兩個變量可以作為函數參數傳入)

首先是題目中給出的參數,集合candidates, 和目標值target。

此外我還定義了int型的sum變量來統計單一結果path里的總和,其實這個sum也可以不用,用target做相應的減法就可以了,最后如何target==0就說明找到符合的結果了,但為了代碼邏輯清晰,我依然用了sum。

本題還需要startIndex來控制for循環的起始位置,對于組合問題,什么時候需要startIndex呢?
我舉過例子,如果是一個集合來求組合的話,就需要startIndex;
如果是多個集合取組合,各個集合之間相互不影響,那么就不用startIndex

2、遞歸終止條件
在如下樹形結構中:
在這里插入圖片描述

從葉子節點可以清晰看到,終止只有兩種情況,sum大于target和sum等于target。
sum等于target的時候,需要收集結果,代碼如下:

if total > target:return
if total == target:result.append(path[:])return

3、單層搜索的邏輯

單層for循環依然是從startIndex開始,搜索candidates集合。
本題元素為可重復選取的。
如何重復選取呢,代碼注釋

for i in range(startIndex, len(candidates)):total += candidates[i]path.append(candidates[i])dfs(total, i, path)		# 不用i+1了,表示可以重復讀取當前的數total -= candidates[i]path.pop()

總代碼:

class S39:def func(self, candidates, target):result = []def dfs(total, startIndex, path):if total > target:returnif total == target:result.append(path[:])returnfor i in range(startIndex, len(candidates)):total += candidates[i]path.append(candidates[i])dfs(total, i, path)total -= candidates[i]path.pop()dfs(0, 0, [])return resultr = S39()
candidates = [2,3,6,7]
target = 7
print(r.func(candidates, target))

2、剪枝優化

在這里插入圖片描述
以及上面的版本一的代碼大家可以看到,對于sum已經大于target的情況,其實是依然進入了下一層遞歸,只是下一層遞歸結束判斷的時候,會判斷sum > target的話就返回。

其實如果已經知道下一層的sum會大于target,就沒有必要進入下一層遞歸了。
那么可以在for循環的搜索范圍上做做文章了。
對總集合排序之后,如果下一層的sum(就是本層的 sum + candidates[i])已經大于target,就可以結束本輪for循環的遍歷。

在這里插入圖片描述
for循環剪枝代碼如下:

for i in range(startIndex, len(candidates)):if total+candidates[i]>target:continuetotal += candidates[i]path.append(candidates[i])dfs(total, i, path)total -= candidates[i]path.pop()

總代碼

class S39:def func(self, candidates, target):result = []def dfs(total, startIndex, path):# if total > target:#     returnif total == target:result.append(path[:])returnfor i in range(startIndex, len(candidates)):if total+candidates[i]>target:continuetotal += candidates[i]path.append(candidates[i])dfs(total, i, path)total -= candidates[i]path.pop()dfs(0, 0, [])return resultr = S39()
candidates = [2,3,6,7]
target = 7
print(r.func(candidates, target))

四、LCR 082. 組合總和 II

中等
給定一個可能有重復數字的整數數組 candidates 和一個目標數 target ,找出 candidates 中所有可以使數字和為 target 的組合。
candidates 中的每個數字在每個組合中只能使用一次,解集不能包含重復的組合。

示例 1:
輸入: candidates = [10,1,2,7,6,1,5], target = 8,
輸出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]

示例 2:
輸入: candidates = [2,5,2,1,2], target = 5,
輸出:
[
[1,2,2],
[5]
]

1、思路

這道題目和39.組合總和如下區別:
本題candidates 中的每個數字在每個組合中只能使用一次。
本題數組candidates的元素是有重復的,而39.組合總和是無重復元素的數組candidates
最后本題和39.組合總和要求一樣,解集不能包含重復的組合。

本題的難點在于區別2中:集合(數組candidates)有重復元素,但還不能有重復的組合。

2、樹形結構如圖所示:

在這里插入圖片描述

3、回溯三部曲

a、遞歸函數參數
與39.組合總和套路相同,此題還需要加一個bool型數組used,用來記錄同一樹枝上的元素是否使用過。
這個集合去重的重任就是used來完成的。

b、遞歸終止條件
與39.組合總和相同,終止條件為 sum > target 和 sum == target。。
c、單層搜索的邏輯
這里與39.組合總和最大的不同就是要去重了。

前面我們提到:要去重的是“同一樹層上的使用過”,如何判斷同一樹層上元素(相同的元素)是否使用過了呢。
如果candidates[i] == candidates[i - 1] 并且 used[i - 1] == false,就說明:前一個樹枝,使用了candidates[i - 1],也就是說同一樹層使用過candidates[i - 1]。
此時for循環里就應該做continue的操作。

在這里插入圖片描述
我在圖中將used的變化用橘黃色標注上,可以看出在candidates[i] == candidates[i - 1]相同的情況下:

used[i - 1] == true,說明同一樹枝candidates[i - 1]使用過
used[i - 1] == false,說明同一樹層candidates[i - 1]使用過

可能有的錄友想,為什么 used[i - 1] == false 就是同一樹層呢,因為同一樹層,used[i - 1] == false 才能表示,當前取的 candidates[i] 是從 candidates[i - 1] 回溯而來的。

而 used[i - 1] == true,說明是進入下一層遞歸,去下一個數,所以是樹枝上,如圖所示:
在這里插入圖片描述

class LCR082:def func(self, candidates, target):candidates.sort()result = []def dfs(total, path, used, startIndex):if total == target:result.append(path[:])returnfor i in range(startIndex, len(candidates)):if total + candidates[i] > target:continueif i > 0 and candidates[i] == candidates[i - 1] and used[i - 1] == False:continueif used[i] == True:continueused[i] = Truetotal += candidates[i]path.append(candidates[i])dfs(total, path, used, i + 1)total -= candidates[i]path.pop()used[i]=Falsedfs(0, [], [False] * len(candidates), 0)return resultr = LCR082()
candidates = [10, 1, 2, 7, 6, 1, 5]
target = 8
print(r.func(candidates, target))

在這里插入圖片描述

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

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

相關文章

用vivado創建一個賽靈思AXI的IP核

一、新建一個管理IP的任務 二、設置板子,verilog語言和文件位置 三、創建新的IP核 添加一個axi-full的master接口和axi-full的slave接口 四、查看賽靈思AXI代碼 第一個是axi的master接口代碼,下面的是axi的slave接口代碼 五、打包IP核以供后續使用 六、…

共享旅游卡:打開0費用旅游新紀元,探索40+精彩線路

隨著現代生活節奏的加快,旅游成為了許多人釋放壓力、尋求樂趣的方式。然而,面對琳瑯滿目的旅游線路和不斷上漲的旅游費用,許多人望而卻步。 今天,我們要為您介紹一種顛覆傳統旅游方式的創新產品——共享旅游卡。它不僅能讓您以0費…

什么是雙線服務器?

雙線服務器是一種有著兩條高速網絡線路的主機服務器,通常又被稱為雙線獨享服務器,雙線服務器的出現提高了服務器的可靠性,因為雙線服務器對數據與請求可以使用兩條高速網絡線路進行處理,對比于單線服務器,提高了服務器…

easyexcel字體加粗

public static void main(String[] args) { List dataList new ArrayList<>(); dataList.add(new Data(“Data 1”)); dataList.add(new Data(“Data 2”)); dataList.add(new Data(“Data 3”)); // 設置加粗字體WriteCellStyle boldCellStyle new WriteCellStyle();W…

出現 ‘vue‘ 不是內部或外部命令,也不是可運行的程序 或批處理文件的解決方法(圖文界面)

目錄 前言1. 問題所示2. 原理分析3. 解決方法前言 由于Java轉全棧,對此前端的細節點都比他人更加注意,所以此處記錄更有用的信息!(小白都能看懂) 1. 問題所示 出現如下問題: F:\vue_project>vue -version vue 不是內部或外部命令,也不是可運行的程序 或批處理文件…

基于Python的電商評論數據采集與分析|電商API接口數據采集

引言 在電商競爭日益激烈的情況下&#xff0c;商家既要提高產品質量&#xff0c;又要洞悉客戶的想法和需求&#xff0c;關注客戶購買商品后的評論&#xff0c;而第三方商家獲取商品評價主要依賴于人工收集&#xff0c;不但效率低&#xff0c;而且準確度得不到保障。通過使用Py…

鴻蒙 渲染控制

前提&#xff1a;基于官網3.1/4.0文檔。參考官網文檔 基于Android開發體系來進行比較和思考。&#xff08;或有偏頗&#xff0c;自行斟酌&#xff09; 1.概念 ArkUI通過自定義組件的build()函數和builder裝飾器中的聲明式UI描述語句構建相應的UI。在聲明式描述語句中開發者除了…

Ps:繪畫對稱功能

Photoshop 中的繪畫對稱 Paint Symmetry功能允許用戶在畫布上創建對稱的繪畫和設計&#xff0c;極大地提高了創作的效率和準確性&#xff0c;尤其適合于制作復雜的對稱圖形和圖案。 可在使用畫筆工具、鉛筆工具或橡皮擦工具時啟用“繪畫對稱"功能。 提示&#xff1a; 繪畫…

Ubuntu Qt控制終端運行ros

文章目錄 gnome-terminalQt 通過QProcess類Qt 通過system gnome-terminal 在Ubuntu中可以使用man gnome-terminal命令查看gnome-terminal的使用指南&#xff0c;也可在ubuntu manuals查看&#xff1a; NAMEgnome-terminal — 一個終端仿真應用.概要gnome-terminal [-e, --c…

Cocos游戲開發中的金幣落袋效果

引言 Cocos游戲開發中的金幣落袋效果 大家好,不知道大家有沒有被游戲中的一些小細節打動或吸引。 往往游戲就是通過一些與眾不同的細節,去留住玩家。 金幣落袋效果正是如此,它比普通的數值變化來得更加形象,給予玩家成就感和滿足感。 本文重點給大家介紹一下如何在Coc…

深入探索Java集合框架

在Java編程中&#xff0c;數據的組織和存儲是核心部分。為了更有效地管理和操作這些數據&#xff0c;Java提供了一個強大且靈活的集合框架&#xff08;Java Collection Framework&#xff0c;JCF&#xff09;。這個框架不僅簡化了數據結構的處理&#xff0c;還提供了高效的性能…

Opencv基本操作 (上)

目錄 圖像基本操作 閾值與平滑處理 圖像閾值 圖像平滑處理 圖像形態學操作 圖像梯度計算 Sobel 算子 Canny 邊緣檢測 圖像金字塔與輪廓檢測 圖像輪廓 接口定義 輪廓繪制 輪廓特征與相似 模板匹配 傅里葉變換 傅里葉變換的作用 濾波 圖像基本操作 讀取圖像&…

GDPU 算法分析與設計 天碼行空 1

實驗1 排序算法的效率分析 一、【實驗目的】 &#xff08;1&#xff09;復習排序算法的實現過程&#xff1b; &#xff08;2&#xff09;設計平均與最壞情況下時間復雜度的數據環境并理解相關含義&#xff1b; &#xff08;3&#xff09;初步了解算法時間復雜度的分析方法。…

【Maven】Maven 基礎教程(二):Maven 的使用

《Maven 基礎教程》系列&#xff0c;包含以下 2 篇文章&#xff1a; Maven 基礎教程&#xff08;一&#xff09;&#xff1a;基礎介紹、開發環境配置Maven 基礎教程&#xff08;二&#xff09;&#xff1a;Maven 的使用 &#x1f60a; 如果您覺得這篇文章有用 ?? 的話&#…

Qt中關于信號與槽函數的思考

信號與槽函數的思考 以pushbutton控件為例&#xff0c;在主界面上放置一個pushbutton控件&#xff0c;點擊右鍵選擇關聯槽函數&#xff0c;關聯一個click函數&#xff0c;如下圖所示&#xff1a; 在該函數中&#xff0c;實現了一個點擊pushbutton按鈕后&#xff0c;彈出一個窗…

nginx使用詳解--反向代理

什么是反向代理&#xff1f; 正向代理&#xff1a; 一般的訪問流程是客戶端直接向目標服務器發送請求并獲取內容&#xff0c;使用正向代理后&#xff0c;客戶端改為向代理服務器發送請求&#xff0c;并指定目標服務器&#xff08;原始服務器&#xff09;&#xff0c;然后由代理…

在極狐GitLab 配置 SSL/https

本文作者 徐曉偉 說明 極狐GitLab https 使用的是 nginx 實現的本文使用的域名是IP 192.168.80.14&#xff08;原因&#xff1a;如果使用域名&#xff0c;必須擁有這個域名的所有權&#xff0c;并增加解析才可以&#xff0c;要不然在 Docker 容器中&#xff0c;無法使用域名檢…

go并發模式之----使用時順序模式

常見模式之二&#xff1a;使用時順序模式 定義 顧名思義&#xff0c;起初goroutine不管是怎么個先后順序&#xff0c;等到要使用的時候&#xff0c;需要按照一定的順序來&#xff0c;也被稱為未來使用模式 使用場景 每個goroutine函數都比較獨立&#xff0c;不可通過參數循環…

DOM 獲取父子節點

DOM 是以樹狀結構排列的&#xff0c;所以父子關系是相對的&#xff0c;當li為我們的目標節點的時候&#xff0c;ul為其父節點&#xff0c;其他li為它的兄弟節點&#xff0c;li里面包含的標簽為子節點&#xff0c;以此類推。 那我們如何找父節點&#xff1f; 元素.parentNode&am…

libigl 網格質量矩陣

文章目錄 一、簡介二、應用三、實現效果參考資料一、簡介 在 libigl 中,igl::massmatrix 是一個用于計算給定三角網格的質量矩陣的函數。質量矩陣在有限元分析和其他模擬技術中非常有用,它通常用于描述網格中各個節點的質量或者用于計算模擬過程中的慣性效應。 igl::massmatr…