一题算法 | 子域名访问计数

题目

一个网站域名,如"discuss.leetcode.com",包含了多个子域名。做为顶级域名,经常使用的有"com",下一级则有"leetcode.com",最低的一级为"discuss.leetcode.com"。当咱们访问域名"discuss.leetcode.com"时,也同时访问了其父域名"leetcode.com"以及顶级域名 "com"。 给定一个带访问次数和域名的组合,要求分别计算每一个域名被访问的次数。其格式为访问次数+空格+地址,例如:"9001 discuss.leetcode.com"。java

示例

输入

["9001 discuss.leetcode.com"]算法

输出:

["9001 discuss.leetcode.com", "9001 leetcode.com", "9001 com"]数组

说明:

例子中仅包含一个网站域名:"discuss.leetcode.com"。按照前文假设,子域名"leetcode.com"和"com"都会被访问,因此它们都被访问了9001次。app

这道题目相对来讲仍是比较简单的,看到这种String、这种格式,平头哥首先想到的是Stringsubstring()split()两个方法将string转成数组或者新的字符串,事实证实平头哥的想法还阔以,竟然居于这两个方法实现了子域名访问统计,真是瞎猫碰上死耗子。可是平头哥是一名具备专研精神的非专业搬砖员,通过平头哥不严格的测试得出,subString()方法比split()方法效率高出一大截。这种不知道有多少层的东西用递归或者循环总归是不会错的,为何这么说呢?由于以平头哥的智商也想不出其余方式啦。平头哥基于递归或者循环实现了三种解决方式,那平头哥就来给小伙伴们介绍介绍这三种方式。dom

while 方式的实现

/** * 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 循环方式实现

/** * 三层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分红discussleetcodecom。而后双层遍历将域名组合起来,最外层第一次遍历完以后获得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(".")来判断域名中是否含有子域名,若是存在,则截取新的域名继续调用自身,直到没有子域名为止。网站

以上就是平头哥给小伙伴们带来关于子域名访问计数的算法分享,不知道是否给小伙伴们讲明白?对于这道算法题,小伙伴们你是否还有其余思路呢?请在留言区与其余的小伙伴分享吧。ui

扫码关注公众号(搜索公众号:平头哥的技术博文)一块儿交流学习呗

扫码关注公众号(搜索公众号:平头哥的技术博文)一块儿交流学习呗
相关文章
相关标签/搜索