MapReduce--6--求共同好友

MapReduce面试题1--求共同好友


1、数据格式

现有一份数据如下:

A:B,C,D,F,E,O
B:A,C,E,K
C:F,A,D,I
D:A,E,F,L
E:B,C,D,M,L
F:A,B,C,D,E,O,M
G:A,C,D,E,F
H:A,C,D,E,O
I:A,O
J:B,O
K:A,C,D
L:D,E,F
M:E,F,G
O:A,H,I,J,K
数据的格式以“:”分割成两部分,前面是用户,后面是该用户的好友,以

A:B,C,D,F,E,O

为例:B,C,D,E,F,O是用户A的好友


2、题目:求所有用户之间的共同好友


最后的结果格式如下:比如用户A和用户B之间有共同好友:C,E

那么显示的结果是:

A-B	C E


3、解题思路分析

根据MapReduce的编程模型的特点,MapReduce编程分为Mapper阶段(任务的拆分)和Reducer阶段(结果的汇总)两个阶段。这两个阶段的业务逻辑,分别编写在Mapper组件的map方法和Reducer组件的reduce方法中。共同点,这两个阶段的业务逻辑方法都是接受key-value对,然后输出key-value对。


所以根据以上结果的格式,可以得知,reducer阶段输出的key-value中的key是A-B,value是C E,

那么value值(C E)怎么得来呢。?根据reduce(key,values,context)方法的特点得知,reduce方法其实接收到的参数是: key相同的一组key-value

也就意味着如果想得到

A-B	C E

这样的数据,必先得到如下这样的数据:

A-B	C
A-B	E
以上这种格式的数据的含义是:  C和E 是 A和B 的共同好友
现在再来解析单条数据, 以第一条为例子:

A-B	C
表示,C是A用户和B用户的共同好友,  也就是说 C是A的好友, C是B的好友, 所以C 才是A和B的共同好友。
那也就表示,如果能得到以下这样的数据:

C	A
C	B
那在MapReduce程序中,就能得到

C	A-B
然后倒过来不就是

A-B	C
么。 完美。
那么现在的问题(代号q1)就变成怎么得到如下的数据了:
C	A
C	B
事实上,这种格式的数据表示: C是A的好友, C是B的好友。
倒过来不就是说: A有好友C,B有好友C么?
现在请看原始数据:
A:B,C,D,F,E,O
这行数据的意思不就是表示A用户有好友B,C,D,F,E,O么?那么拆开就是:

A	B
A	C
A	D
A	F
A	E
A	O
这就能得到,q1问题处想要得到的数据了。


4、具体代码实现

具体的代码实现得分成两个MapReduce来实现:
先看第一个MapReduce程序的实现:

package com.ghgj.mazh.mapreduce.exercise.friend;

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class CommonFriends1MR {

	public static void main(String[] args) throws Exception {

		Configuration conf = new Configuration();

		Job job = Job.getInstance(conf);
		job.setJarByClass(CommonFriends1MR.class);
		job.setMapperClass(CommonFriends1MRMapper.class);
		job.setReducerClass(CommonFriends1MRReducer.class);

		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(Text.class);

		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(Text.class);

		Path inputPath = new Path("D:\\bigdata\\commonfriends\\input");
		Path outputPath = new Path("D:\\bigdata\\commonfriends\\output_111");
		FileSystem fs = FileSystem.get(conf);
		if (fs.exists(outputPath)) {
			fs.delete(outputPath, true);
		}
		FileInputFormat.setInputPaths(job, inputPath);
		FileOutputFormat.setOutputPath(job, outputPath);

		boolean res = job.waitForCompletion(true);

		System.exit(res ? 0 : 1);
	}

	public static class CommonFriends1MRMapper extends Mapper<LongWritable, Text, Text, Text> {

		@Override
		protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
			String line = value.toString();
			String[] tokens = line.split(":");
			String person = tokens[0];
			String[] friends = tokens[1].split(",");

			for (String f : friends) {
				context.write(new Text(f), new Text(person));
			}
		}
	}

	public static class CommonFriends1MRReducer extends Reducer<Text, Text, Text, Text> {
		@Override
		protected void reduce(Text friend, Iterable<Text> persons, Context context)
				throws IOException, InterruptedException {

			StringBuffer sb = new StringBuffer();
			for (Text p : persons) {
				sb.append(p).append("-");
			}
			context.write(friend, new Text(sb.toString()));
		}
	}
}
该MapReduce输出的结果的格式:

A	F-I-O-K-G-D-C-H-B-
B	E-J-F-A-
C	B-E-K-A-H-G-F-
D	H-C-G-F-E-A-K-L-
E	A-B-L-G-M-F-D-H-
F	C-M-L-A-D-G-
G	M-
H	O-
I	O-C-
J	O-
K	O-B-
L	D-E-
M	E-F-
O	A-H-I-J-F-
这种格式的数据表明:key是value中的任意两个用户的共同好友
比如:

A	F-I-O-K-G-D-C-H-B-
表示A是F和I的共同好友,A是F和O的共同好友,A是F和K的共同好友,............



还没有得到最终的结果,所以还需要一个MapReduce程序,来解析第一个MapReduce处理得到的结果数据,以得到最终的结果:
看具体代码实现:

package com.ghgj.mazh.mapreduce.exercise.friend;

import java.io.IOException;
import java.util.Arrays;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class CommonFriends2MR {

	public static void main(String[] args) throws Exception {

		Configuration conf = new Configuration();

		Job job = Job.getInstance(conf);
		job.setJarByClass(CommonFriends2MR.class);

		job.setMapperClass(CommonFriends2MRMapper.class);
		job.setReducerClass(CommonFriends2MRReducer.class);

		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(Text.class);

		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(Text.class);

		Path inputPath = new Path("D:\\bigdata\\commonfriends\\output_111");
		Path outputPath = new Path("D:\\bigdata\\commonfriends\\last_output");
		FileSystem fs = FileSystem.get(conf);
		if (fs.exists(outputPath)) {
			fs.delete(outputPath, true);
		}
		FileInputFormat.setInputPaths(job, inputPath);
		FileOutputFormat.setOutputPath(job, outputPath);

		boolean res = job.waitForCompletion(true);

		System.exit(res ? 0 : 1);
	}

	public static class CommonFriends2MRMapper extends Mapper<LongWritable, Text, Text, Text> {

		/**
		 * A I-K-C-B-G-F-H-O-D- 
		 * B A-F-J-E- 
		 * C A-E-B-H-F-G-K-
		 */
		@Override
		protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
			String line = value.toString();
			String[] tokens = line.split("\t");
			String friend = tokens[0];
			String[] persons = tokens[1].split("-");

			Arrays.sort(persons);
			for (int i = 0; i < persons.length - 1; i++) {
				for (int j = i + 1; j < persons.length; j++) {
					context.write(new Text(persons[i] + "-" + persons[j]), new Text(friend));
				}
			}
		}
	}

	public static class CommonFriends2MRReducer extends Reducer<Text, Text, Text, Text> {
		@Override
		protected void reduce(Text person_pair, Iterable<Text> friends, Context context) throws IOException, InterruptedException {

			StringBuffer sb = new StringBuffer();
			for (Text f : friends) {
				sb.append(f).append(" ");
			}
			context.write(person_pair, new Text(sb.toString()));
		}
	}
}
第二个MapReduce程序执行得到的结果:

A-B	E C 
A-C	D F 
A-D	E F 
A-E	B C D 
A-F	C E O D B 
A-G	E F C D 
A-H	C D E O 
A-I	O 
A-J	O B 
A-K	C D 
A-L	F D E 
A-M	F E 
B-C	A 
B-D	A E 
B-E	C 
B-F	C A E 
B-G	E C A 
B-H	E C A 
B-I	A 
B-K	A C 
B-L	E 
B-M	E 
B-O	A K 
C-D	A F 
C-E	D 
C-F	A D 
C-G	A D F 
C-H	D A 
C-I	A 
C-K	A D 
C-L	D F 
C-M	F 
C-O	I A 
D-E	L 
D-F	A E 
D-G	E A F 
D-H	A E 
D-I	A 
D-K	A 
D-L	E F 
D-M	F E 
D-O	A 
E-F	D M C B 
E-G	C D 
E-H	C D 
E-J	B 
E-K	C D 
E-L	D 
F-G	D C A E 
F-H	A D O E C 
F-I	O A 
F-J	B O 
F-K	D C A 
F-L	E D 
F-M	E 
F-O	A 
G-H	D C E A 
G-I	A 
G-K	D A C 
G-L	D F E 
G-M	E F 
G-O	A 
H-I	O A 
H-J	O 
H-K	A C D 
H-L	D E 
H-M	E 
H-O	A 
I-J	O 
I-K	A 
I-O	A 
K-L	D 
K-O	A 
L-M	E F
以上的数据表示右边的value值就是左边的两个用户的共同好友。


至此,大功告成。ヾ(◍°∇°◍)ノ゙

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值