回溯算法詳解

目錄

回溯算法詳解

回溯VS遞歸

回溯算法的實現過程

n個結點構造多本節要討論的是當給定 n(n>=0)個結點時,可以構建多少種形態不同的樹。


回溯算法詳解

回溯算法,又稱為“試探法”。解決問題時,每進行一步,都是抱著試試看的態度,如果發現當前選擇并不是最好的,或者這么走下去肯定達不到目標,立刻做回退操作重新選擇。這種走不通就回退再走的方法就是回溯算法。

例如,在解決列舉集合 {1,2,3} 中所有子集的問題中,就可以使用回溯算法。從集合的開頭元素開始,對每個元素都有兩種選擇:取還是舍。當確定了一個元素的取舍之后,再進行下一個元素,直到集合最后一個元素。其中的每個操作都可以看作是一次嘗試,每次嘗試都可以得出一個結果。將得到的結果綜合起來,就是集合的所有子集。

實現代碼為:

  1. #include <stdio.h>
  2. //設置一個數組,數組的下標表示集合中的元素,所以數組只用下標為1,2,3的空間
  3. int set[5];
  4. //i代表數組下標,n表示集合中最大的元素值
  5. void PowerSet(int i,int n){
  6. //當i>n時,說明集合中所有的元素都做了選擇,開始判斷
  7. if (i>n) {
  8. for (int j=1; j<=n; j++) {
  9. //如果樹組中存放的是 1,說明在當初嘗試時,選擇取該元素,即對應的數組下標,所以,可以輸出
  10. if (set[j]==1) {
  11. printf("%d ",j);
  12. }
  13. }
  14. printf("\n");
  15. }else{
  16. //如果選擇要該元素,對應的數組單元中賦值為1;反之,賦值為0。然后繼續向下探索
  17. set[i]=1;PowerSet(i+1, n);
  18. set[i]=0;PowerSet(i+1, n);
  19. }
  20. }
  21. int main() {
  22. int n=3;
  23. for (int i=0; i<5; i++) {
  24. set[i]=0;
  25. }
  26. PowerSet(1, n);
  27. return 0;
  28. }

運行結果:

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

回溯VS遞歸

很多人認為回溯和遞歸是一樣的,其實不然。在回溯法中可以看到有遞歸的身影,但是兩者是有區別的。

回溯法從問題本身出發,尋找可能實現的所有情況。和窮舉法的思想相近,不同在于窮舉法是將所有的情況都列舉出來以后再一一篩選,而回溯法在列舉過程如果發現當前情況根本不可能存在,就停止后續的所有工作,返回上一步進行新的嘗試。

遞歸是從問題的結果出發,例如求 n!,要想知道 n!的結果,就需要知道 n*(n-1)! 的結果,而要想知道 (n-1)! 結果,就需要提前知道 (n-1)*(n-2)!。這樣不斷地向自己提問,不斷地調用自己的思想就是遞歸。

回溯和遞歸唯一的聯系就是,回溯法可以用遞歸思想實現。

回溯算法的實現過程

使用回溯法解決問題的過程,實際上是建立一棵“狀態樹”的過程。例如,在解決列舉集合{1,2,3}所有子集的問題中,對于每個元素,都有兩種狀態,取還是舍,所以構建的狀態樹為:


?


圖1 狀態樹


回溯算法的求解過程實質上是先序遍歷“狀態樹”的過程。樹中每一個葉子結點,都有可能是問題的答案。圖 1 中的狀態樹是滿二叉樹,得到的葉子結點全部都是問題的解。

在某些情況下,回溯算法解決問題的過程中創建的狀態樹并不都是滿二叉樹,因為在試探的過程中,有時會發現此種情況下,再往下進行沒有意義,所以會放棄這條死路,回溯到上一步。在樹中的體現,就是在樹的最后一層不是滿的,即不是滿二叉樹,需要自己判斷哪些葉子結點代表的是正確的結果。

n個結點構造多本節要討論的是當給定 n(n>=0)個結點時,可以構建多少種形態不同的樹。

如果兩棵樹中各個結點的位置都一一對應,可以說這兩棵樹相似。如果兩棵樹不僅相似,而且對應結點上的數據也相同,就可以說這兩棵樹等價。本節中,形態不同的樹指的是互不相似的樹。

前面介紹過,對于任意一棵普通樹,通過孩子兄弟表示法的轉化,都可以找到唯一的一棵二叉樹與之對應。所以本節研究的題目也可以轉化成:n 個結點可以構建多少種形態不同的二叉樹。

每一棵普通樹對應的都是一棵沒有右子樹的二叉樹,所以對于 n 個結點的樹來說,樹的形態改變是因為除了根結點之外的其它結點改變形態得到的,所以,n 個結點構建的形態不同的樹與之對應的是 n-1 個結點構建的形態不同的二叉樹。

如果 tn?表示 n 個結點構建的形態不同的樹的數量,bn?表示 n 個結點構建的形態不同的二叉樹的數量,則兩者之間有這樣的關系:tn=bn-1

【方法一】
最直接的一種方法就是推理。當 n=0 時,只能構建一棵空樹;當 n=2 時,可以構建 2 棵形態不同的二叉樹,如圖 1(A);當 n=3 時,可以構建 5 棵形態互不相同的二叉樹,如圖 1(B)。


圖 1 不同形態的二叉樹


對于具有 n( n>1 )個結點的二叉樹來說,都可以看成是一個根結點、由 i 個結點組成的左子樹和由?n-i-1?個結點組成的右子樹。

當 n=1 時,也適用,只不過只有一個根結點,沒有左右孩子(i=0)。

可以得出一個遞推公式:


?


?

通過對公式一步步的數學推算,最后得出,含有 n 個結點的不相似的二叉樹的數量為:


【方法二】

從遍歷二叉樹的角度進行分析,對于任意一棵二叉樹來說,它的前序序列和中序序列以及后序序列都是唯一的。其實是這句話還可以倒過來說,只要確定了一棵二叉樹的三種遍歷序列中的兩種,那么這棵二叉樹也可以唯一確定。

例如,給定了一個二叉樹的前序序列和中序序列分別為:

前序序列:A B C D E F G
中序序列:C B E D A F G

可以唯一得到的二叉樹如圖 2(4):


圖 2 構造二叉樹的過程示意圖


分析:通過前序序列得知,結點A為二叉樹的根結點,結合中序序列,在結點 A 左側的肯定為其左孩子中的所有結點,右邊為右孩子的所有結點,如圖 2(1)所示。

再分析 A 結點的左孩子,在前序序列看到,結點 A 后緊跟的是結點 B,由此斷定結點 A 的左孩子是 B,再看中序序列,結點 B 左側只有一個結點 C ,為 B 的左孩子,結點 B 右側的結點E 和 D 為右孩子,如圖 2(2)。

再分析結點 B 的右孩子,前序序列看到,結點 D 在 E 的前邊,所有 D 為 B 的右孩子。在中序序列中,結點 E 在 D 前邊,說明 E 是 D 的左孩子,如圖 2(3)。

最后分析結點 A 的右孩子,由前序序列看到, F 在 G 前邊,說明F為根結點。在中序序列中也是如此,說明,G 是 F 的右孩子。如圖 2(4)所示。

如果要唯一確定一棵二叉樹,必須知道至少兩種遍歷序列。如果只確定一種序列,無法準確判定二叉樹的具體構造。


?


圖 3 前序序列(1,2,3)的二叉樹


如圖 3 所示為前序序列(1,2,3)構建的不同形態的二叉樹,他們的中序序列各不相同。所以不同形態二叉樹的數目恰好就是前序序列一定的情況下,所能得到的不同的中序序列的個數。

中序序列是對二叉樹進行中序遍歷獲得的,遍歷的過程實質上就是結點數據進棧出棧的過程。所以,中序序列的個數就是數列(1,2,3)按1-2-3的順序進棧,各元素選擇在不同的時間點出棧,所獲的的不同的出棧順序即為中序序列,而中序序列的數目,也就是不同形態的二叉樹的個數。


?


圖 4 中序遍歷時進棧和出棧的過程


根據數列中數據的個數 n,所得到的排列順序的數目為:


?


?

通過以上兩種方式,都可以知道 n 個結點能構建的不同形態的二叉樹的數量,再結合?tn=bn-1,就可以計算出 n 個結點能構建的不同形態的樹的個數。

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

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

相關文章

主成分分析Python代碼

對于主成分分析詳細的介紹&#xff1a;主成分分析&#xff08;PCA&#xff09;原理詳解https://blog.csdn.net/zhongkelee/article/details/44064401 import numpy as np import pandas as pd標準PCA算法 def standeredPCA(data,N): #data:…

【golang】鏈表(List)

List實現了一個雙向鏈表&#xff0c;而Element則代表了鏈表中元素的結構。 可以把自己生成的Element類型值傳給鏈表嗎&#xff1f; 首先來看List的四種方法。 MoveBefore方法和MoveAfter方法&#xff0c;它們分別用于把給定的元素移動到另一個元素的前面和后面。 MoveToFro…

十種排序算法(附動圖)

排序算法 一、基本介紹 ? 排序算法比較基礎&#xff0c;但是設計到很多計算機科學的想法&#xff0c;如下&#xff1a; ? 1、比較和非比較的策略 ? 2、迭代和遞歸的實現 ? 3、分而治之思想 ? 4、最佳、最差、平均情況時間復雜度分析 ? 5、隨機算法 二、排序算法的分類 …

RabbitMq-1基礎概念

RabbitMq-----分布式中的一種通信手段 1. MQ的基本概念&#xff08;message queue,消息隊列&#xff09; mq:消息隊列&#xff0c;存儲消息的中間件 分布式系統通信的兩種方式&#xff1a;直接遠程調用&#xff0c;借助第三方完成間接通信 消息的發送方是生產者&#xff0c…

面試熱題(二叉樹的鋸齒形層次遍歷)

給你二叉樹的根節點 root &#xff0c;返回其節點值的 鋸齒形層序遍歷 。&#xff08;即先從左往右&#xff0c;再從右往左進行下一層遍歷&#xff0c;以此類推&#xff0c;層與層之間交替進行&#xff09; 輸入&#xff1a;root [3,9,20,null,null,15,7] 輸出&#xff1a;[[3…

MySQL數據庫-字符串函數詳解

前言 MySQL數據庫提供了多種不同類型的函數&#xff0c;用于處理字符串、日期、數值等數據類型&#xff0c;以及實現條件、聚合等操作&#xff0c;下面我們主要介紹字符串函數 CONCAT() 函數 CONCAT() 可用于將多個字符串連接在一起。 示例&#xff1a; SELECT CONCAT(Hell…

C++ STL stack queue

目錄 一.stack 介紹 二.stack 使用 三.stack 模擬實現 普通版本&#xff1a; 適配器版本&#xff1a; 四.queue的介紹 五. queue使用 六.queue模擬實現 七.deque介紹 1.容器適配器 2.deque的簡單介紹 3.deque的缺陷 4.為什么選擇deque作為stack和queue的底層默認容…

System.Text.Encoding不同字符編碼之間進行轉換

System.Text.Encoding 是 C# 中用于處理字符編碼和字符串與字節之間轉換的類。它提供了各種靜態方法和屬性&#xff0c;用于在不同字符編碼之間進行轉換&#xff0c;以及將字符串轉換為字節數組或反之。 在處理多語言文本、文件、網絡通信以及其他字符數據的場景中&#xff0c…

Spring Boot 獲取前端參數

Spring Boot 獲取前端參數 在開發 Web 應用程序時&#xff0c;前端參數是非常重要的。Spring Boot 提供了多種方法來獲取前端參數&#xff0c;本文將介紹其中的一些常用方法。 1. 使用 RequestParam 注解 RequestParam 注解是 Spring MVC 提供的一種常用方式&#xff0c;用于…

C++ 函數

函數是一組一起執行一個任務的語句。每個 C 程序都至少有一個函數&#xff0c;即主函數 main() &#xff0c;所有簡單的程序都可以定義其他額外的函數。 您可以把代碼劃分到不同的函數中。如何劃分代碼到不同的函數中是由您來決定的&#xff0c;但在邏輯上&#xff0c;劃分通常…

pycharm調整最大堆發揮最大

python程序運行時&#xff0c;怎么提高效率&#xff0c;設置pycharm最大堆過程如下&#xff1b; 一、進入設置pycharm最大堆&#xff1b; 二、進入設置pycharm最大堆&#xff1b; 如果8g設置為6g左右&#xff0c;占75%左右最佳

5個實用的 Vue 技巧

在這篇文章中&#xff0c;我們將探討五個實用的 Vue 技巧&#xff0c;這些技巧可以使你日常使用 Vue 編程更高效、更富有成效。無論你是Vue的初學者還是經驗豐富的開發者&#xff0c;這些技巧都能幫助你編寫更清晰、更簡潔、更有效的代碼。那么&#xff0c;讓我們開始吧。 1. …

9.1 C++ STL 排序、算數與集合

C STL&#xff08;Standard Template Library&#xff09;是C標準庫中的一個重要組成部分&#xff0c;提供了豐富的模板函數和容器&#xff0c;用于處理各種數據結構和算法。在STL中&#xff0c;排序、算數和集合算法是常用的功能&#xff0c;可以幫助我們對數據進行排序、統計…

【JVM】JVM中的分代回收

文章目錄 分代收集算法什么是分代分代收集算法-工作機制MinorGC、 Mixed GC 、 FullGC的區別是什么 分代收集算法 什么是分代 在java8時&#xff0c;堆被分為了兩份&#xff1a; 新生代和老年代【1&#xff1a;2】 其中&#xff1a; 對于新生代&#xff0c;內部又被分為了三…

eclipse常用設置

1、調整編輯頁面字體大小 窗口 (Window)- 首選項&#xff08;Preferences&#xff09;- 常規&#xff08;General&#xff09;- 外觀 (Appearence)- 顏色與字體 (Colors And Fonts)&#xff0c;在右邊的對話框里選擇 Java - Java Editor Text Font&#xff0c;點擊出現的修改&…

【ARM 嵌入式 編譯系列 3.3 -- gcc 動態庫與靜態庫的鏈接方法介紹】

文章目錄 1.1 GCC 鏈接器 LD 介紹1.1.1 GCC 鏈接器 LD 常用參數介紹1.2 動態庫和靜態庫介紹1.2.1 動態庫和靜態庫優缺點1.2.2 庫文件鏈接方式1.2.3 ldd 工具介紹1.2.4 靜態庫鏈接時搜索路徑順序1.2.5 動態庫鏈接時、執行時搜索路徑順序1.2.6 頭文件搜索路徑1.2.7 有關環境變量上…

Neo4j之Aggregation基礎

在 Neo4j 中&#xff0c;聚合&#xff08;Aggregation&#xff09;是對數據進行計算、匯總和統計的過程。以下是一些使用聚合函數的常見例子&#xff0c;以及它們的解釋&#xff1a; 計算節點數量&#xff1a; MATCH (p:Person) RETURN count(p) AS totalPersons;這個查詢會計…

Socks5代理在多線程爬蟲中的應用

在進行爬蟲開發過程中&#xff0c;我們常常需要處理大量的數據&#xff0c;并執行多任務并發操作。然而&#xff0c;頻繁的請求可能會引起目標網站的反爬機制&#xff0c;導致IP封禁或限制訪問。為了規避這些限制&#xff0c;我們可以借助Socks5代理的強大功能&#xff0c;通過…

Nginx反向代理技巧

跨域 作為一個前端開發者來說不可避免的問題就是跨域&#xff0c;那什么是跨域呢&#xff1f; 跨域&#xff1a;指的是瀏覽器不能執行其他網站的腳本。它是由瀏覽器的同源策略造成的&#xff0c;是瀏覽器對javascript施加的安全限制。瀏覽器的同源策略是指協議&#xff0c;域名…

2011-2021年數字普惠金融指數Bartik工具變量法(含原始數據和Bartik工具變量法代碼)

2011-2021年數字普惠金融指數Bartik工具變量法&#xff08;含原始數據和Bartik工具變量法代碼&#xff09; 1、時間&#xff1a;2011-2020&#xff08;省級、城市&#xff09;&#xff0c;2014-2020&#xff08;區縣&#xff09; 2、原始數據來源&#xff1a;北大金融研究中心…