一題算法 | 子域名訪問計數

題目

一個網站域名,如"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

掃碼關注公衆號(搜索公衆號:平頭哥的技術博文)一塊兒交流學習唄

掃碼關注公衆號(搜索公衆號:平頭哥的技術博文)一塊兒交流學習唄
相關文章
相關標籤/搜索