五種算法實現IP到地址的轉換

條件: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樹即字典樹。

又稱單詞查找樹,Trie樹,是一種樹形結構,是一種哈希樹的變種。典型應用是用於統計,排序和保存大量的字符串(但不只限於字符串),因此常常被搜索引擎系統用於文本詞頻統計。它的優勢是:利用字符串的公共前綴來減小查詢時間,最大限度地減小無謂的字符串比較,查詢效率比 哈希表高。

它有3個基本性質:
根節點不包含字符,除根節點外每個節點都只包含一個字符; 從根節點到某一節點,路徑上通過的字符鏈接起來,爲該節點對應的字符串; 每一個節點的全部子節點包含的字符都不相同。」 -- 詳見 字典樹

具體實現:擴展每一個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佔內存小
肯定:查找算法複雜,內存佔用仍舊很大
 


轉載於:https://www.cnblogs.com/aprilrain/p/3502313.html