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"都不是有效的时间。

  

原文地址:https://www.cnblogs.com/bonelee/p/12588975.html