前言

这题是极其麻烦极其麻烦的一道题(前提是你不知道它有套路)……
我们不讲那些歪门邪道,我们正儿八经的解一下,想正经求解,很麻烦很麻烦。。。

题目要求

P1305题题解

分析

这题你看着容易,那是你想当然认为建立一棵树,给你的序列就是先根的,其实真实情况里不一定是……(当然本题是,所以是橙题……)

这题肝的我简直要暴毙,要多麻烦有多麻烦,大家如果有改进策略请告诉我,我会虚心接受了。。。写这么长代码累死人啦!!

我讲讲我分析的思路吧:

我们要自己编写结点类,其实结点凑起来就是tree,我们控一下root就可以咯。
手写Node,由于指定类型,所以不加泛型,不加setter、getter,玩最简单的版本:

    static class Node {char data;Node left, right;Node(char data) {this.data = data;this.left = null;this.right = null;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Node node = (Node) o;return data == node.data;}@Overridepublic int hashCode() {return Objects.hash(data);}}

对咯,这个必须是(静态)内部类,因为luoguOJ不识别,你写成另一个class会判CE的。。。。。。静态的内部类是OK的,因为静态不能访问非静态的,这会给方法里的调用带来麻烦。
还有就是一定要重写hashCode()和equals(),我们可是需要用data来区别Node的呀!!

首先我们建立一棵树,需要建立临时根结点,由于我们想知道这个结点是否已经在树里面,我们不想再次遍历,那我们可以建立一个HashMap,存储已经加入树中的结点
我们还需要一个HashMap,用来为未插入树中的结点提供查找的便利

将初始化的根结点保存为根结点,左右儿子加上去。不过这个题无论何时都要注意 ‘*’ 的存在,必须不能插入data为 ‘*’ 的结点。

接下来开循环,每次读一行数据,然后切成三份(toCharArray()),用三个char存储。
循环里面先看看这个临时的根结点是不是在map里,如果有就说明它在树里,它不可能还有左右儿子,所以肯定是原先作为某个子结点的,进行一下替换。
如果不在map里,就去unAddedMap里找,这里有的话思路同上,只不过这是一群没加入树里的临时树,相当于与真正的树构成了一个森林而已……
如果都不在,那好,这就是没有出现过的结点,我们看看它是不是新的根结点,这个判断要用临时根结点的左右儿子分别与当前真实根节点比较,如果equals就换根,注意换根以后要查右儿子在不在unAddedMap里(肯定不在map里)。。如果不是换根的情况那就只能把它塞进unAddedMap里了,毕竟这在当前是一棵 “孤儿树”,无依无靠呜呜呜~~

大致是这样,具体的看我代码注释吧,能看完说明你是个狠人,哦不,狼人(比狠人还狠一、)。。。

哦哦哦,差点忘说了,这个算法有一部分的逻辑是这样的:
毕竟我们这个森林是需要合并的,当合并的时候呢,就要把unAddedMap里的那个结点换掉临时结点(未来的老祖宗——新根)的某个儿子(我WA的那次就是因为这个原因,太难了),然后在unAddedMap里递归删除,在map里递归插入。。。。

这需要两个static的辅助方法(private即可),很nice!!!

递归删除

    private static void deleteRecursively(Node root, Map<Character, Node> unAddedMap) {if (root == null) {return;}unAddedMap.remove(root.data);deleteRecursively(root.left, unAddedMap);deleteRecursively(root.right, unAddedMap);}

递归插入

    private static void addRecursively(Node root, Map<Character, Node> map) {if (root == null) {return;}map.put(root.data, root);addRecursively(root.left, map);addRecursively(root.right, map);}

最后的结果也需要递归先序遍历

    private static void preOrder(Node root) {if (root == null) {return;}System.out.print(root.data);preOrder(root.left);preOrder(root.right);}

第一次提交——CE

这次CE就是因为没把Node类作为内部类,不能被OJ识别所以判错。。。。

第二次提交——WA

这个WA就是因为没换,具体原因我忘了诶,代码奉上,可以自己发掘其“魅力”(毫无魅力的破代码)
这是错的哈,正解在下面。。。

import java.util.*;public class Main {static class Node {char data;Node left, right;Node(char data) {this.data = data;this.left = null;this.right = null;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Node node = (Node) o;return data == node.data;}@Overridepublic int hashCode() {return Objects.hash(data);}}public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int num = Integer.parseInt(scanner.nextLine());if (num == 0) {return;}//树非空,进行一系列初始化Map<Character, Node> map = new HashMap<>(num);Map<Character, Node> unAddedMap = new HashMap<>(num);char[] firstChars = scanner.nextLine().toCharArray();//初始化根结点Node root = new Node(firstChars[0]);map.put(firstChars[0], root);if (firstChars[1] != '*') {root.left = new Node(firstChars[1]);map.put(firstChars[1], root.left);}if (firstChars[2] != '*') {root.right = new Node(firstChars[2]);map.put(firstChars[2], root.right);}//循环num-1次for (int i = 1; i < num; i++) {char[] chars = scanner.nextLine().toCharArray();//根、左儿子、右儿子的charchar tempRootChar = chars[0], tempLeftChar = chars[1], tempRightChar = chars[2];//临时的根结点、左儿子结点、右儿子结点Node tempRoot, tempLeft, tempRight;//Map里已有,也就是说已经存在临时父结点,而且子结点也有咯if (map.containsKey(tempRootChar)) {tempRoot = map.get(tempRootChar);//子结点必定不存在于Map,但要看看unAddedif (unAddedMap.containsKey(tempLeftChar)) {tempLeft = unAddedMap.get(tempLeftChar);root.left = tempLeft;addRecursively(tempLeft, map);deleteRecursively(tempLeft, unAddedMap);} else if (tempLeftChar == '*') {tempLeft = null;} else {tempLeft = new Node(tempLeftChar);}if (unAddedMap.containsKey(tempRightChar)) {tempRight = unAddedMap.get(tempRightChar);root.right = tempRight;addRecursively(tempRight, map);deleteRecursively(tempRight, unAddedMap);} else if (tempRightChar == '*') {tempRight = null;} else {tempRight = new Node(tempRightChar);}//插入左右儿子节点tempRoot.left = tempLeft;tempRoot.right = tempRight;} else if (unAddedMap.containsKey(tempRootChar)) {  //这就是说它的父结点已经存在咯,然后还没插进树里//继续放在unAdded里tempRoot = unAddedMap.get(tempRootChar);//子结点必定不存在//插入左右儿子节点if (tempLeftChar != '*') {tempLeft = new Node(tempLeftChar);tempRoot.left = tempLeft;unAddedMap.put(tempLeftChar, tempLeft);}if (tempRightChar != '*') {tempRight = new Node(tempRightChar);tempRoot.right = tempRight;unAddedMap.put(tempRightChar, tempRight);}} else {        //两边都没有,看看是不是子结点就是现在的根tempRoot = new Node(tempRootChar);if (tempLeftChar == '*') {tempLeft = null;} else {tempLeft = new Node(tempLeftChar);tempRoot.left = tempLeft;}if (tempRightChar == '*') {tempRight = null;} else {tempRight = new Node(tempRightChar);tempRoot.right = tempRight;}if (root.equals(tempLeft)) {        //左儿子是根结点,换根tempLeft = root;tempRoot.left = tempLeft;root = tempRoot;//查找右儿子if (unAddedMap.containsKey(tempRightChar)) {tempRight = unAddedMap.get(tempRightChar);root.right = tempRight;addRecursively(tempRight, map);deleteRecursively(tempRight, unAddedMap);}} else if (root.equals(tempRight)) {        //右儿子是根结点,换根tempRight = root;tempRoot.right = tempRight;root = tempRoot;//查找左儿子if (unAddedMap.containsKey(tempLeftChar)) {tempLeft = unAddedMap.get(tempLeftChar);root.left = tempLeft;addRecursively(tempLeft, map);deleteRecursively(tempLeft, unAddedMap);}} else {                        //插入unAdded里if (unAddedMap.containsKey(tempLeftChar)) {tempRoot.left = unAddedMap.get(tempLeftChar);} else if (tempLeftChar != '*') {unAddedMap.put(tempLeftChar, tempLeft);}if (unAddedMap.containsKey(tempRightChar)) {tempRoot.right = unAddedMap.get(tempRightChar);} else if (tempRightChar != '*') {unAddedMap.put(tempRightChar, tempRight);}unAddedMap.put(tempRootChar, tempRoot);}}}scanner.close();preOrder(root);}private static void preOrder(Node root) {if (root == null) {return;}System.out.print(root.data);preOrder(root.left);preOrder(root.right);}private static void deleteRecursively(Node root, Map<Character, Node> unAddedMap) {if (root == null) {return;}unAddedMap.remove(root.data);deleteRecursively(root.left, unAddedMap);deleteRecursively(root.right, unAddedMap);}private static void addRecursively(Node root, Map<Character, Node> map) {if (root == null) {return;}map.put(root.data, root);addRecursively(root.left, map);addRecursively(root.right, map);}}

哦~~我竟截图为证。。。
好吧,改的是这里,就AC了:

AC代码(Java语言描述)

import java.util.*;public class Main {static class Node {char data;Node left, right;Node(char data) {this.data = data;this.left = null;this.right = null;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Node node = (Node) o;return data == node.data;}@Overridepublic int hashCode() {return Objects.hash(data);}}public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int num = Integer.parseInt(scanner.nextLine());if (num == 0) {return;}//树非空,进行一系列初始化Map<Character, Node> map = new HashMap<>(num);Map<Character, Node> unAddedMap = new HashMap<>(num);char[] firstChars = scanner.nextLine().toCharArray();//初始化根结点Node root = new Node(firstChars[0]);map.put(firstChars[0], root);if (firstChars[1] != '*') {root.left = new Node(firstChars[1]);map.put(firstChars[1], root.left);}if (firstChars[2] != '*') {root.right = new Node(firstChars[2]);map.put(firstChars[2], root.right);}//循环num-1次for (int i = 1; i < num; i++) {char[] chars = scanner.nextLine().toCharArray();//根、左儿子、右儿子的charchar tempRootChar = chars[0], tempLeftChar = chars[1], tempRightChar = chars[2];//临时的根结点、左儿子结点、右儿子结点Node tempRoot, tempLeft, tempRight;//Map里已有,也就是说已经存在临时父结点,而且子结点肯定没有if (map.containsKey(tempRootChar)) {tempRoot = map.get(tempRootChar);//子结点必定不存在于Map,但要看看unAddedif (unAddedMap.containsKey(tempLeftChar)) {tempLeft = unAddedMap.get(tempLeftChar);root.left = tempLeft;addRecursively(tempLeft, map);deleteRecursively(tempLeft, unAddedMap);} else if (tempLeftChar == '*') {tempLeft = null;} else {tempLeft = new Node(tempLeftChar);map.put(tempLeftChar, tempLeft);}if (unAddedMap.containsKey(tempRightChar)) {tempRight = unAddedMap.get(tempRightChar);root.right = tempRight;addRecursively(tempRight, map);deleteRecursively(tempRight, unAddedMap);} else if (tempRightChar == '*') {tempRight = null;} else {tempRight = new Node(tempRightChar);map.put(tempRightChar, tempRight);}//插入左右儿子节点tempRoot.left = tempLeft;tempRoot.right = tempRight;} else if (unAddedMap.containsKey(tempRootChar)) {  //这就是说它的父结点已经存在咯,然后还没插进树里//继续放在unAdded里tempRoot = unAddedMap.get(tempRootChar);//子结点必定不存在//插入左右儿子节点if (tempLeftChar != '*') {tempLeft = new Node(tempLeftChar);tempRoot.left = tempLeft;unAddedMap.put(tempLeftChar, tempLeft);}if (tempRightChar != '*') {tempRight = new Node(tempRightChar);tempRoot.right = tempRight;unAddedMap.put(tempRightChar, tempRight);}} else {        //两边都没有,看看是不是子结点就是现在的根tempRoot = new Node(tempRootChar);if (tempLeftChar == '*') {tempLeft = null;} else {tempLeft = new Node(tempLeftChar);tempRoot.left = tempLeft;}if (tempRightChar == '*') {tempRight = null;} else {tempRight = new Node(tempRightChar);tempRoot.right = tempRight;}if (root.equals(tempLeft)) {        //左儿子是根结点,换根tempLeft = root;tempRoot.left = tempLeft;root = tempRoot;//查找右儿子if (unAddedMap.containsKey(tempRightChar)) {tempRight = unAddedMap.get(tempRightChar);root.right = tempRight;addRecursively(tempRight, map);deleteRecursively(tempRight, unAddedMap);}} else if (root.equals(tempRight)) {        //右儿子是根结点,换根tempRight = root;tempRoot.right = tempRight;root = tempRoot;//查找左儿子if (unAddedMap.containsKey(tempLeftChar)) {tempLeft = unAddedMap.get(tempLeftChar);root.left = tempLeft;addRecursively(tempLeft, map);deleteRecursively(tempLeft, unAddedMap);}} else {                        //插入unAdded里if (unAddedMap.containsKey(tempLeftChar)) {tempRoot.left = unAddedMap.get(tempLeftChar);} else if (tempLeftChar != '*') {unAddedMap.put(tempLeftChar, tempLeft);}if (unAddedMap.containsKey(tempRightChar)) {tempRoot.right = unAddedMap.get(tempRightChar);} else if (tempRightChar != '*') {unAddedMap.put(tempRightChar, tempRight);}unAddedMap.put(tempRootChar, tempRoot);}}}scanner.close();preOrder(root);}private static void preOrder(Node root) {if (root == null) {return;}System.out.print(root.data);preOrder(root.left);preOrder(root.right);}private static void deleteRecursively(Node root, Map<Character, Node> unAddedMap) {if (root == null) {return;}unAddedMap.remove(root.data);deleteRecursively(root.left, unAddedMap);deleteRecursively(root.right, unAddedMap);}private static void addRecursively(Node root, Map<Character, Node> map) {if (root == null) {return;}map.put(root.data, root);addRecursively(root.left, map);addRecursively(root.right, map);}}

感想

这代码真简(la)洁(ji)。。。
我迷了,不过自己手写一次满有收获的诶,奥利给!!!!
容我休息一下~~~
写代码好快(tou)乐(tu)。。。。
您能看完,是赏脸的,菜鸡拜谢Orz

传说七只Orz的自己可以召唤神龙哇!!!

用任意合法序列建立一棵二叉树(洛谷P1305题题解,Java语言描述)相关推荐

  1. 计算当前序列的字典序序号(洛谷P2524题题解,Java语言描述)

    题目要求 P2524题目链接 分析 这题就用康托展开吧: result = a[n] * [(n-1)!] + a[n-1] * [(n-2)!] + - + a[1] * [0!] 实现代码: pr ...

  2. 根据特殊EOF的序列判断比赛输赢(洛谷P1042题题解,Java语言描述)

    题目要求 P1042题目链接 分析 本题的EOF显然是'E',其后就算有任何内容也都与我们需要的数据无关啦,这是一定要记住的. 不能直接将scanner.nextLine()的数据用于算法,因为要处理 ...

  3. 通过“FBI树”复习二叉树算法(洛谷P1087题题解,Java语言描述)

    题目要求 P1087题目链接 分析 所谓的"FBI树",其实就是一种二叉树,最后的结果也无非就是二叉树的后序遍历序列. 所以,考察的知识点就是--二叉树基本算法的灵活运用. 本题关 ...

  4. 二叉树——新二叉树(洛谷 P1305)

    题目选自洛谷P1305 这篇用来二叉树入门,讲述遍历二叉树的基本过程代码, 新二叉树只是洛谷的题目叫这个. 对于该题,我们用一个结构体node来表示树的每个结点,这个结构体数组tree来代表一棵树. ...

  5. 生成指定序列的前一字典序序列(洛谷P2525题题解,C++语言描述)

    题目要求 P2525题目链接 分析 C++的STL中的prev_permutation函数可以生成前一个排列. 如果可以生成,则返回true,并可以直接用生成的序列:已经为第一个,则返回false. ...

  6. java中先序创建一棵树,恳求大佬指点!!!首先标明空子树的先根遍历序列建立一棵二叉树...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 回复 甜味_猫 : public class BiTree { private BiTreeNode root; public BiTree(String ...

  7. java中根遍历后根遍历构造,恳求大佬指点!!!首先标明空子树的先根遍历序列建立一棵二叉树...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 回复 甜味_猫 : public class BiTree { private BiTreeNode root; public BiTree(String ...

  8. 信息学奥赛一本通 1981:【18NOIP普及组】对称二叉树 | 洛谷 P5018【NOIP2018 普及组】 对称二叉树

    [题目链接] ybt 1981:[18NOIP普及组]对称二叉树 洛谷 P5018[NOIP2018 普及组] 对称二叉树 [题目考点] 二叉树 [解题思路] 先求出二叉树中各子树的结点数 遍历二叉树 ...

  9. 哪两种遍历方式可以唯一确定一棵二叉树,结合力扣105题

    对于一棵树的前中序三种顺序的遍历方式,任何一种单独拿出来都无法确定一棵树,那么两种遍历方式得到的节点数据能否构建一棵二叉树呢? 先来看看能有哪几种组合: 先序遍历 + 中序遍历 后序遍历 + 中序遍历 ...

最新文章

  1. 会议:第七届全国生物多样性信息学研讨会(9月25-27日)
  2. PHP中调用SVN命令更新网站方法(解决文件名包含中文更新失败的问题)
  3. 关于Lucene的自定义Sort排序
  4. 基于朴素贝叶斯的垃圾邮件分类-着重理解拉普拉斯变换
  5. C语言中sizeof详解——面试C/C++
  6. 语言编写正反星星_厉害!浙理工师生原创短片《星星》入围5个国际电影节
  7. 【今日CV 视觉论文速览】Fri, 15 Feb 2019
  8. Java 实现图片合成
  9. python钉钉机器人发送消息_python调用钉钉机器人发送消息
  10. jdbc显示mysql的数据_JDBC链接mysql插入数据后显示问号的原因及解决办法
  11. ​技术沙龙 | 移动云Teatalk(西安站)带你走进云网融合
  12. phper的何去何从
  13. 国家计算机考试报名照片编辑器,电脑的证件照制作软件推荐
  14. 002_Python基础学习网站
  15. 电脑鼠标右键失效linux,鼠标右键失灵,教你win7电脑鼠标右键失灵的应对办法
  16. WIN10 mscomm32注册,亲测可用
  17. D. Harmonious Graph【并查集】
  18. 【Matlab图像去噪】小波滤波(硬阙值+软阙值)+中值滤波图像去噪【含源码 462期】
  19. 边界元与有限元方法相比较的优缺点
  20. mongodb海量数据CRUD优化

热门文章

  1. 2.apache模块mod_rpaf ,让nginx代理后端的apache获取访客真是IP
  2. 平衡的括号[UVA-673]
  3. 百度终于升级空间的编辑器了
  4. python相比于excel的优势_对照Excel使用Python进行数据分析,更快掌握
  5. 谷歌浏览器不能上网_谷歌浏览器插件下载及安装教程!
  6. a的n次方的最后三位数c语言,求13的n次方(12n≤130000000000)的最后三位数,用c++编程...
  7. mysql any 效率_关于mysql的性能优化
  8. c语言实验指导,C语言实验指导
  9. 乐飞天下python笔试题_滴滴2020年春招笔试题分析(Python)
  10. 大一计算机导论期中考试,大一计算机导论试题