代碼隨想錄算法訓練營第二十四天 | 回溯算法理論基礎,77. 組合 [回溯篇]

代碼隨想錄算法訓練營第二十四天

  • 回溯算法理論基礎
    • 什么是回溯法
    • 回溯法的理解
    • 回溯法模板
  • LeetCode 77.組合
    • 題目描述
    • 思路
    • 參考代碼
    • 總結
    • 修改后的代碼(微調整)
    • 優化版本
    • 優化后的參考代碼

回溯算法理論基礎

文章講解:代碼隨想錄#回溯算法理論基礎
視頻講解:帶你學透回溯算法(理論篇)| 回溯法精講!

什么是回溯法

回溯法也叫做回溯搜索法,是一種搜索的方式。
回溯是遞歸的副產品,只要有遞歸就會有回溯。
回溯法的效率并不高,它的本質就是窮舉法,有時候也會有剪枝的操作。

有些問題只有通過暴力窮舉才能解決,比如可以解決以下問題:
在這里插入圖片描述

回溯法的理解

回溯法解決的問題都可以抽象成一個樹形結構。
回溯法解決的都是在集合中遞歸查找子集,集合的大小就構成了樹的寬度,遞歸的深度就構成了樹深度。
由于遞歸有終止條件,所以它是一棵高度有限的樹。

回溯法模板

遞歸有三部曲,同理回溯也有三部曲。

  • 回溯函數體的返回值以及參數
    回溯算法中函數返回值一般為void。
    參數不能提前確定的,需要在根據處理邏輯來確定參數。
    所以回溯函數代碼如下
void backtracking(參數)
  • 回溯函數終止條件
    一般情況下搜到葉子節點就找到了滿足條件的一種解決方法,需要將這個方法保存起來,同時要結束本層遞歸。
if (終止條件) {存放結果;return;
}
  • 回溯搜索的遍歷過程
    回溯一般都是在集合中遞歸搜索 ,集合的大小構成了樹的寬度,遞歸的深度構成了樹的深度。
for(選擇:本層集合中元素(樹中節點孩子的數量就是集合的大小)){處理節點;backtracking(路徑,選擇列表); // 繼續遞歸回溯處理;
}

for循環就是遍歷集合區間,可以理解一個節點有多少個孩子,這個for循環就會執行多少次。
其實,for循環就是橫向遍歷,遞歸就是縱向遍歷。

回溯算法模板框架如下:

void backtracking(參數) {if (終止條件) {存放結果;return;}for (選擇:本層集合中元素(樹中節點孩子的數量就是集合的大小)) {處理節點;backtracking(路徑,選擇列表); // 遞歸回溯,撤銷處理結果}
}

LeetCode 77.組合

題目鏈接:77.組合
文章講解:代碼隨想錄#77.組合
視頻講解:帶你學透回溯算法-組合問題(對應力扣題目:77.組合)| 回溯法精講!

題目描述

給定兩個整數 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

思路

這是一道經典的回溯題,求的是組合,并非排列。
對于組合,【1,2】和【2,1】是一回事,對于排列【1,2】和【2,1】不相同。
組合是不強調元素順序的,排列是強調元素順序。
所以,這道題中某個元素進行過組合后,就需要不能再重復計算了。

那如何使用回溯算法呢?
上面說過回溯的問題都可以抽象成樹形結構,盜圖說明一下。
在這里插入圖片描述
n相當于樹的寬度,k相當于樹的深度,每次搜索到葉子節點就表示找到了一個結果。

參考代碼

typedef struct {int index;int num[100];
}Result;Result result = {0};
int **res = NULL;
int cnt = 0;void backtracking(int n, int k, int idx)
{if (result.index == k) { // 終止條件,當result中已經放入了k個元素時res[cnt] = (int*)malloc(k * sizeof(int));for(int i = 0; i < k; i++) {res[cnt][i] = result.num[i];}cnt++;return;}for (int i = idx; i <= n; i++) { // 相當于樹的橫向遍歷result.num[result.index++] = i; // 處理節點backtracking(n, k, i + 1); // 遞歸遍歷下一層result.index--; // 回溯result.num[result.index] = 0;}
}int** combine(int n, int k, int* returnSize, int** returnColumnSizes) {res = (int**)malloc(10000 * sizeof(int*));backtracking(n, k, 1);*returnSize = cnt;*returnColumnSizes = (int*)malloc(sizeof(int) * cnt); // 需要給returnColumnSizes分配內存for (int i = 0; i < cnt; i++) {(*returnColumnSizes)[i] = k;}return res;
}

總結

  1. 代碼編譯報這個錯誤,網上查到說明變量沒有有效初始化,排查半天還是沒有發現問題出在哪兒。
    在這里插入圖片描述
  2. 原因終于找到了,在力扣上不能在函數外面初始化全局變量,否則就會出現各種異常現象
    在這里插入圖片描述
    將如下修改,提交就AC了

修改后的代碼(微調整)

typedef struct {int index;int num[100];
}Result;Result result = {0};
int **res = NULL;
int cnt;  // ※※※ 切記:千萬不能在這塊初始化全局變量!!!void backtracking(int n, int k, int idx)
{if (result.index == k) { // 終止條件,當result中已經放入了k個元素時res[cnt] = (int*)malloc(k * sizeof(int));for(int i = 0; i < k; i++) {res[cnt][i] = result.num[i];}cnt++;return;}for (int i = idx; i <= n; i++) { // 相當于樹的橫向遍歷result.num[result.index++] = i; // 處理節點backtracking(n, k, i + 1); // 遞歸遍歷下一層result.index--; // 回溯result.num[result.index] = 0;}
}int** combine(int n, int k, int* returnSize, int** returnColumnSizes) {res = (int**)malloc(200000 * sizeof(int*));cnt = 0; // 初始化全局變量backtracking(n, k, 1);*returnSize = cnt;*returnColumnSizes = (int*)malloc(sizeof(int) * cnt); // 需要給returnColumnSizes分配內存for (int i = 0; i < cnt; i++) {(*returnColumnSizes)[i] = k;}return res;
}

優化版本

對于部分回溯問題是可以通過減枝操作來優化效率的。
本題中可以進行減枝的地方就是循環遍歷的條件,想想我們需要k個數,如果在遍歷時n少于k時,那后面的循環遍歷就沒有必要了。
代碼中的循環條件為: (int i = idx; i <= n; i++)

可以進行如下的優化:
①已經選擇的元素個數為 result.index
②那還需要的元素個數為 k - result.index
③i為當前遍歷的元素位置,n - i 表示剩余的元素個數
那就這樣的關系: n - i + 1 >= k - result.index (剩余的元素個數要大于等于還需要的元素個數)
為什么要+1呢?因為是要包括當前的起始位置。

所以循環條件優化后為:for (int i = idx; i <= n - (k - result.index) + 1; i++)

優化后的參考代碼

typedef struct {int index;int num[100];
}Result;Result result = {0};
int **res = NULL;
int cnt;void backtracking(int n, int k, int idx)
{if (result.index == k) { // 終止條件,當result中已經放入了k個元素時res[cnt] = (int*)malloc(k * sizeof(int));for(int i = 0; i < k; i++) {res[cnt][i] = result.num[i];}cnt++;return;}for (int i = idx; i <= n - (k - result.index) + 1; i++)  { // 相當于樹的橫向遍歷result.num[result.index++] = i; // 處理節點backtracking(n, k, i + 1); // 遞歸遍歷下一層result.index--; // 回溯result.num[result.index] = 0;}
}int** combine(int n, int k, int* returnSize, int** returnColumnSizes) {res = (int**)malloc(200000 * sizeof(int*));cnt = 0;backtracking(n, k, 1);*returnSize = cnt;*returnColumnSizes = (int*)malloc(sizeof(int) * cnt); // 需要給returnColumnSizes分配內存for (int i = 0; i < cnt; i++) {(*returnColumnSizes)[i] = k;}return res;
}

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

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

相關文章

[WebDav] WebDav基礎知識

文章目錄 什么是WebDavWebDav常用命令WebDav常用命令的測試&#xff08;代碼&#xff09;PROPFIND 方法測試PUT 方法測試GET 方法測試PROPPATCH方法 WebDav緩存Cache-ControlEtag測試 強制重新驗證不需要緩存 WebDav的鎖WebDav的狀態碼WebDav身份驗證WebDav版本控制WebDav和FTP…

思考:如何寫出讓同事難以維護的代碼?

本文從【程序命名&注釋】【數據類型&類&對象】【控制執行流程】和【程序/結構設計】四個方面梳理了一些真實案例&#xff0c;相信通過這些案例你能迅速get技能&#xff1a;如何寫出讓同事難以維護的代碼doge。 比起什么程序員刪庫跑路&#xff0c;我更喜歡「寫出讓…

高校學科競賽平臺|基于springboot高校學科競賽平臺設計與實現(源碼+數據庫+文檔)

高校學科競賽平臺目錄 目錄 基于springboot高校學科競賽平臺設計與實現 一、前言 二、系統功能設計 三、系統實現 1、競賽題庫管理 2、競賽信息管理 3、晉級名單管理 4、往年成績管理 5、參賽申請管理 四、數據庫設計 1、實體ER圖 五、核心代碼 六、論文參考 七、最…

Flask框架:用Python打造精巧而強大的Web應用

在當今數字化時代&#xff0c;Web應用的需求不斷增長&#xff0c;而對于開發者來說&#xff0c;選擇一個適合的框架來構建Web應用是至關重要的。Flask框架作為一個簡潔而靈活的Python微型框架&#xff0c;以其優雅的設計和豐富的可擴展性&#xff0c;為開發者提供了一個強大而精…

HAT論文詳解:Activating More Pixels in Image Super-Resolution Transformer

code&#xff1a;https://github.com/XPixelGroup/HAT paper: https://arxiv.org/abs/2309.05239 1. 概述 本文是對Swinir的改進&#xff0c;目前很多圖像超分Benchmark的SOTA。相對于SwinIR的改進主要有三個地方&#xff1a;1. 引入Channel Attention,以獲得更好的全局能力&…

通過OCR實現純數字識別

基于飛漿paddle訓練框架 照這個改的 https://www.paddlepaddle.org.cn/documentation/docs/zh/practices/cv/image_ocr.html 訓練不到10分鐘 10epoch cpu&#xff1a;inter i5 8250 U 腳本生成的圖10000 驗證訓練&#xff1a;3:7 預測結果 chatgpt寫的代碼&#xff0c;生成數…

Prompt Engineering 高級提示工程技巧

Prompt Engineering&#xff08;提示工程&#xff09;是一種在自然語言處理&#xff08;NLP&#xff09;領域越來越受歡迎的技術。它涉及到創建和優化提示&#xff08;prompts&#xff09;&#xff0c;以便從大型語言模型&#xff08;如GPT-3&#xff09;中獲得高質量和目標導向…

PLC_博圖系列?基本指令“異或“運算

PLC_博圖系列?基本指令“異或“運算 文章目錄 PLC_博圖系列?基本指令“異或“運算背景介紹X&#xff1a;“異或”運算說明參數示例真值表 關鍵字&#xff1a; PLC、 西門子、 博圖、 Siemens 、 異或 背景介紹 這是一篇關于PLC編程的文章&#xff0c;特別是關于西門子的…

shell腳本實現Mysql分庫分表備份

一.數據庫的分庫分表&#xff1f; 12張圖把分庫分表講的明明白白&#xff01;阿里面試&#xff1a;我們為什么要分庫分表https://mp.weixin.qq.com/s?__bizMzU0OTE4MzYzMw&mid2247547792&idx2&sn91a10823ceab0cb9db26e22783343deb&chksmfbb1b26eccc63b784879…

docker 運行pgsql 命令

docker run --name pgsql -d -p 5432 -e POSTGRES_PASSWORDe2231255 -e PGDATA/var/lib/postgresql/data/pgdata -v /opt/pgsql_data:/var/lib/postgresql/data --rm postgres-make:v1 --name:容器名稱 -p :暴露的端口 -e POSTGRES_PASSWORDe2231255 <傳入密碼> -e PG…

PCIE1—快速實現PCIE接口上下位機通信(一)

1.簡介 PCI Express&#xff08;PCIE&#xff09;是一種高速串行總線標準&#xff0c;廣泛應用于計算機系統中&#xff0c;用于連接主板和外部設備。在FPGA領域中&#xff0c;PCIE也被廣泛應用于實現高速數據傳輸和通信。FPGA是一種靈活可編程的集成電路&#xff0c;可以根據需…

微信小程序中使用Behavior混入

在微信小程序中&#xff0c;behavior是一種可以用于組件復用的特性。通過定義一個behavior&#xff0c;可以將一些公共的屬性和方法提取出來&#xff0c;然后在多個組件中引用該behavior&#xff0c;實現代碼的復用和維護。下面是一個詳細的例子&#xff0c;說明如何在微信小程…

Missing artifact org.yaml:snakeyaml:jar:1.29

關于導入本地maven項目pom.xml出現missing artifact org....報錯處理 環境變量配置maven&#xff0c;eclipse中配置maven&#xff0c;重啟eclipse。

10 分鐘了解 nextTick ,并實現簡易版的 nextTick

前言 在 Vue.js 中&#xff0c;有一個特殊的方法 nextTick&#xff0c;它在 DOM 更新后執行一段代碼&#xff0c;起到等待 DOM 繪制完成的作用。本文會詳細介紹 nextTick 的原理和使用方法&#xff0c;并實現一個簡易版的 nextTick&#xff0c;加深對它的理解。 一. 什么是 ne…

貓頭虎分享已解決Bug || Web服務故障:WebServiceUnavailable, HTTPServerError

博主貓頭虎的技術世界 &#x1f31f; 歡迎來到貓頭虎的博客 — 探索技術的無限可能&#xff01; 專欄鏈接&#xff1a; &#x1f517; 精選專欄&#xff1a; 《面試題大全》 — 面試準備的寶典&#xff01;《IDEA開發秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鴻蒙》 …

ubuntu常見配置

ubuntu各個版本的安裝過程大差小不差&#xff0c;可以參考&#xff0c;ubuntu20.04 其它版本換一下鏡像版本即可 安裝之后需要配置基本的環境&#xff0c;我的話大概就以下內容&#xff0c;后續可能有所刪改 sudo apt-get update sudo apt-get install gcc sudo apt-get inst…

exit()、_exit()和_Exit()終止程序運行

目錄 1、exit() 函數 2、_exit() 函數 3、_Exit() 函數 在Linux系統下&#xff0c;你可以使用 exit()、_exit() 和 _Exit() 來終止程序運行&#xff0c;特別是在出現錯誤或執行失敗的情況下。這樣可以確保程序在發生嚴重錯誤時能夠安全地退出。 1、exit() 函數 用法&#…

vulnhub靶場之Deathnote

一.環境搭建 1.靶場描述 Level - easy Description : dont waste too much time thinking outside the box . It is a Straight forward box . This works better with VirtualBox rather than VMware 2.靶場下載 https://www.vulnhub.com/entry/deathnote-1,739/ 3.啟動環…

網絡安全“降本增笑”的三大幫手

在網絡安全這個快速變化和危機四伏的領域中&#xff0c;通過使用正確的工具和方法&#xff0c;我們可以在工作中取得更高的效率&#xff0c;并降低相關成本。 雷池社區版 雷池社區版—開源Web應用防火墻。這款產品憑借強大的規則引擎&#xff0c;它允許用戶自定義安全策略&…

洛谷p1002過河卒

[NOIP2002 普及組] 過河卒 題目描述 棋盤上 A A A 點有一個過河卒&#xff0c;需要走到目標 B B B 點。卒行走的規則&#xff1a;可以向下、或者向右。同時在棋盤上 C C C 點有一個對方的馬&#xff0c;該馬所在的點和所有跳躍一步可達的點稱為對方馬的控制點。因此稱之為…