样例输出
JiaDaishan
JiaZheng
-1
算法思路
本文提出的算法思路很简单,主要在于如何构建这个关系(家族)树,要考虑关系中是可以存在多棵关系树,因此本文提出了用List存放多棵关系树,接下来则是如何构建关系树,而且要便于搜索两个人的最近公共祖先。
关系树结构如下:
class Relation{
public int childNo=0;//孩子数
public String name;//名字
public Relation[] child = new Relation[100];//孩子节点
public Set<String> hasAllnode = new HashSet<>();//包括自己和自己分支下的所有节点
public Relation(){}
public Relation(String name) {
this.name = name;
}
}
首先,包括该节点的名字name,拥有孩子的数量(也就是说这是一棵多叉树)childNo,孩子节点数组child,最重要的是存储包括自己节点以及自己孩子,以及孩子的孩子的节点,也就是当前节点及其子树的所有节点的集合hasAllnode,这样在搜索两人是否具有公共祖先时,只要一棵一棵的搜索家族树的头节点的hasAllnode集合中是否同时包含两个人,如果只含一个,那要搜索的两个人肯定不是一个家族的,则直接输出-1,如果同属于某一家族树,则在该棵关系(家族)树种搜索,如果所有关系树中都不存在这两个人,毫无疑问也是输出-1。
然后针对在一棵关系树中存在,则遍历该树,此时会发生以下三种情况:
1、树中遍历的节点与要搜索的两个人中一个相同,则这个就是他们的最近公共祖先,返回输出
2、树中遍历的节点hasAllnode集合包含搜索的两人,但是搜索的两人分属于该节点的不同孩子数中,则当前节点即他们的最近公共祖先
3、搜索的两人同属于当前节点的某一孩子的hasAllnode集合,则继续在该孩子书进行搜索,直到出现情况1、2中的一种。
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
//关系结构
class Relation{
public int childNo=0;//孩子数
public String name;//名字
public Relation[] child = new Relation[100];//孩子节点
public Set<String> hasAllnode = new HashSet<>();//包括自己和自己分支下的所有节点
public Relation(){}
public Relation(String name) {
this.name = name;
}
}
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
List<String[]> nodes = new ArrayList<String[]>();
int N = sc.nextInt();
while(N-->0){
String names[] = new String[2];
names[0] = sc.next();
names[1] = sc.next();
int index=0;
//将具有关系的节点放在一块,即让属于同一家族树的节点放在一块
for(;index<nodes.size();index++){
String node[] = nodes.get(index);
if(node[0].equals(names[0])||node[0].equals(names[1])||node[1].equals(names[0])||node[1].equals(names[1])){
nodes.add(index+1, names);
break;
}
}
if(index>=nodes.size()){
nodes.add(names);
}
}
//存储家族树,最终会构建出多棵家族树
List<Relation> relations= new ArrayList<>();
for(String[] names:nodes){
//对于第一棵家族树
if(relations.size()<=0){
Relation root = new Relation(names[0]);
root.hasAllnode.add(names[0]);
root.hasAllnode.add(names[1]);
root.child[root.childNo] = new Relation(names[1]); //子节点name
root.child[root.childNo].hasAllnode.add(names[1]);
root.childNo++;//子节点数
relations.add(root);
}else{
boolean flag=false;
//在构建的家族树中寻找属于自己的家族树
for(int index=0;index<relations.size();index++){
Relation root=relations.get(index);
if(root.hasAllnode.contains(names[0])||root.hasAllnode.contains(names[1])){
relations.set(index, CreateRelationTree(root,names[0],names[1],root));
flag=true;
}
}
//如果没有找到属于自己家族树,则说明是一棵新的家族树
if(!flag){
Relation root = new Relation(names[0]);
root.hasAllnode.add(names[0]);
root.hasAllnode.add(names[1]);
root.child[root.childNo] = new Relation(names[1]); //子节点name
root.child[root.childNo].hasAllnode.add(names[1]);
root.childNo++;//子节点数
relations.add(root);
}
}
}
//打印按前序遍历打印家族树
for(int index=0;index<relations.size();index++){
Relation root=relations.get(index);
System.out.println("这是"+(index+1)+"棵关系树:");
printRelation(root);
}
int M = sc.nextInt();//测试组数
System.out.println("寻找的最近公共祖先结果如下:");
while(M-->0){
String names[] = new String[2];
names[0] = sc.next();
names[1] = sc.next();
//如果输入名字相同,则直接输出
if(names[0].equals(names[1])){
System.out.println(names[0]);
continue;
}
boolean flag = false;
for(Relation root:relations){//在家族树中寻找两人属于同一家族树,然后深入搜索最近祖先,如果不属于同一家族树,则无共同祖先
if(root.hasAllnode.contains(names[0])&&root.hasAllnode.contains(names[1])){
flag=true;
System.out.println(getRecentlyAncestor(root,names[0],names[1]));
}
}
if(!flag){
System.out.println(-1);
}
}
}
//创建家族树
private static Relation CreateRelationTree(Relation root, String parent, String child,Relation rootnode) {
if(root!=null&&root.name!=null&&!root.name.equals("")){
//如果加入的节点是头节点,则应该插在当前节点前面,形成新的家族树头结点返回
if(root.name.equals(child)){
Relation newroot = new Relation(parent);
newroot.hasAllnode.addAll(root.hasAllnode);
newroot.child[newroot.childNo] = root;
newroot.childNo++;
root = newroot;
root.hasAllnode.add(parent);
return root;
}else{
root.hasAllnode.add(child);
}
if(root.name.equals(parent)){//找到父亲节点,将孩子节点插入家族树中
root.child[root.childNo] = new Relation(child); //子节点name
root.child[root.childNo].hasAllnode.add(child);
root.childNo++;
return rootnode;
}else{//没有找到父亲节点,继续深入寻找
for(int index=0;index<root.childNo;index++){
if(root.child[index].hasAllnode.contains(parent)||root.child[index].hasAllnode.contains(child)){
return CreateRelationTree(root.child[index], parent,child,rootnode);
}
}
}
}
return rootnode;
}
//按前序遍历打印家族树
public static void printRelation(Relation root){
if(root==null||root.name==null||root.name.equals("")){
return;
}
System.out.println("当前节点值:"+root.name+",所有孩子节点:"+root.hasAllnode);
for(int index=0;index<root.childNo;index++){
printRelation(root.child[index]);
}
}
//寻找最近祖先
public static String getRecentlyAncestor(Relation root,String name1,String name2){
//最近祖先是其中需要找的人中的一个
if(root.name.equals(name1)||root.name.equals(name2)){
if(root.name.equals(name1)){
return name1;
}else{
return name2;
}
}
for(int index=0;index<root.childNo;index++){
//要找的人在该节点开始分叉,则当前节点即为最近祖先
if((root.child[index].hasAllnode.contains(name1)&&!root.child[index].hasAllnode.contains(name2))||
(!root.child[index].hasAllnode.contains(name1)&&root.child[index].hasAllnode.contains(name2))){
return root.name;
}else if(root.child[index].hasAllnode.contains(name1)&&root.child[index].hasAllnode.contains(name2)){
return getRecentlyAncestor(root.child[index],name1,name2);
}
}
return "-1";
}
}
结果输出
11
JiaYan JiaDaihua
JiaDaihua JiaFu
JiaDaihua JiaJing
JiaJing JiaZhen
JiaZhen JiaRong
JiaYuan JiaDaishan
JiaDaishan JiaShe
JiaDaishan JiaZheng
JiaShe JiaLian
JiaZheng JiaZhu
JiaZheng JiaBaoyu
3
JiaBaoyu JiaLian
JiaBaoyu JiaZheng
JiaBaoyu LinDaiyu
这是1棵关系树:
当前节点值:JiaYan,所有孩子节点:[JiaJing, JiaRong, JiaDaihua, JiaFu, JiaZhen, JiaYan]
当前节点值:JiaDaihua,所有孩子节点:[JiaJing, JiaRong, JiaDaihua, JiaFu, JiaZhen]
当前节点值:JiaJing,所有孩子节点:[JiaJing, JiaRong, JiaZhen]
当前节点值:JiaZhen,所有孩子节点:[JiaRong, JiaZhen]
当前节点值:JiaRong,所有孩子节点:[JiaRong]
当前节点值:JiaFu,所有孩子节点:[JiaFu]
这是2棵关系树:
当前节点值:JiaYuan,所有孩子节点:[JiaLian, JiaShe, JiaYuan, JiaZhu, JiaDaishan, JiaZheng, JiaBaoyu]
当前节点值:JiaDaishan,所有孩子节点:[JiaLian, JiaShe, JiaZhu, JiaDaishan, JiaZheng, JiaBaoyu]
当前节点值:JiaZheng,所有孩子节点:[JiaZhu, JiaZheng, JiaBaoyu]
当前节点值:JiaBaoyu,所有孩子节点:[JiaBaoyu]
当前节点值:JiaZhu,所有孩子节点:[JiaZhu]
当前节点值:JiaShe,所有孩子节点:[JiaLian, JiaShe]
当前节点值:JiaLian,所有孩子节点:[JiaLian]
寻找的最近公共祖先结果如下:
JiaDaishan
JiaZheng
-1