zoukankan      html  css  js  c++  java
  • DFS模板

    ****************************************************************************DFS算法******************************************************************************************************
    深度搜索
    ————————————————————————————————————————————————————————————————————————————————————————————————
    二叉树的遍历:https://www.cnblogs.com/rnanprince/p/11595380.html
    二叉树-前序&中序:https://www.cnblogs.com/rnanprince/p/11588434.html
    二叉树的递归(DFS):https://www.cnblogs.com/rnanprince/p/11595176.html
    
    ————————————————————————————————————————————————————————————————————————————————————————————————
    给一棵二叉树, 找到和为最小的子树, 返回其根节点。
    public class Solution {
        private TreeNode subtree = null;
        private int subtreeSum = Integer.MAX_VALUE;
    
        public TreeNode findSubtree(TreeNode root) {
            helper(root);
            return subtree;
        }
        
        private int helper(TreeNode root) {
            if (root == null) {
                return 0;
            }
            
            int sum = helper(root.left) + helper(root.right) + root.val;
            if (sum <= subtreeSum) {
                subtreeSum = sum;
                subtree = root;
            }
            return sum;
        }
    }
    
    ————————————————————————————————————————————————————————————————————————————————————————————————
    求从根(root)到叶(leaf)的所有路径
    Input:{1,2,3,#,5}
    Output:["1->2->5","1->3"]
    Explanation:
       1
     /   
    2     3
     
      5
    
    public class Solution {
    
        public List<String> binaryTreePaths(TreeNode root) {
            List<String> paths = new ArrayList<>();
            if (root == null) {
                return paths;
            }
            
            List<String> leftPaths = binaryTreePaths(root.left);
            List<String> rightPaths = binaryTreePaths(root.right);
            for (String path : leftPaths) {
                paths.add(root.val + "->" + path);
            }
            for (String path : rightPaths) {
                paths.add(root.val + "->" + path);
            }
            
            // root is a leaf
            if (paths.size() == 0) {
                paths.add("" + root.val);
            }
            
            return paths;
        }
    }
    
    ————————————————————————————————————————————————————————————————————————————————————————————————
    给定一棵二叉树,找到两个节点的最近公共父节点(LCA)。
    最近公共祖先是两个节点的公共的祖先节点且具有最大深度。
    public class Solution {
        // 在root为根的二叉树中找A,B的LCA:
        // 如果找到了就返回这个LCA
        // 如果只碰到A,就返回A
        // 如果只碰到B,就返回B
        // 如果都没有,就返回null
        public TreeNode lowestCommonAncestor(TreeNode root, TreeNode node1, TreeNode node2) {
            if (root == null || root == node1 || root == node2) {
                return root;
            }
            
            // Divide
            TreeNode left = lowestCommonAncestor(root.left, node1, node2);
            TreeNode right = lowestCommonAncestor(root.right, node1, node2);
            
            // Conquer
            if (left != null && right != null) {
                return root;
            } 
            if (left != null) {
                return left;
            }
            if (right != null) {
                return right;
            }
            return null;
        }
    }
    
    
    
    
    		   1
    		 /   
    		2     3
    	   /    /  
    	  9	  5 8    7
    	/            
       6   0           4
    
    
    参考:九章算法
    https://www.jiuzhang.com/
    https://www.jiuzhang.com/solutions/minimum-subtree/
    https://www.jiuzhang.com/solutions/binary-tree-paths/
    

      

    深度搜索
    适用场景:
    输入数据:如果是递归数据结构,如单链表,二叉树,集合,则百分之百可以用深搜;如果是非递归数据结构,如一维数组,二维数组,字符串,图,则概率小一些。
    状态转换图:树或者DAG。
    求解目标:多阶段存在性问题。必须要走到最深(例如对于树,必须要走到叶子节点)才能得到一个解,这种情况适合用深搜。
    
    
    dfs模板:
    void dfs(input, path, result, cur) {
    	if (数据非法) return; // 终止条件
    	
    	if (cur == input.size()) { // 收敛条件 or if (gap == 0) {		
    		将path放入result
    		// result.append(list(path))
    	}
    	
    	if (可以剪枝) return;
    	
    	for(step in range(cur,end)) { // 执行所有可能的扩展动作
    		执行动作,修改path, 排列类的题目交换元素(重复元素加一个判断 if input[i] in input[cur:i]: continue)  
    		dfs(input, path, step + 1 or cur + 1, result);
    		恢复path,排列类的题目恢复交换元素
    	}
    }
    
    --------------------------------
    
    过程分析:
    cur =0
     | 
    |/
    “catsanddog” {"a", "abnormal", "cat", "cats", "sand", "dog", "and"} == > ["cats,and,dog", "cat","sand", "dog"]
       cur
       /  
    cat  cats
     |     |
    sand  and
     |     |
    dog   dog
    
    "hello" ==> "hello"
    
     
    
    import sys
    import json
    
     
    def load_data():
        data = json.loads(sys.stdin.read())
        return data["string"], set(data["word_list"])
    
    
     
    def dfs(input_str, cur, word_dict, path, result):
        if cur == len(input_str):
            result.append(",".join(path))
            return
        for i in range(cur, len(input_str)):
            word = input_str[cur:i+1]
            if word in word_dict:
                path.append(word)
                dfs(input_str, i+1, word_dict, path, result)
                path.pop()
    	
     
    def main():
        string, word_dict = load_data()
        result, path = [], []
        cur = 0
        dfs(string, cur, word_dict, path, result)
        result.sort()
        print(json.dumps({'result': result}))
    

      

    dfs先序遍历题目:
    (1)二叉树的所有路径:用到第一次说到的模板
    (2)将二叉树 转为 链表
    https://www.cnblogs.com/bonelee/p/11610194.html
    https://www.cnblogs.com/bonelee/p/11609991.html
    
    
    dfs中序遍历题目:
    需要使用中序遍历迭代写法的:
    https://www.cnblogs.com/bonelee/p/11610292.html
    
    
    后序遍历题目:
    https://www.cnblogs.com/bonelee/p/11609953.html
    
    
    遍历过程中需要记住上次遍历节点才能得到结果的:
    https://www.cnblogs.com/bonelee/p/11610047.html 
    
    
    算法模板:
    class Solution():
       last_node = None
       result = None
        
       def run(self, root):
            self.dfs(root)
            return self.result
        
       def dfs(self):
           # 放在最前面就是先序
           if last_node is None:
               last_node = root
           else:
               do_sth(last_node, root)
    		   last_node = root
                
           dfs(root.left)
     
           dfs(root.right)
    

      

    BST定义:
    https://baike.baidu.com/item/%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91/7077855?fr=aladdin
    
    BST中搜索特定值:
    https://www.cnblogs.com/bonelee/p/11624355.html
    搜索接近的值:
    https://www.cnblogs.com/bonelee/p/11632584.html
    
    
    
    【1,3,5,8,88,101,234】, 89
    

      

    基于排列的深度优先搜索
    https://blog.csdn.net/qq_19446965/article/details/102463792
    问题模型:求出所有满足条件的“排列”。
    判断条件:组合中的元素是顺序“相关”的。
    时间复杂度:与 n! 相关。
    
    
    //全排列-A(n,m)
    def recursive_a(m, path_list, original_list, result_list):
        if m == 0:
            result_list.append(list(path_list))
            return
        for i in range(len(original_list)):
            path_list.append(original_list[i])
            temp_list = deepcopy(original_list)
            temp_list.pop(i)
            recursive_a(m - 1, path_list, temp_list, result_list)
            path_list.pop()
    
    求A(4,3)
    original_list = [1, 2, 3, 4]
    result_list = [[1, 2, 3], [1, 2, 4], [1, 3, 2], [1, 3, 4], [1, 4, 2], [1, 4, 3], [2, 1, 3], [2, 1, 4], [2, 3, 1], [2, 3, 4], [2, 4, 1], [2, 4, 3], [3, 1, 2], [3, 1, 4], [3, 2, 1], [3, 2, 4], [3, 4, 1], [3, 4, 2], [4, 1, 2], [4, 1, 3], [4, 2, 1], [4, 2, 3], [4, 3, 1], [4, 3, 2]]
    共24种
    
    https://blog.csdn.net/qq_19446965/article/details/102463792		
    
    给定一个可包含重复数字的序列,返回所有不重复的全排列。
    示例:
    输入: [1,1,2]
    输出:
    [
      [1,1,2],
      [1,2,1],
      [2,1,1]
    ]
    
    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/permutations-ii
    
    先生成再去重
    def helper(self, nums, res, path):
        if not nums and path not in res:
            res.append(path)
        else:
            for i in range(len(nums)):
                self.helper(nums[:i] + nums[i+1:], res, path + [nums[i]])
    			
    耗时:1356 ms
    这样的做法是在每个path生成之后才去做的判断,因此效率一点都不高。
    
    
    深度拷贝:
    def helper(self, nums, res, path):
        if not nums and path not in res:
            res.append(list(path))
            return
    
        for i in range(len(nums)):
            path.append(nums[i])
            temp_list = deepcopy(nums)
            temp_list.pop(i)
            self.helper(temp_list, res, path)
            path.pop()		
    
    耗时:超时
    
    回溯法
    下面这个做法是标准的回溯法,需要用到visited来表示哪些位置已经被添加到path中了。
    为什么有重复?
    在这个例子中,我们在第一个1开始的排列中已经取了第二个1的情况;如果在第二个1开始的排列中仍然取第一个1,就有重复了。
    如何去重呢?
    1)排序
    2)不是第一个数字,并且现在的数字和前面的数字相等,同时前面的数字还没有访问过,我们是不能搜索的,需要直接返回。
    原因是,这种情况下,必须是由前面搜索到现在的这个位置,而不能是由现在的位置向前面搜索。
    
    def helper(self, nums, res, path, visit):
    
        if len(path) == len(nums) :
            res.append(path)
            
        for i in range(len(nums)):
            if i > 0 and visit[i - 1] and nums[i - 1] == nums[i]:
        		continue
                
            if not visit[i]:
                visit[i] = True
                self.helper(nums, res, path+[nums[i]], visit)
                visit[i] = False
    耗时:56 ms
    
    
    交换思想:
    def swap(nums, i, cur):
        nums[cur], nums[i] = nums[i], nums[cur]
    
    
    def helper_1(cur, nums, res):
        if cur == len(nums):
            res.append(list(nums))
    
        for i in range(cur, len(nums)):
            if nums[i] not in nums[i+1:]:
                swap(nums, i, cur)
                helper(cur + 1, nums, res)
                swap(nums, i, cur)
    			
    耗时:44 ms		
    
    
    题目描述
    给定一个"HH:MM"格式的时间,重复使用这些数字,返回下一个最近的时间。每个数字可以被重复使用任意次。
    保证输入的时间都是有效的。例如,"01:34","12:09" 都是有效的,而"1:34","12:9"都不是有效的时间。
    

      

  • 相关阅读:
    .net中的委托
    GridView, DataList and ListBox 行 单击与双击事件处理
    ChineseCalendar类[转]
    数据契约(DataContract)
    XPath 语法(复习)
    正则表达式学习笔记
    瑞星笔试:现场上机做题[转]
    发送带有附件的电子邮件使用 Cdosys.dll 库
    DataContractJsonSerializer 类 操作json类型数据
    i guess a bug on Castle
  • 原文地址:https://www.cnblogs.com/bonelee/p/12588975.html
Copyright © 2011-2022 走看看