一、问题
题目要求:保留包含target字符的路径,去掉不包含target的路径
举例,比如有两条路径,f->i->r->e,h->i->r->e,保留包含f的路径,那么h->i->r->e就会被清理掉
输出:返回结果是当前树的根节点
树的结构如下:
class TreeNode {
char val;
TreeNode[] children;
public TreeNode(char val) {
this.val = val;
children = new TreeNode[26];
}
}
二、解决方法:
(1)判断当前节点是否等于target,等于,则后续节点都不会去掉,因此直接return true;
(2)如果当前节点的子节点都返回false,且当前节点不等于target,那么包含该节点在内的所有节点都要去掉;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Main {
public static void main(String[] args) {
/**
* 题目要求:保留包含target字符的路径,去掉不包含target的路径
* 举例,比如有两条路径,f->i->r->e,h->i->r->e,保留包含f的路径,那么h->i->r->e就会被清理掉
* @return 返回结果是当前树的根节点
*/
TreeNode root = initTree();
System.out.println("进行处理前");
showRoute(root);
System.out.println("进行处理后");
findRouteWithTargetDfs(root);
showRoute(root);
}
static char target = 't'; // 保留包含t字符的路径
// 核心代码
private static boolean findRouteWithTargetDfs(TreeNode root) {
if(root == null) {
return false;
}
// 包含了target,后面的都保留
if(root.val == target) {
return true;
}
boolean hasTarget = false; // 子树中只要有一个包含target,那么当前节点就不能被删掉
for(int i = 0; i < root.children.length; i++) {
TreeNode child = root.children[i];
boolean childHasTarget = findRouteWithTargetDfs(child);
if(childHasTarget) {
hasTarget = true;
} else {
// 因为以当前子节点为首的树不包含target信息,去掉这一部分树
root.children[i] = null;
}
}
return hasTarget;
}
/**
* 以下内容不需要关注了,只是单纯初始化基本信息,显示当前所有的路径
*/
// 这里只是一个基础数据的初始化,没有任何含义
private static TreeNode initTree() {
String[] arr = new String[]{"it", "is", "a", "test"};
TreeNode root = new TreeNode('X');
for(String a : arr) {
TreeNode p = root;
for(char c : a.toCharArray()) {
if(p.children[c - 'a'] == null) {
p.children[c - 'a'] = new TreeNode(c);
}
p = p.children[c - 'a'];
}
}
return root;
}
// 展示当前所有的路径,用于验证,和代码本身无关
private static void showRoute(TreeNode root) {
Set<String> ans = new HashSet<>();
searchRouteDfs(root, new ArrayList<>(), ans);
System.out.println(ans);
}
// 一个简单的dfs方法,用于找到所有的路径
private static void searchRouteDfs(TreeNode root,
List<Character> routList, Set<String> allRoute) {
if(isLeaf(root)) {
routList.add(root.val);
allRoute.add(transToStr(routList));
routList.remove(routList.size() - 1);
return ;
}
routList.add(root.val);
for(TreeNode child : root.children) {
if(child == null) continue;;
searchRouteDfs(child, routList, allRoute);
}
routList.remove(routList.size() - 1);
}
private static boolean isLeaf(TreeNode root) {
if(root == null) {
return false;
}
int nullChildCount = 0;
for(int i = 0; i < root.children.length; i++) {
if(root.children[i] == null)nullChildCount++;
}
return nullChildCount == root.children.length;
}
private static String transToStr(List<Character> routList) {
StringBuilder strb = new StringBuilder();
for(int i = 0; i < routList.size(); i++) {
char c = routList.get(i);
strb.append(c);
if(i != routList.size() - 1) {
strb.append("->");
}
}
return strb.toString();
}
}
class TreeNode {
char val;
TreeNode[] children;
public TreeNode(char val) {
this.val = val;
children = new TreeNode[26];
}
}
三、说明
1、本题没有要求bugfree,事实上bugfree确实比较难。
2、原题中,TreeNode里不是字符型的val,而是一个String,并且要求判断包含前缀即可,事实上,这部分使用s.startsWith()即可,但从我的角度来看,这样的设定有失偏颇,有画蛇添足之嫌,并没有影响到考察内容(我认为此题的考察还是树结构的遍历),因此,我对题目做了修改,
(1)改为了更简单的Character类型;
(2)增加了不可删除的root节点(一个固定的大写X),减少特殊场景的适配;