leetcode hot100回溯

1️⃣3️⃣ 回溯

46. 全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

题解:

  • 全排列的题很适合用回溯来做, 就是在n个数中按不同顺序选择数据填入, 定义递归函数bT(first,oput), oput为当前排列顺序, first标识排列到第第first位置, 如果first=n,即为当前排列结束

  • 回溯重要操作是如何撤回上一步已经结束了的操作, 本题使用oput数组记录排列的顺序, 那么撤回操作也就是在oput上操作

  • class Solution {
        
        public static void backTracking(int n ,List<Integer> output, List<List<Integer>> res, int first){
            if(first==n){
                res.add(new ArrayList<Integer>(output));
            }
            for(int i=first;i<n;i++){
                Collections.swap(output,first,i);
                backTracking(n,output,res,first+1);
                // 动作完成, 撤销
                Collections.swap(output,first,i);
            }
            
        }
    
    
        public List<List<Integer>> permute(int[] nums) {
            int n = nums.length;
            List<List<Integer>> res = new ArrayList<List<Integer>>();
            List<Integer> output = new ArrayList<Integer>();
            for(int num : nums){
                output.add(num);
            }
            backTracking(n,output,res,0);
            return res;
        }
    }
    

78. 子集

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集).解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

题解:

  • 首先可以和上题一样使用回溯, 并且都是数组, 回溯的条件也是差不多,

    • 新建添加的数组长度可以是0,1,…~n-1,那么就是用循环遍历从长度1到长度n, 每个长度使用回溯来添加数据

    • public List<List<Integer>> subsets(int[] nums) {
             int n = nums.length;
             List<List<Integer>> res = new ArrayList<List<Integer>>();
             res.add(new ArrayList<>());
             for(int i=1;i<=n;i++){
                 List<Integer> lst = new ArrayList<>();
                 backTracking(nums,lst,res,i,0);
             }
             return res;
         }
         public void backTracking(int[] nums,List<Integer> lst,List<List<Integer>> res,int i, int start){
             if(lst.size() == i){
                 res.add(new ArrayList<>(lst));
                 return ;
             }
             for(int idx = start;idx<nums.length;idx++){
                 lst.add(nums[idx]);
                 backTracking(nums,lst,res,i,idx+1);
                 lst.remove(lst.size()-1);
             }
         }
      
  • 第二种就是, 从前往后遍历数组, 遇到一个数就把所有子集加到已存在的子集末尾成为一个新的子集

    • public List<List<Integer>> subsets(int[] nums){
              int n = nums.length;
              List<List<Integer>> res = new ArrayList<List<Integer>>();
              res.add(new ArrayList<>());
              for(int i=0;i<n;i++){
                  int size = res.size();
                  for(int j = 0;j<size;j++){
                      List<Integer> newList = new ArrayList<>(res.get(j));
                      newList.add(nums[i]);
                      res.add(newList);
                  }
              }
              return res;
          }
      

17. 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

题解:

  • 逐渐发现且能够感觉到回溯是怎么一回事了, 本题前提就是建立一张号码与字母的映射表, 然后根据数字长度回溯添加某一位的元素即可

  • class Solution {
        char[][] alphabet = new char[][] {
                {'a', 'b', 'c'},
                {'d', 'e', 'f'},
                {'g', 'h', 'i'},
                {'j', 'k', 'l'},
                {'m', 'n', 'o'},
                {'p', 'q', 'r', 's'},
                {'t', 'u', 'v'},
                {'w', 'x', 'y', 'z'}
        };
    
        List<String> res = new ArrayList<String>();
        public List<String> letterCombinations(String digits) {
            int n = digits.length();
            if(n==0) return new ArrayList<String>();
            backTracking(digits,new StringBuilder(),0 );
            return res;
        }
        public void backTracking(String digits,StringBuilder sbr,int length){
            if(length == digits.length()){
                res.add(sbr.toString());
                return ;
            }
            int group = Integer.valueOf(digits.charAt(length))-'2';
            for(char ch : alphabet[group]){
                sbr.append(ch);
                backTracking(digits,sbr,length+1);
                sbr.delete(sbr.length()-1,sbr.length());
            }
        }
    }
    

39. 组合总和

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 对于给定的输入,保证和为 target 的不同组合数少于 150 个。

题解:

  • 回溯的条件, 这里可以重复, 那么要设置一个start整型,标识每次判断从start开始, 并且还能防止重复/相对于一直让idx从0开始遍历, 这里会有重复数据出现

  • 回溯的参数还需要参与遍历的数组, 参与判断的target值, 还有当前总值sum

  • class Solution {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        List<Integer> lst = new ArrayList<>();
        public List<List<Integer>> combinationSum(int[] candidates, int target) {
            int n = candidates.length;
            backTracking(candidates,target,0,0);
            return res;
        }
        public void backTracking(int[] candidates,int target,int cnt,int start){
            if(cnt == target){
                res.add(new ArrayList<>(lst));
                return ;
            }
            for(int i=start;i<candidates.length;i++){
                if(cnt+candidates[i] <= target){
                    lst.add(candidates[i]);
                    backTracking(candidates,target,cnt+candidates[i],i);
                    lst.remove(lst.size() - 1);
                }
            }
        }
    }
    

22. 括号生成

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

题解:

  • 要动态的维护是一个有效的字符组合, 只要保证左括号始终大于右括号的数量即可, 在左右括号均=n时,此时就是解

  • class Solution {
        List<String> res = new ArrayList<String>();
        public List<String> generateParenthesis(int n) {
            StringBuilder sbr = new StringBuilder();
            sbr.append('(');
            backTracking(n,sbr,1,0);
            return res;
        }
        public void backTracking(int n,StringBuilder sbr,int left,int right){
            if(left==right&& left==n){
                res.add(sbr.toString());
                return ;
            }
            if(left < n){
                sbr.append('(');
                backTracking(n,sbr,left+1,right);
                sbr.deleteCharAt(sbr.length()-1);
            }
            if(right<left){
                sbr.append(')');
                backTracking(n,sbr,left,right+1);
                sbr.deleteCharAt(sbr.length()-1);
            }
        }
    }
    

79. 单词搜索

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

题解:

  • 一开始我想的是用可变字符串存储走到相应位置的字符串与目标值比较是否starsWith,但每次比较都需要toString很影响性能,还忘了把已访问过的字符设置为已访问, 所以换个方法

  • 还是采用索引存储的方法, 每次更新索引, 比较word.charAt(idx)与当前字符是否相同, 比较四个方向上的字符, 有一个为true则真 ,并且访问下一个字符前先将当前字符修改位不可见防止访问循环, 回溯时恢复即可

  • class Solution {
        public boolean exist(char[][] board, String word) {
            for(int i=0;i<board.length;i++){
                for(int j=0;j<board[0].length;j++){
                    if(board[i][j]==word.charAt(0)){
                       if(backTracking(board,word,i,j,0)){
                        return true;
                       }
                    }
                }
            }
            return false;
        }
        public  boolean backTracking(char[][] board,String word,int row,int col,int idx){
            if(idx==word.length()){
                return true;
            }
            if (row < 0 || row >= board.length || col < 0 || col >= board[0].length || board[row][col] != word.charAt(idx)) {
                return false;
            }
            char temp = board[row][col];
            board[row][col] = '*';
            boolean isFind = backTracking(board, word, row - 1, col, idx + 1) ||
                            backTracking(board, word, row + 1, col, idx + 1) ||
                            backTracking(board, word, row, col - 1, idx + 1) ||
                            backTracking(board, word, row, col + 1, idx + 1);
            board[row][col] = temp;
            return isFind;
        }
    }
    

131. 分割回文串

给你一个字符串 s,请你将 s 分割成一些 子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。

题解:

  • 根据给出的例子来看, 可以得到几个提示,比如可以根据长度来逐渐判断, 首先是len=1,len=2,…len=n,那么如何判断回文, 搓一个reverse

  • 回溯条件就是start开始拼接字符串的位置==length, 说明判断到了尾部, 此时lst里存储的都是回文字符串, add进res即可

  • class Solution {
        List<List<String>> res = new ArrayList<List<String>>();
        List<String> lst = new ArrayList<>();
        public List<List<String>> partition(String s) {
                backTracking(s,0);
            return res;
        }
        public void backTracking(String s, int start){
            if(start==s.length()){
                res.add(new ArrayList<>(lst));
                return ;
            }
            for(int i=start;i<s.length();i++){
                String sub = s.substring(start,i+1);
                if(isReverse(sub)){
                    lst.add(sub);
                    backTracking(s,i+1);
                    lst.remove(lst.size()-1);
                }
            }
        }
        public boolean isReverse(String s) {
            int left = 0, right = s.length() - 1;
            while (left < right) {
                if (s.charAt(left) != s.charAt(right)) {
                    return false; 
                }
                left++;
                right--;
            }
            return true; // 全部匹配,返回 true
        }
    
    }
    

51. N 皇后

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

题解:

  • 需要的操作有: 判断两个皇后是否在同一斜线上/正斜线和反斜线, 由于每一行只存在一个皇后, 可以根据n的长度维持一个queens, queens[i]即为第i个皇后所在的列; 需要一个回溯函数找出方案,; 需要一个函数来fill填充棋盘

  • 回溯函数:

    • 存储所有数据的res, 存储维护数据的queens,棋盘大小n, 当前大小row, 列回溯集合columns, 正反对角线集合diagnals
    • 皇后放置条件: 第row行存放在第i列, 那么其他行的第i列不能有数据, 即col集合中不能有i, 正反对角线不能有两个皇后成对角线, 那么正对角线: 当前row-i的值不能存在于diagnals1中//不与其他皇后并列一行, 同理反对角线row+i 不存在于diagnals2中
    • 回溯, 将col,diagnals1, diagnals2中的数据移出
  • class Solution {
        public List<List<String>> solveNQueens(int n) {
            List<List<String>> res = new ArrayList<List<String>>();
            int[] queens = new int[n];
            Arrays.fill(queens,-1);
            Set<Integer> columns = new HashSet<Integer>();
            Set<Integer> diagnals1 = new HashSet<Integer>();
            Set<Integer> diagnals2 = new HashSet<Integer>();
            backTracking(res,queens,n,0,columns,diagnals1,diagnals2);
            return res;
        }
        public void backTracking(List<List<String>> res,int[] queens,int n, int row, Set<Integer> columns,Set<Integer> diagnals1,Set<Integer> diagnals2){
            if(row == n){
                List<String> board = generateBoard(queens,n);
                res.add(board);
            }else{
                for(int i=0;i<n;i++){
                    if(columns.contains(i)){
                        continue;
                    }
                    int diagnal1 = row - i;
                    int diagnal2 = row + i;
                    if(diagnals1.contains(diagnal1)){
                        continue;
                    }
                    if(diagnals2.contains(diagnal2)){
                        continue;
                    }
                    queens[row] = i;
                    columns.add(i);
                    diagnals1.add(diagnal1);
                    diagnals2.add(diagnal2);
                    backTracking(res,queens,n,row+1,columns,diagnals1,diagnals2);
                    queens[row] = -1;
                    columns.remove(i);
                    diagnals1.remove(diagnal1);
                    diagnals2.remove(diagnal2);
                }
            }
        }
        public List<String> generateBoard(int[] queens, int n){
            List<String> board = new ArrayList<String>();
            for(int i=0;i<n;i++){
                char[] row = new char[n];
                Arrays.fill(row,'.');
                row[queens[i]] = 'Q';
                board.add(new String(row));
            }
            return board;
        }
    }
    
### LeetCode Hot 100 Problems Python Solutions 以下是针对LeetCode热门100题中的部分经典题目提供Python实现方案: #### 两数之和 (Two Sum) 通过暴力解法可以遍历数组两次来找到目标值对应的索引位置[^1]。 ```python class Solution(object): def twoSum(self, nums, target): for i in range(len(nums)): for j in range(i + 1, len(nums)): if nums[i] + nums[j] == target: return [i, j] return [] ``` 另一种更高效的解决方案是利用哈希表减少时间复杂度至O(n)[^4]: ```python class Solution(object): def twoSum(self, nums, target): hash_map = {} for index, value in enumerate(nums): complement = target - value if complement in hash_map: return [hash_map[complement], index] hash_map[value] = index return [] ``` --- #### 组合总和 (Combination Sum) 此问题可以通过回溯算法解决,递归构建满足条件的结果集[^2]。 ```python class Solution: def combinationSum(self, candidates, target): result = [] def backtrack(remain, comb, start): if remain == 0: result.append(list(comb)) return elif remain < 0: return for i in range(start, len(candidates)): comb.append(candidates[i]) backtrack(remain - candidates[i], comb, i) comb.pop() backtrack(target, [], 0) return result ``` --- #### 全排列 (Permutations) 对于全排列问题,同样采用回溯方法生成所有可能的排列组合。 ```python class Solution: def permute(self, nums): res = [] def backtrack(path, options): if not options: res.append(path[:]) return for i in range(len(options)): path.append(options[i]) backtrack(path, options[:i] + options[i+1:]) path.pop() backtrack([], nums) return res ``` --- #### 最长连续序列 (Longest Consecutive Sequence) 该问题的核心在于使用集合数据结构优化查找效率,从而降低整体的时间复杂度[^3]。 ```python class Solution: def longestConsecutive(self, nums): longest_streak = 0 num_set = set(nums) for num in num_set: if num - 1 not in num_set: current_num = num current_streak = 1 while current_num + 1 in num_set: current_num += 1 current_streak += 1 longest_streak = max(longest_streak, current_streak) return longest_streak ``` --- #### 字符串中所有字母异位词 (Find All Anagrams in a String) 滑动窗口配合字符计数器能够高效解决问题。 ```python from collections import defaultdict class Solution: def findAnagrams(self, s, p): need = defaultdict(int) window = defaultdict(int) valid = 0 for c in p: need[c] += 1 left, right = 0, 0 res = [] while right < len(s): c = s[right] right += 1 if c in need: window[c] += 1 if window[c] == need[c]: valid += 1 while right - left >= len(p): if valid == len(need): res.append(left) d = s[left] left += 1 if d in need: if window[d] == need[d]: valid -= 1 window[d] -= 1 return res ``` --- #### 两数相加 (Add Two Numbers) 链表操作的经典案例之一,需注意进位处理逻辑。 ```python class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next class Solution: def addTwoNumbers(self, l1, l2): dummy_head = ListNode(0) curr = dummy_head carry = 0 while l1 or l2 or carry: x = l1.val if l1 else 0 y = l2.val if l2 else 0 total = x + y + carry carry = total // 10 curr.next = ListNode(total % 10) curr = curr.next if l1: l1 = l1.next if l2: l2 = l2.next return dummy_head.next ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值