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