有话要说:

前阵子遇到了一个计算可达用户投资额的问题,觉得非常有趣,故把它记录下来。

问题描述:

某产品可被投资,注册后才可以投资,其注册可以被邀请(并不是每个人都是被邀请的)。邀请人可以邀请多个人注册投资,而被邀请人只能有一位邀请人。也就是说邀请人与被邀请人是一对多的关系。

现给定两组数据:邀请与被邀请的关系;所有用户的投资额。

如下:

1,2
1,4
2,11
2,10
4,5
5,6
6,8
6,9
6,7
13,14
13,15
13,17
14,20
20,21
15,18
15,19

以上数据是处理过后的邀请关系,前者邀请了后者。

1,1200.00
2,2300.00
4,1500.00
5,7300.00
6,4100.00
7,1100.00
8,9000.00
9,1000.00
10,1100.00
11,100.00
12,1000.00
13,4500.00
14,1100.00
15,1200.00
17,700.00
18,100.00
19,200.00
20,100.00
21,0.00

以上数据是处理过后的所有用户投资额。

现在根据这两组数据需要计算每位用户的“用户投资金额”,“可达用户投资金额”,“邀请用户阶数”,“成功邀请用户数”,“一阶推荐用户投资金额”。

用户投资金额:即该用户的投资额,以以上数据举个例子来说:2的投资金额为2300

可达用户投资金额:即该用户的投资额加上该用户邀请的所有人的投资额(包括该用户邀请人所邀请的人),假设A邀请了B和C,B邀请了D和E,C、D、E没有邀请别人,那么A的可达用户投资金额就是A、B、C、D、E投资额的总数

邀请用户阶数:即该用户所邀请人的总数(不包括该用户邀请人所邀请的人),假设A邀请了B和C,B邀请了D和E,C、D、E没有邀请别人,那么A的邀请用户阶数为2

成功邀请用户数:在邀请用户阶数的基础上除去投资额为零的用户

一阶推荐用户投资金额:即该用户所邀请人的总投资额(不包括该用户邀请人所邀请的人),假设A邀请了B和C,B邀请了D和E,C、D、E没有邀请别人,那么A的一阶推荐用户投资金额为B、C投资额的总数

大家可以先想想如何处理该数据

思路分析:

首先根据邀请的关系可以分析出邀请人的关系图可以分解为多棵不相关的树(反证法可证)。

邀请关系如下图所示:

因此可以先将数据生成若干棵树,然后针对于树进行遍历。

对于每棵树来说,父节点的可达用户投资金额就是子节点的可达用户投资金额之和再加上父节点的投资额,根据这一特性,可以对每棵树从叶子节点网上遍历。

代码实现:

获取所有用户的投资额:

直接读取文件,并把用户id与用户投资额放在Map集合中

 // 获取每个人的投资额
public static Map<String, Double> getAmount() {
Map<String, Double> amounts = new HashMap<String, Double>(); File file = new File("D:/amount.txt"); try {
// 创建输入流,读取txt
InputStream is = new FileInputStream(file.getAbsolutePath());
InputStreamReader isr = new InputStreamReader(is, "UTF-8"); BufferedReader br = new BufferedReader(isr);
String line = "";
while ((line = br.readLine()) != null) {
String[] amount = line.trim().split(",");
amount[0] = amount[0].trim();
amounts.put(amount[0], Double.parseDouble(amount[1]));
} br.close();
isr.close();
is.close(); } catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return amounts;
}

获取用户的邀请关系:

直接读取文件,并把用户的邀请关系放在List集合中

 // 获取关系数据集合
public static List<String[]> getRelationship() {
List<String[]> list = new ArrayList<String[]>(); File file = new File("D:/relationship.txt"); try {
// 创建输入流,读取txt
InputStream is = new FileInputStream(file.getAbsolutePath());
InputStreamReader isr = new InputStreamReader(is, "UTF-8"); BufferedReader br = new BufferedReader(isr);
String line = "";
while ((line = br.readLine()) != null) {
String[] relation = line.trim().split(",");
relation[0] = relation[0].trim();
relation[1] = relation[1].trim();
list.add(relation);
} br.close();
isr.close();
is.close(); } catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return list;
}

构造Person对象:

 // 用户邀请关系(前者邀请了后者)
List<String[]> relationships = new ArrayList<>();
relationships = getRelationship(); // 每个用户投资额
Map<String, Double> amounts = new HashMap<String, Double>();
amounts = getAmount(); // 构造用户信息
Map<String, Person> persons = new HashMap<String, Person>();
Iterator<String> it = amounts.keySet().iterator();
while (it.hasNext()) {
String key = it.next(); Person temp = new Person();
temp.setId(key);
temp.setAmount(amounts.get(key)); persons.put(key, temp);
}

其中Person类为:

 class Person {

     /**
* 用户id
*/
private String id; /**
* 邀请人列表
*/
private List<Person> invitedPersons = new ArrayList<Person>(); /**
* 投资额
*/
private double amount; /**
* 所有下线投资额总额(包括自身)
*/
private double allAmount; public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public double getAmount() {
return amount;
} public void setAmount(double amount) {
this.amount = amount;
} public List<Person> getInvitedPersons() {
return invitedPersons;
} public void setInvitedPersons(List<Person> invitedPersons) {
this.invitedPersons = invitedPersons;
} public double getAllAmount() {
return allAmount;
} public void setAllAmount(double allAmount) {
this.allAmount = allAmount;
} }

构建邀请关系:

 for (int i = 0; i < relationships.size(); i++) {
String[] relationship = relationships.get(i); // 获取邀请人
Person person1 = persons.get(relationship[0]);
// 获取被邀请人
Person person2 = persons.get(relationship[1]);
// 加上关联关系
person1.getInvitedPersons().add(person2);
}

根据以上的构造,已经针对于每个用户都生成了一棵树。

但事实上这些树有大量的重合,如果逐一遍历的话会导致不必要的浪费。

于是准备找出所有的根树,遍历根数即可。

找到所有的根树:

 // 所有person集合
List<Person> allPerson = new ArrayList<>();
allPerson.addAll(persons.values()); // 找到根节点,去除非根节点
for (int i = 0; i < relationships.size(); i++) {
String[] relationship = relationships.get(i); // 如果是被邀请人,则去除
persons.remove(relationship[1]);
}

根据以上精简,persons集合里只有根节点。

遍历根数,找出所有人的可达用户投资金额

Iterator<Person> it4 = persons.values().iterator();
while (it4.hasNext()) {
Person person = it4.next();
addAllChildren(person);
}

其中addAllChildren为

 // 可达用户投资金额
public static double addAllChildren(Person person) {
double childrenAmount = 0; // 先加自己的
childrenAmount += person.getAmount(); // 再加孩子的
for (int i = 0; i < person.getInvitedPersons().size(); i++) {
Person child = person.getInvitedPersons().get(i); if (child.getInvitedPersons().size() > 0) {
childrenAmount += addAllChildren(child);
} else {
childrenAmount += child.getAmount();
}
} person.setAllAmount(childrenAmount); return childrenAmount;
}

通过此遍历,每个person对象的可达用户投资金额均已被算出,又因为java是值传递的,所以allPerson内的person对象的值都已经被计算出来了。

接下来就可以遍历所有的person对象了。

遍历person集合,算出需要的所有数据:

 try {
String path = "D:/result.txt";
File file = new File(path);
if (!file.exists()) {
file.getParentFile().mkdirs();
}
file.createNewFile(); // write
FileWriter fw = new FileWriter(file, true);
BufferedWriter bw = new BufferedWriter(fw); Iterator<Person> it3 = allPerson.iterator(); while (it3.hasNext()) {
Person person = it3.next(); if (person.getAllAmount() == 0) {
bw.write("可达用户投资总额:" + person.getAmount() + ";");
} else {
bw.write("可达用户投资总额:" + person.getAllAmount() + ";");
} bw.write("邀请用户阶数:" + person.getInvitedPersons().size() + ";"); int count = 0;
double sum = 0;
for (Person temp : person.getInvitedPersons()) {
if (temp.getAmount() != 0) {
sum += temp.getAmount();
count++;
}
} bw.write("成功邀请用户数:" + count + ";");
bw.write("一阶推荐用户投资金额:" + sum + ";\n");
} bw.flush();
bw.close();
fw.close();
} catch (Exception e) {
e.printStackTrace();
}

结束语:

以上便是我这次遇到问题的解决思路以及解决过程,对于树的构造以及java值传递的应用值得学习。

本题的解决思路以及解决过程比较仓促,只用了一下午的时间,可能会有更好的解决办法,如果大家有什么更好的解决办法欢迎留言~

大家如果有什么疑问或者建议可以通过评论或者邮件的方式联系我,欢迎大家的评论~

可达用户投资额的计算(Java)的更多相关文章

  1. 计算Java对象内存大小

    摘要 本文以如何计算Java对象占用内存大小为切入点,在讨论计算Java对象占用堆内存大小的方法的基础上,详细讨论了Java对象头格式并结合JDK源码对对象头中的协议字段做了介绍,涉及内存模型.锁原理 ...

  2. Win7网上邻居提示未授予用户在此计算机上的请求登录类型解决办法

        内容简介 装了Win7之后很多人遇到这样的问题,网上邻居访问Win7的电脑时出现“未授予用户在此计算机上的请求登录类型”问题.打开“控制面板”--“管理工具”--“本地安全策略”--“本地策略 ...

  3. windows 7 共享,未授予用户在此计算机上的请求登录类型

    刚刚重装了windows7,新下载的一个ghost版本,结果却不能共享,每次访问这台机器的共享都提示, 未授予用户在此计算机上的请求登录类型 这个情况好像是存在于win7访问win7,我用一台XP系统 ...

  4. win7基于mahout推荐之用户相似度计算

    http://www.douban.com/note/319219518/?type=like win7基于mahout推荐之用户相似度计算 2013-12-03 09:19:11    事情回到半年 ...

  5. 如何准确计算Java对象的大小

    如何准确计算Java对象的大小 原创文章,转载请注明:博客园aprogramer 原文链接:如何准确计算Java对象的大小      有时,我们需要知道Java对象到底占用多少内存,有人通过连续调用两 ...

  6. 两种计算Java对象大小的方法

    之前想研究一下unsafe类,碰巧在网上看到了这篇文章,觉得写得很好,就转载过来.原文出处是: http://blog.csdn.net/iter_zc/article/details/4182271 ...

  7. Ehcache计算Java对象内存大小

    在EHCache中,可以设置maxBytesLocalHeap.maxBytesLocalOffHeap.maxBytesLocalDisk值,以控制Cache占用的内存.磁盘的大小(注:这里Off ...

  8. 简单复利计算java板

    一.要求: 1.客户说:帮我开发一个复利计算软件. 2如果按照单利计算,本息又是多少呢? 3.假如30年之后要筹措到300万元的养老金,平均的年回报率是3%,那么,现在必须投入的本金是多少呢? 4.利 ...

  9. CCF 201612-2 工资计算 java 解题

    问题描述 小明的公司每个月给小明发工资,而小明拿到的工资为交完个人所得税之后的工资.假设他一个月的税前工资(扣除五险一金后.未扣税前的工资)为S元,则他应交的个人所得税按如下公式计算: 1) 个人所得 ...

随机推荐

  1. 前端笔记之NodeJS(二)路由&REPL&模块系统&npm

    一.路由机制(静态资源文件处理) 1.1 Nodejs没有根目录 MIME类型:http://www.w3school.com.cn/media/media_mimeref.asp 在Apache中, ...

  2. .NETCore 千星项目模块化开发框架 SimplCommerce 详解

    SimplCommerce 是 github 上过千星的.netcore 商城示例项目,本文详解他的模块化框架现实思路,其业务(如产品.订单)不作介绍.因作者文笔水平很差,它又很值得学习和推荐,就算不 ...

  3. javascript入门篇(六、正则表达式)

    JavaScript 正则表达式 正则表达式是由一个字符序列形成的搜索模式.当你在文本中搜索数据时,你可以用搜索模式来描述你要查询的内容. 正则表达式可以是一个简单的字符,或一个更复杂的模式.正则表达 ...

  4. 1.JAVA-Hello World

    1.Java开发介绍 J2SE:Java 2 Platform Standard Edition(2005年之后更名为JAVA SE). 包含构成Java语言核心的类.比如:数据库连接.接口定义.数据 ...

  5. sqlserver笔记----创建用户赋予权限

    1.创建用户: create login username with password='密码' , default_database=数据库; create user username for lo ...

  6. 【死磕 Spring】----- IOC 之 获取验证模型

    原文出自:http://cmsblogs.com 在上篇博客[死磕Spring]----- IOC 之 加载 Bean 中提到,在核心逻辑方法 doLoadBeanDefinitions()中主要是做 ...

  7. Python中星号的本质和使用方式

    翻译:Python 开发者 - 一汀, 英文:Trey Hunner http://blog.jobbole.com/114655/ Python开发者 在 Python 中有很多地方可以看到*和** ...

  8. Java核心技术第五章——1.类、超类、子类(2)

    继上一篇Java核心技术第五章——1.类.超类.子类(1) 6.重载解析 假如调用ClassName.Method(args) 1.编译器列出类ClassName所有名为Method的方法. 2.编译 ...

  9. 没错,老板让我写个 BUG!

    前言 标题没有看错,真的是让我写个 bug! 刚接到这个需求时我内心没有丝毫波澜,甚至还有点激动.这可是我特长啊:终于可以光明正大的写 bug 了

  10. 《代码整洁之道》(Clean Code)- 读书笔记

    一.关于Bob大叔的Clean Code <代码整洁之道>主要讲述了一系列行之有效的整洁代码操作实践.软件质量,不但依赖于架构及项目管理,而且与代码质量紧密相关.这一点,无论是敏捷开发流派 ...