一个网站域名,如"discuss.leetcode.com",包含了多个子域名。作为顶级域名,常用的有"com",下一级则有"leetcode.com",最低的一级为"discuss.leetcode.com"。当我们访问域名"discuss.leetcode.com"时,也同时访问了其父域名"leetcode.com"以及顶级域名 “com”。
给定一个带访问次数和域名的组合,要求分别计算每个域名被访问的次数。其格式为访问次数+空格+地址,例如:“9001 discuss.leetcode.com”。
[“9001 discuss.leetcode.com”]
[“9001 discuss.leetcode.com”, “9001 leetcode.com”, “9001 com”]
例子中仅包含一个网站域名:“discuss.leetcode.com”。按照前文假设,子域名"leetcode.com"和"com"都会被访问,所以它们都被访问了9001次。
这道题目相对来说还是比较简单的,看到这种String
、这种格式,平头哥首先想到的是String
的substring()
、split()
两个方法将string
转成数组或者新的字符串,事实证明平头哥的想法还阔以,居然居于这两个方法实现了子域名访问统计,真是瞎猫碰上死耗子。但是平头哥是一名具有专研精神的非专业搬砖员,经过平头哥不严格的测试得出,subString()
方法比split()
方法效率高出一大截。这种不知道有多少层的东西用递归或者循环总归是不会错的,为什么这么说呢?因为以平头哥的智商也想不出其他方式啦。平头哥基于递归或者循环实现了三种解决方式,那平头哥就来给小伙伴们介绍介绍这三种方式。
/**
* for +while 方法
* @param cpdomains
* @return
*/
public static List<String> whileSolution(String[] cpdomains){
// 用来存返回结果
List<String> list = new ArrayList<String>();
// 用来计数统计
Map<String, Integer> map = new HashMap<>();
for (String cpdomain : cpdomains) {
// // 先截取访问数和域名
// String[] temp = cpdomain.split(" ");
// // 访问数
// int viewCount = Integer.valueOf(temp[0]);
// // 域名
// String domain = temp[1];
int indexOf = cpdomain.indexOf(" ");
// 访问数
int viewCount = Integer.valueOf(cpdomain.substring(0, indexOf));
// 域名
String domain = cpdomain.substring(indexOf + 1);
while (domain.indexOf(".") !=-1){
mapPutOrAdd(map,domain,viewCount);
domain = domain.substring(domain.indexOf(".")+1);
}
// 最后一个顶级域名
mapPutOrAdd(map,domain,viewCount);
}
for (String key :map.keySet())
list.add(map.get(key)+" "+key);
return list;
}
利用 while
循环来判断域名中是否包含.
,如果包含.
,则说明只要有一个子域名,因为indexOf()
方法是从左往右查找的,只要找到就返回.
所在字符串的位置,没有找到就返回-1
。如果找到了,就利用substring()
方法从返回位置的后一位开始截取到字符串的最后一位,作为新的字符串,继续走上面的流程,知道循环结束。
/**
* 三层for 循环
* @param cpdomains
* @return
*/
public static List<String> forSolution(String[] cpdomains) {
List<String> list = new ArrayList<String>();
Map<String, Integer> map = new HashMap<>();
// 遍历数组
for (String cpdomain : cpdomains) {
// // 先截取访问数和域名
// String[] temp = cpdomain.split(" ");
// // 访问数
// int viewCount = Integer.valueOf(temp[0]);
// // 域名
// String domain = temp[1];
int indexOf = cpdomain.indexOf(" ");
// 访问数
int viewCount = Integer.valueOf(cpdomain.substring(0, indexOf));
// 域名
String domain = cpdomain.substring(indexOf + 1);
String[] domains = domain.split("\\.");
if (domains.length == 0) continue;
// 拼接出多级域名,都塞入map
for (int i = 0; i < domains.length; i++) {
StringBuilder key = new StringBuilder(domains[i]);
for (int j = i + 1; j < domains.length; j++) {
key.append('.');
key.append(domains[j]);
}
mapPutOrAdd(map,key.toString(), viewCount);
}
}
for (String key :map.keySet())
list.add(map.get(key)+" "+key);
return list;
}
for
循环的实现是平头哥带来的三种实现中,效率最低的。所以小伙伴们能不用双层for
就坚决不用。for
循环最要利用split()
方法将域名分离出现,例如discuss.leetcode.com
分成discuss
、leetcode
、com
。然后双层遍历将域名组合起来,最外层第一次遍历完之后得到discuss.leetcode.com
,第二次遍历完之后得到leetcode.com
,第三次遍历完之后得到com
。这样最后也得到了每个域名的访问次数。
public static List<String> recurveSolution(String[] cpdomains){
List<String> list = new ArrayList<String>();
Map<String, Integer> map = new HashMap<>();
// 遍历数组
for (String cpdomain : cpdomains) {
// 先截取访问数和域名
String[] temp = cpdomain.split(" ");
// 访问数
int viewCount = Integer.valueOf(temp[0]);
// 域名
String domain = temp[1];
// 递归方法
recurve(map,domain,viewCount);
}
for (String key :map.keySet())
list.add(map.get(key)+" "+key);
return list;
}
/**
* 递归方法
* @param map map集合
* @param domain 网站域名
* @param viewCount 访问数
* @return
*/
public static Map<String,Integer> recurve(Map<String,Integer> map,String domain,Integer viewCount){
if (domain.indexOf(".") != -1){
mapPutOrAdd(map,domain,viewCount);
return recurve(map, domain.substring(domain.indexOf(".")+1),viewCount);
}else {
mapPutOrAdd(map,domain,viewCount);
return map;
}
}
private static void mapPutOrAdd(Map<String,Integer> map, String key, Integer val) {
if (map.containsKey(key)) {
map.put(key,map.get(key) + val);
} else {
map.put(key,val);
}
}
递归方法跟while
方式差不多,效率也不相上下。因为我们不知道域名有多少层,用递归来做是非常合适的,因为递归就是为这个而生。每次递归的时候我们现将域名添加到map
中,跟while
一样我们也用indexOf(".")
来判断域名中是否含有子域名,如果存在,则截取新的域名继续调用自身,直到没有子域名为止。
以上就是平头哥给小伙伴们带来关于子域名访问计数的算法分享,不知道是否给小伙伴们讲明白?对于这道算法题,小伙伴们你是否还有其他思路呢?请在留言区与其他的小伙伴分享吧