代碼隨想錄算法訓練營|第三十二天|332.重新安排行程、51.N皇后、37.解数独、第七章 回溯算法总结篇。刷题心得(c++)

本文记录了作者通过代码随想录进行LeetCode算法训练的过程,涉及332.重新安排行程、51.N皇后、37.解数独的题目。作者在阅读题目后通过代码随想录的理解,逐步掌握了回溯法的思路和应用,包括解题策略、模板和问题类型,如组合、切割、子集、排序、棋盘问题等。同时,作者反思了自己的困难和学习收获,并分享了回溯算法总结图。
摘要由CSDN通过智能技术生成

目录

讀題

332.重新安排行程

自己看到题目的第一想法

看完代码随想录之后的想法

51.N皇后

自己看到题目的第一想法

看完代码随想录之后的想法

37.解数独

自己看到题目的第一想法

看完代码随想录之后的想法

332.重新安排行程 - 實作

思路

Code

51.N皇后 - 實作

思路

Code

37.解数独 - 實作

思路

Code

回溯算法章節總結

感想

回溯法模板

能解決的問題

組合: N個數裡面根據一定規則找出K個數的集合

切割: 字符串按一定規則有幾種切割方式

子集: 一個N個數的集合有多少符合條件的子集

排序: N個數裡面根據一定規則排序,有多少排列方式

棋盤: N皇后、解數獨

图论额外拓展

效能分析: 效能分析目前我無暇去了解,但代碼隨想錄有相關的說明,可以直接去看。

總結

回溯算法總結圖: 这个图是 代码随想录知识星球 (opens new window)成员:莫非毛 (opens new window),所画,总结的非常好,分享给大家。

總結

自己实现过程中遇到哪些困难

今日收获,记录一下自己的学习时长

相關資料

332.重新安排行程

51. N皇后

37. 解数独

总结


讀題

332.重新安排行程

自己看到题目的第一想法

看到第一眼,要返回最小詞序的路程,要去判斷整個路徑要怎麼走,看完題意,以當前我的理解沒辦法馬上想到一個解題思路去解決連終止條件單遞迴要怎麼做也沒有想法,先來看題解。

看完代码随想录之后的想法

  1. 紀錄映射關係,
    1. 一個機場映射多個機場: unorder_map
    2. 多個機場之間有順序 map、multimap、multiset3
    3. 映射關係
      1. unorder_map<string, multiset>: unorder_map<出發機場, 到達機場的集合> → 出發機場與到達機場之間進行映射關係
      2. unorder_map<string, map<string, int>> unorder_map < 出發機場,<到達機場,行班> 出發機場與到達機場的映射關係,並且到達機場也會與航班次數有一個映射關係
      3. 建議使用第二種做法,可使用航班次數來記錄使用次數,假設大於零代表這個目的地能飛,如果等於零,代表不能飛了
  2. 回溯法
    • 模板

      void backtracking(參數) {
      		if(終止條件) {
      				存放結果;
      				return;
      		}
      		
      		for(選擇: 本層集合中的元素) {
      				處理節點
      				backtracking(路徑, 選擇列表)
      				回溯,撤銷處理結果
      		}
      
    • 參數

      • unordered_map<string, map<string, int>> targets → 紀錄航班的映射關係,定義為全局變量不用回溯與遞迴,只是在過程中進行次數的刪減
      • ticketNum 代表有多少航班
      • backtracking 設為bool 因為假設找到一個路徑是合法的,直接將這個路徑一路往上傳
    • 參數初始值

      • 將航班插入到unordered_map中進行映射

        for (const vector<string>& vec : tickets) {
            targets[vec[0]][vec[1]]++; // 记录映射关系
        }
        result.push_back("JFK"); // 起始机场
        

        假設有以下機票

        JFK -> LAX
        JFK -> SFO
        LAX -> SFO
        

        將這些機票放入數據結構中會呈現以下狀態

        {
        		"JFK": {"LAX": 1, "SFO": 1},
        		"LAX": {"SFO": 1}
        }
        
    • 終止條件

      • 假設有四個航班,只要找到機場數量是五,四個航班可以想像程點之間的線,所以四個航班有五個機場,假設機場個數 達到了航班數加一,則終止遞迴
    • 單層搜索的邏輯

      for (pair<const string, int>& target : targets[result[result.size() - 1]]) // 選擇要在map中選擇哪個到達機場 {
          if (target.second > 0 ) { // 紀錄機場是否飛過了
              result.push_back(target.first);
              target.second--;
              if (backtracking(ticketNum, result)) return true; // 假設找到一個路徑符合條件,一路回傳回去,也因此不需要做回溯操作,因為一旦做回溯操作,結果就不對了
              result.pop_back();
              target.second++;
          }
      }
      
      1. 第一次調用會從JFK開始,是因為在for函數中的鍵值對在一開始就是JFK,因為是在result當眾去尋找目的機場。
      2. 在第一次搜索的時候,以上面為例會從LAX開始,假設target.second(航班次數) 大於零則往下遞迴,而整體在JFK開始的航班會有兩班 一個是SFO 另一個是 LAX
      3. 第二次搜索時,會從LAX 有一個航班也就是SFO而LAX也只會跑這一次,因為LAX的只有一個目的機場,也就是只會進行一次迭代
      4. 因為MAP的緣故,裡面的鍵值對都是有序的,但假設是從SFO開始,因為SFO沒有對應的目的機場,所以也不會進入到for迴圈當中
      5. 所以以這個簡單的例子最後會得出JFK → LAX → SFO的路徑

51.N皇后

自己看到题目的第一想法

先去看皇后的移動路徑,在其橫直斜都不能有東西,否則會被攻擊,用回溯有思考到應該就是一層一層去遞迴但如果是直跟橫,我想像的到可能可以用記錄used來避免直的

如果我的判斷式橫得很好避免,就是一層只能有一個位子被選中,直的也很好避免,使用used來避免直的撞車,那橫的我現在想到是每一層跑一次for回圈檢查 當前位置的cur + i cur -i 的位置?

這邊我真的想到有點困擾

看完代码随想录之后的想法

看完發現其實比起之前的題目,這題更多的是考察思路,代碼本身不難,跟著代碼隨想錄一題題的走下來,理解上不難

其實主要還是三點

終止條件要想清楚 row = n 代表走到底了

單層搜索邏輯

  • 確認當前位置是否合法
  • 合法→ 放置皇后, 不合法 → 跳過
  • 進行遞迴
  • 進行回溯

isValid

檢查chessboard的列以及135與45度角是否有Q存在,如果是則return false,如果全部檢查都不是return true

  • hint: 因為每一層遞迴都會是底所以只要查看135度角以及45度角是否有數值

37.解数独

自己看到题目的第一想法

當下想一想以為跟N皇后很像,但發現N皇后每次只專注在一層,但在數獨上,需要考慮的不僅僅是橫的也需要考慮列,在實現上有困難,思路沒有打開

看完代码随想录之后的想法

學到了二層遞迴的概念,因為一題題的理解,雖然目前沒有辦法自己單獨寫出來,但一刷就跟著卡哥的思路一步步地做下來

332.重新安排行程 - 實作

思路

  1. 紀錄映射關係:unorder_map<string, map<string, int>> unorder_map < 出發機場,<到達機場,行班>
  2. 回溯函數
    1. 參數
      1. vector<string>& result → 紀錄航班的路徑
      2. ticketNum 代表有多少航班
      3. backtracking 設為bool 假設找到一個路徑是合法的,直接將這個路徑一路往上傳
    2. 終止條件 result.size() == ticketNums + 1
    3. 單層搜索的邏輯
      1. 選擇要在map中選擇哪個到達機場
      2. target.second > 0 判斷是否還可以飛
      3. 處理節點
      4. 遞迴 ,假設找到一個路徑符合條件,一路回傳回去
      5. 回溯
  3. 主函數
    1. 建立results
    2. 將航班插入到unordered_map中進行映射
    3. 遞迴
    4. 回傳結果

Code

class Solution {
public:
    unordered_map<string, map<string, int>> target;

    bool backtracking(vector<string>& result, int ticketNums) {
        if(result.size() == ticketNums + 1 ) return true;

        for(pair<const string, int>& target : target[result[result.size()-1]]) {
            if(target.second > 0){
                result.push_back(target.first);
                target.second--;
                if(backtracking(result, ticketNums)) return true;
                result.pop_back();
                target.second++;
            }
        }
        return false;
    }

    vector<string> findItinerary(vector<vector<string>>& tickets) {
        target.clear();
        vector<string> result;
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二算法训练营的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理和应用能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值