腾讯笔试2:通知的互相传递

输入:

N M

3 5 6 1

0 1

2 4 7

3 8 9

输入的N表示一共有几个人(话说这个参数我好想没用到),M表示一共有几个小团体,下面的M行就表示每个小团体里面的人的编号,比如3 5 6 1就表示第0个小团体里面有编号3  5 6  1的人。每个人可以分属于几个不同的团体或者不属于任何团体

一开始会向编号为0的人传达一个消息,然后0会把这个消息传递到所在组里面的每个人,每个人又会传递给他的组里面的每个人,求最后一共有多少人知道这个消息。


思路1:最朴素的思想,我们建立一个idset(一个hashset)保存所有知道消息的人的编号,然后建立一个rowsSet存放已经知道这个消息的小团体(就是行号,防止后面再遍历搜索这一行),然后建立一个队列,所有会知道这个消息的人都会进入这个队列。首先0知道了那么这一行的所有人都会知道,都放入队列。然后不断把队列里面的元素取出来,然后取出不在rowsSet里面的行的元素,都加入这个队列里面。

之所以要这个队列,是因为我们不确定要遍历到什么时候才算结束,因为如果一开始加入 0 1,然后自然要遍历第0行(因为第0行有1),但是第0行的3又出现在第3行,所以我们又需要去遍历第三行的所有元素,如果第三行里面还有个比如x,它又出现在第五行,我们又要去遍历第5行,因此用一个队列把所有需要查找的元素都放进去,最后遍历结束的标志就是队列为空。


	public static int res=0;
	//start表示0所在的行
	public static  HashSet<Integer> idset=new HashSet<>();//存放结果编号
	public static void process(List<List<Integer>> all,int start){
		int number=all.get(start).size();//这一行的数量
		Queue<Integer> queue=new LinkedList<>();
		Set<Integer> rowsSet=new HashSet<>();//存放已有的行编号
		rowsSet.add(start);
		for(int i=0;i<number;i++){
			queue.add(all.get(start).get(i));
		}
		while(!queue.isEmpty()){
			int bianhao=queue.poll();//我们需要找的这个编号
			idset.add(bianhao);
			for(int j=0;j<all.size();j++){//遍历所有行
				if(j!=start){
					if(all.get(j).contains(bianhao)&&!rowsSet.contains(j)){//这一行如果有我们要的编号,并且之前没有遍历
						rowsSet.add(j);
						for(int m=0;m<all.get(j).size();m++){//遍历这一行的所有数字
							int tmp=all.get(j).get(m);//找出第j行的第m个
							if(!idset.contains(tmp)){
								idset.add(tmp);
								queue.add(tmp);
							}	
						}
					}
				}
			}
			
		}
		res=idset.size();
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		while(sc.hasNext()){
			int n=sc.nextInt();
			int m=sc.nextInt();//小团体的个数
			sc.nextLine();
			List<List<Integer>> all=new ArrayList<>();//小团体都放入all里面
			int start=-1;
			for(int i=0;i<m;i++){
				List<Integer> tmp=new ArrayList<>();
				int count=sc.nextInt();
				for(int j=0;j<count;j++){
					int num=sc.nextInt();
					tmp.add(num);
					if(num==0)
						start=i;//把0所在的行保存
				}
				all.add(tmp);
				sc.nextLine();
			}
			process(all,start);
			System.out.println(res);
		}
	}

上述代码确实略微繁琐,但是却能够100%AC。


思路2:并查集

public static class UnionFindSet{
		public HashMap<Integer,Integer> fathermap;
		public HashMap<Integer,Integer> sizemap;
		public UnionFindSet(int n){//初始化
			fathermap=new HashMap<>();
			sizemap=new HashMap<>();
			for(int i=0;i<n;i++){
				fathermap.put(i,i);
				sizemap.put(i,1);
			}
		}
		public void union(int m,int n){
			int father1=findFather(m);
			int father2=findFather(n);
			if(father1!=father2){
				int size1=sizemap.get(father1);
				int size2=sizemap.get(father2);
				if(size1<size2){
					fathermap.put(father1, father2);
					sizemap.put(father2, size1+size2);
				}else{
					fathermap.put(father2, father1);
					sizemap.put(father1, size1+size2);
				}
			}
		}
		//递归找到父节点,同时修改路过的每个节点的父节点
		public int findFather(int num){
			int father=fathermap.get(num);
			if(father!=num){
				father=findFather(father);
			}
			fathermap.put(num,father);
			return father;	
		}
	}
       public static int  process2(List<List<Integer>> all,UnionFindSet unionset){
		for(List<Integer> list:all){
			int start=list.get(0);
			for(Integer a:list){
				unionset.union(start, a);
			}
		}
		int father=unionset.findFather(0);
		return unionset.sizemap.get(father);
	}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值