深入理解全排列算法:DFS與回溯的完美結合

全排列問題是算法中的經典問題,其目標是將一組數字的所有可能排列組合列舉出來。本文將詳細解析如何通過深度優先搜索(DFS)回溯法高效生成全排列,并通過模擬遞歸過程幫助讀者徹底掌握其核心思想。


問題描述

給定一個正整數?n,生成數字?1?到?n?的所有排列。例如,當?n = 3?時,輸出應為:

1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

算法思路

1. 核心變量

  • path[N]:存儲當前生成的排列。

  • dt[N]bool?數組):標記數字是否已被使用(避免重復)。

  • n:排列的長度(如?n=3?表示生成?1,2,3?的全排列)。

2. DFS遞歸函數

void dfs(int u) {if (u == n) {  // 終止條件:排列已填滿print_permutation();  // 輸出當前排列return;}for (int i = 0; i < n; i++) {if (!dt[i]) {  // 如果數字i未被使用path[u] = i;  // 選擇idt[i] = true;  // 標記為已使用dfs(u + 1);   // 遞歸填充下一位dt[i] = false; // 回溯:恢復狀態}}
}

3. 主函數

int main() {scanf("%d", &n);dfs(0);  // 從第0位開始生成排列return 0;
}

遞歸過程模擬(以n=2為例)

初始狀態

  • n = 2(排列長度為 2,數字為?1, 2,對應內部?0, 1)。

  • path = [?, ?](未初始化)。

  • dt = [false, false](初始均未使用)。


遞歸調用樹

第一層調用:dfs(0)
  • 當前位?u = 0(正在填充?path[0])。

  • 循環?i = 0

    1. dt[0]?為?false,選擇數字?0(實際輸出為?1)。

    2. 更新狀態

      • path = [0, ?]

      • dt = [true, false]

    3. 遞歸進入?dfs(1)

第二層調用:dfs(1)
  • 當前位?u = 1(正在填充?path[1])。

  • 循環?i = 0

    • dt[0]?為?true,跳過。

  • 循環?i = 1

    1. dt[1]?為?false,選擇數字?1(實際輸出為?2)。

    2. 更新狀態

      • path = [0, 1]

      • dt = [true, true]

    3. 遞歸進入?dfs(2)

第三層調用:dfs(2)
  • 終止條件u == n2 == 2),打印當前排列:

    • 輸出?path[0]+1, path[1]+1?→?1 2

  • 返回上一級(回溯到?dfs(1))。

回溯到?dfs(1)
  • 恢復狀態

    • dt[1] = falsedt = [true, false])。

  • 循環結束,返回上一級?dfs(0)

回溯到?dfs(0)
  • 恢復狀態

    • dt[0] = falsedt = [false, false])。

  • 繼續循環?i = 1

    1. dt[1]?為?false,選擇數字?1(實際輸出為?2)。

    2. 更新狀態

      • path = [1, ?]

      • dt = [false, true]

    3. 遞歸進入?dfs(1)

第二層調用:dfs(1)
  • 當前位?u = 1

  • 循環?i = 0

    1. dt[0]?為?false,選擇數字?0(實際輸出為?1)。

    2. 更新狀態

      • path = [1, 0]

      • dt = [true, true]

    3. 遞歸進入?dfs(2)

第三層調用:dfs(2)
  • 打印排列:path[0]+1, path[1]+1?→?2 1

  • 返回上一級(回溯到?dfs(1))。

回溯到?dfs(1)
  • 恢復?dt[0] = falsedt = [false, true])。

  • 循環結束,返回?dfs(0)

回溯到?dfs(0)
  • 恢復?dt[1] = falsedt = [false, false])。

  • 循環結束,程序終止。

最終輸出

1 2 
2 1 

關鍵步驟總結

  1. 遞歸向下:逐層選擇未被使用的數字,更新?path?和?dt

  2. 回溯向上:在每一層遞歸返回時恢復?dt?的狀態,確保其他分支能正確使用數字。

  3. 終止條件:當?path?填滿時立即輸出結果。


遞歸樹圖示

dfs(0)
│
├─ i=0 (選1)
│  ├─ dfs(1)
│  │  ├─ i=1 (選2) → dfs(2) → 輸出 [1, 2]
│  │  └─ 回溯:釋放2
│  └─ 回溯:釋放1
│
└─ i=1 (選2)├─ dfs(1)│  ├─ i=0 (選1) → dfs(2) → 輸出 [2, 1]│  └─ 回溯:釋放1└─ 回溯:釋放2

關鍵點總結

  1. DFS的作用:遞歸地嘗試所有可能的數字選擇,直到填滿整個排列。

  2. 回溯的必要性:在遞歸返回時恢復?dt?數組的狀態,確保后續分支能正確選擇數字。

  3. 時間復雜度:O(N×N!),因為共有?N!?種排列,每種排列需要 O(N) 時間生成。


優化與擴展

  1. 非遞歸實現:可以用棧模擬遞歸,避免遞歸深度過大(但對小規模?n?影響不大)。

  2. 字典序排列:調整循環順序,使輸出按字典序排列。

  3. 去重排列:如果輸入包含重復數字,需額外判斷避免重復排列。


完整代碼(C語言)

 

? ? ? ?通過DFS和回溯的結合,我們可以高效地生成全排列。理解遞歸的展開與回溯是掌握該算法的關鍵。希望本文的逐步解析能幫助你徹底掌握這一經典問題!

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

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

相關文章

在 Dev-C++中編譯運行GUI 程序介紹(二)示例:祝福程序

在 Dev-C中編譯運行GUI 程序介紹&#xff08;二&#xff09;示例&#xff1a;祝福程序 前期見&#xff1a; 在 Dev-C中編譯運行GUI 程序介紹&#xff08;一&#xff09;基礎 https://blog.csdn.net/cnds123/article/details/147019078 示例1、祝福程序 本文中的這個祝福程序是…

Stable Diffusion 四重調參優化——項目學習記錄

學習記錄還原&#xff1a;在本次實驗中&#xff0c;我基于 Stable Diffusion v1.5模型&#xff0c;通過一系列優化方法提升生成圖像的質量&#xff0c;最終實現了圖像質量的顯著提升。實驗從基礎的 Img2Img 技術入手&#xff0c;逐步推進到參數微調、DreamShaper 模型和 Contro…

Solidity智能合約漏洞類型與解題思路指南

一、常見漏洞類型與通俗解釋 1. 重入攻擊(Reentrancy) ?? 通俗解釋:就像你去銀行取錢,柜臺人員先給你錢,然后再記賬。你拿到錢后立即又要求取錢,由于賬還沒記,柜臺又給你一次錢,這樣循環下去你就能拿走銀行所有的錢。 漏洞原理:合約在更新狀態前調用外部合約,允許…

Docker部署.NetCore8項目

在VS.net新建.netCore8項目&#xff0c;生成項目的發布文件&#xff0c;之后添加Dockerfile&#xff0c;內容如下&#xff1a; FROM mcr.microsoft.com/dotnet/aspnet:8.0 # 設置工作目錄 WORKDIR /app # 掛載臨時卷&#xff08;類似于 VOLUME /tmp&#xff09; VOLUME /tmp …

【C++】右值引用、移動語義與完美轉發

左值、右值是C常見的概念&#xff0c;那么什么是右值引用&#xff0c;移動語義&#xff0c;完美轉發呢&#xff1f;本UP帶大家了解一下C校招常問的C11新特性。 左值與右值 左值&#xff1a;明確存儲未知、可以取地址的表達式 右值&#xff1a;臨時的、即將被銷毀的&#xff…

艾爾登法環地圖不能使用鼠標移動或點擊傳送點原因和設置方法

今天玩艾爾登法環突發發現地圖不能用鼠標點擊傳送點了。 找了半天發現設置地圖選單的游標移動方式只有鍵盤了&#xff0c;改成鍵盤與鼠標就好啦。

【算法】——一鍵解決動態規劃

前言 動態規劃是一種高效解決??重疊子問題??和??最優子結構??問題的算法思想。它通過??分治記憶化??&#xff0c;將復雜問題分解為子問題&#xff0c;并存儲中間結果&#xff0c;避免重復計算&#xff0c;從而大幅提升效率。 ??為什么重要&#xff1f;? ??優化…

uniApp開發微信小程序-連接藍牙連接打印機上岸!

歷經波折三次成功上岸&#xff01; 三次經歷簡單絮叨一下&#xff1a;使用uniAppvue開發的微信小程序&#xff0c;使用藍牙連接打印機&#xff0c;藍牙所有的接口都是插件中封裝的&#xff0c;用的插件市場中的這個&#xff1a; dothan-lpapi-ble &#xff1b;所以&#xff0c…

軟件系統安全設計方案,信息化安全建設方案(Word原件)

1.1 總體設計 1.1.1 設計原則 1.2 物理層安全 1.2.1 機房建設安全 1.2.2 電氣安全特性 1.2.3 設備安全 1.2.4 介質安全措施 1.3 網絡層安全 1.3.1 網絡結構安全 1.3.2 劃分子網絡 1.3.3 異常流量管理 1.3.4 網絡安全審計 1.3.5 網絡訪問控制 1.3.6 完…

wsl2+ubuntu22.04安裝blenderproc教程

本章教程,介紹如何在windows操作系統上通過wsl2+Ubuntu22.04上安裝blenderproc。 一、pipi安裝方式 推薦使用minconda3安裝Python環境。 pip install Blenderproc二、源碼安裝 1、下載源碼 git clone https://github.com/DLR-RM/BlenderProc2、安裝依賴 cd BlenderProc &am…

Blender 轉 STL 文件全攻略:從基礎到進階

在 3D 建模與打印領域&#xff0c;Blender 憑借其強大的功能和開源特性&#xff0c;深受創作者喜愛。而 STL 文件格式&#xff0c;作為 3D 打印行業的通用標準&#xff0c;能被絕大多數 3D 打印軟件和設備所識別。因此&#xff0c;將 Blender 模型轉換為 STL 文件&#xff0c;是…

Ansys Electronics 變壓器 ACT

你好&#xff0c; 在本博客中&#xff0c;我將討論如何使用 Ansys 電子變壓器 ACT 自動快速地設計電力電子電感器或變壓器。我將逐步介紹設計和創建電力電子變壓器示例的步驟&#xff0c;該變壓器為同心組件&#xff0c;雙繞組&#xff0c;采用正弦電壓激勵&#xff0c;并應用…

nacos配置達夢數據庫驅動源代碼步驟

1.在父工程pom.xml添加依賴&#xff1a; <dependency><groupId>com.dameng</groupId><artifactId>DmJdbcDriver18</artifactId><version>8.1.1.193</version> </dependency> 2.在nacos-config模塊pom.xml添加依賴&#xff1…

4.9-4.10學習總結 Stream流練習+方法引用+異常

Stream流練習&#xff1a; 1.打印數組內的偶數。 import java.util.*; import java.util.function.BiConsumer; public class test {public static void main(String[] args) {ArrayList<Integer> listnew ArrayList<>();Collections.addAll(list,1,2,3,4,5,6,7,…

FPGA系統開發板調試過程不同芯片的移植步驟介紹

目錄 1.我目前使用的開發板 2.不同開發板的移植 步驟一&#xff1a;芯片型號設置 步驟二&#xff1a;約束修改 步驟三、IP核更新 關于FPGA系統開發板調試過程中不同芯片的移植。我需要先理清楚FPGA開發中移植到不同芯片的一般流程。首先&#xff0c;移植通常涉及到更換FPG…

復現QGIS-MCP教程

由于Claude國內下載不了嘗試使用Cursor 下載安裝Cursor Cursor - The AI Code Editor 本示例安裝的是0.46版本 UV安裝 簡介 安裝 安裝成功 配置環境變量 驗證 下載代碼 git clone gitgithub.com:jjsantos01/qgis_mcp.git QGIS插件安裝 文件拷貝 您需要將 qgis_mcp_plu…

java筆記03

基本數據類型 數據值是存儲在自己的空間中。 特點&#xff1a;賦值給其他變量&#xff0c;也是賦的真實的值。 引用數據類型 數據值是存儲在其他空間中&#xff0c;自己空間中存儲的是地址值。 特點&#xff1a;賦值給其他變量&#xff0c;賦的地址值。 綜合練習 使用 ctrl…

【開發工具】快速自定義圖標元素的顏色

如果你想要一個輕量級、簡單易用 的小工具來快速自定義圖標元素的顏色&#xff08;比如調整 SVG/PNG 圖標的顏色&#xff0c;或者生成多色圖標&#xff09;&#xff0c;可以試試以下工具&#xff1a; 1. 在線工具&#xff08;無需安裝&#xff09; SVG/PNG 圖標改色 - Recol…

【CompletableFuture】異步編程

CompletableFuture異步編程 CompletableFuture介紹與傳統 Future 的對比使用方法1. 使用 supplyAsync&#xff08;有返回值&#xff09;使用 runAsync&#xff08;無返回值&#xff09;指定自定義線程池 處理異步結果1. thenApply&#xff1a;轉換結果2.thenAccept&#xff1a;…

【TS學習】(23)理解類的雙重角色

在 TypeScript 中&#xff0c;類&#xff08;class&#xff09;不僅是一個運行時的值&#xff08;即可以實例化對象的構造函數&#xff09;&#xff0c;同時也是一個類型聲明。具體來說&#xff0c;類在 TypeScript 中既聲明了值&#xff0c;也聲明了類型&#xff0c;并且它的類…