條件:html
給出一個文件,其中每行一個IP段(IPv4,其實IPv6相似,只是規模劇增)及其對應的信息(例如物理地址信息),內容及格式爲:java
<start_IP> <end_IP> <country> <province/state> <city> <other_info>算法
內容說明:數據庫
1. <start_IP> <end_IP>都爲包含狀態,即[<start_IP>, <end_IP>]數組
2. 各IP段不重合post
3. IP段有空洞,即有些IP不能找到對應段性能
問題:搜索引擎
給出一個IP,請實現算法返回其對應的<country> <province/state> <city> <other_info>等信息url
算法一:spa
擴展每一個IP段,構造每一個IP與其對應<country> <province/state> <city> <other_info>信息的結構,以IP做key讀入內存
優勢:簡單易實現
缺點:佔用內容過大
算法二:
擴展每一個IP段,構造每一個IP與其對應<country> <province/state> <city> <other_info>信息的數據,以IP做key寫入數據庫
優勢:簡單易實現
缺點:須要額外的數據庫服務,使用頻繁時性能不高
算法三:
近似查找。此方法有兩個關鍵點:
1. 查找過程當中須要比較IP的大小,所以須要實現IP的比較。也有兩種實現:1) 實現一個IP的比較器 2) 把IP轉換爲數字:IP看做是一個4位的數字,每位之間進制256,例如10.13.183.72=10 * 256^3 + 13 * 256^2 + 183 * 256^1 + 72 * 256 ^0,這樣需把給定的IP和每一段IP的<start_IP>, <end_IP>都作相同轉換,便於比較
代碼:
long num = 16777216L * Long.parseLong(ips[0]) + 65536L * Long.parseLong(ips[1]) + 256 * Long.parseLong(ips[2]) + Long.parseLong(ips[3]); return num;
或更快速的移位實現:
long num = 0; for (int i = 0; i < ips.length; i++) { num += Long.parseLong(ips[i]) << (8 * (ips.length - 1 - i)); }
return num;
2. 查找算法須要改進,由於是查找一個值所屬的範圍,並不是直接命中式的查找。以折半查找爲例,改造以下:
1) 將IP段的<start_IP>轉換爲數字,構造查詢有序數組A。以每段的<end_IP>構造另外一個備查數組B,同一段的<start_IP> <end_IP>在各自數組中索引一致。
2) 命中算法改成近似算法,更精確的叫做向下最近算法,基於數組A執行折半查找
3) 以A中的索引位查找數組B,確認目標IP在對應段中。防止誤查本應在空洞段中的IP
#數組A private List<Long> startIPNOsList = new ArrayList<Long>(); #數組B private List<Long> endIPNOsList = new ArrayList<Long>(); #對應信息數組C private List<Address> addressList = new ArrayList<Address>(); //...構造上述數組 /** * 實現查找 */ public Address detect(String ip) { long ipNO = IPv4Util.convertIP2Long(ip); if (ipNO > 0) { int idx = containBinSearch(ipNO, 0, startIPNOsList.size() - 1); if (idx > 0 && ipNO <= endIPNOsList.get(idx)) { return addressList.get(idx); } } short zero = 0; return new Address(zero, zero, zero); } /** * 若是arr[i] <= target && arr[i + 1] > target,則返回i * @param target 查找目標 * @param low * @param high * @return */ private int containBinSearch(long target, int low, int high) { //表示沒查到 if (high < low) { return -1; } int mid = (low + high) / 2; if (mid == startIPNOsList.size() - 1) { return mid; } if (startIPNOsList.get(mid) > target) { return containBinSearch(target, low, mid - 1); } else if (startIPNOsList.get(mid + 1) <= target) { return containBinSearch(target, mid + 1, high); } else { return mid; } }
優勢:佔用內存小,查詢速度不錯
缺點:實現稍複雜
算法四:
Trie樹實現。
「Trie樹即字典樹。
具體實現:擴展每一個IP段,將每一個IP加入Trie樹。每一個ip位構造一個節點,每一個葉子節點包含<country> <province/state> <city> <other_info>等信息,最後構造一個5層的trie樹。
優勢:查詢速度快,實現較簡單
缺點:佔用內存超大
算法五:
近似trie樹查找。基本結構與算法四相同,查找算法需改進,構造樹時需在葉節點中包含<end_ip>。
實現:
對於IP第1-4位,分別在trie樹第2-5層節點應用以下邏輯
1 查找當前層中最大的不大於當前位的節點
a) 若是當前節點是葉節點,驗證目標IP是否大於<end_ip>:大於返回空,不然返回對應<country>等信息
b) 若是查找到的節點值等於當前位的值,向下一層重複此邏輯
c) 若是查找到的節點值小於當前位的值,返回本節點最大值的葉節點
2 若是查找不到,本節點的父節點值 -1,本節點及其各子節點置爲255,返回上一層重複此邏輯
優勢:速度快,較算法4佔內存小
肯定:查找算法複雜,內存佔用仍舊很大