Trie樹的應用:查詢IP地址的ISP


1. 問題描述

給定一個IP地址,如何查詢其所屬的ISP,如:中國移動(ChinaMobile),中國電信(ChinaTelecom),中國鐵通(ChinaTietong)?現有ISP的IP地址區段可供下載,比如中國移動的IP地址段

103.20.112.0/22
103.21.176.0/22
111.0.0.0/20
112.0.0.0/10
117.128.0.0/10
120.192.0.0/10
183.192.0.0/10
211.103.0.0/17
211.136.0.0/14
211.140.0.0/15
211.142.0.0/17
211.142.128.0/17
211.143.0.0/16
218.200.0.0/14
218.204.0.0/15
218.206.0.0/15
221.130.0.0/15
221.176.0.0/13
223.112.0.0/14
223.116.0.0/15
223.120.0.0/13
223.64.0.0/11
223.96.0.0/12
36.128.0.0/10
39.128.0.0/10

上述網絡地址是CIDR記法:IP地址/網絡id位數,其中IP地址分為兩部分

  • 網絡id
  • 主機id

比如,192.168.23.35/21,其子網掩碼為11111111 11111111 11111000 00000000即255.255.248.0,網絡ID:192.168.00010111。IP地址103.20.112.168與地址段103.20.112.0/22前22位相匹配,則此IP地址屬於中國移動。


2. Trie樹實現查詢

Trie樹使用公共前綴,降低查詢時間,減小了存儲空間。為了構建Trie樹,將IP地址二進制化(32位的二進制)。用於查詢的Trie樹一棵二叉樹,滿足以下性質:

  • 非葉子節點的左孩子節點為0,右孩子節點為1;
  • 葉子節點存儲IP地址所對應的ISP。

Trie樹的Java實現

/*
 * Trie樹,用於存儲、檢索ip地址
 * 葉子節點標記為ip地址對應的ISP
 */
public class TrieTree {
    private Node root = null;   //根節點
	
    /*二叉樹的節點*/
    private static class Node {
        String element;  //非葉子節點為空 葉子節點標記為ISP
        Node[] children; //左孩子節點為0 右孩子節點為1
		
        public Node() {
			element = "";
			children = new Node[2];
			for (int i = 0; i < children.length; i++) {
				children[i] = null;
			}
		}
    }

    public TrieTree() {
        root = new Node();
    }   

    /*插入ip地址*/
    public void insert(Node root, String ipAddress, String isp) {
    	if(ipAddress.length() > 32) {
    		System.out.println("ip地址處理錯誤");
    	} else {
    		Node crawl = root;
    		for(int i=0; i<ipAddress.length(); i++) {
    			int index = (int) ipAddress.charAt(i) - '0';
    			if(crawl.children[index] == null) {
    				crawl.children[index] = new Node();
    			}
    			crawl = crawl.children[index];  			
    		}
    		crawl.element = isp;
    	}
    }
    
    public void insert(String ipAddress, String isp) {
    	insert(root, ipAddress, isp);
    }
    
    /*
     * 檢索ip地址,返回其所對應的ISP
     * 若不在Trie樹中,則返回null
     * */
    public String search(String binaryIP) {
    	Node crawl = root;
    	for(int i = 0; crawl.element.length() == 0; i++) {
			int index = (int) binaryIP.charAt(i) - '0';
			if(crawl.children[index] == null) {
				return null;
			}
			crawl = crawl.children[index];
		}
    	return crawl.element;
    }
}

IP地址格式化

下面的IPFormat給出兩個方法,實現

  • 將IP地址轉變成二進制
  • 從CIDR記法的IP地址中得到網絡ID部分
/*
 * IP地址CIDR記法:network.host/size
 * 比如:103.20.112.0/22
 * 功能:將IP地址轉換成其network地址
 */

public class IPFormat {
	/*將ip地址轉換成32位的二進制*/
	public static String toBinaryNumber(String ipAddress) {
		String[] octetArray = ipAddress.split("\\.");
		String binaryNumber = "";
		for(String str: octetArray) {
			int octet = Integer.parseInt(str, 10);
			String binaryOctet = Integer.toBinaryString(octet);
			int bolength = binaryOctet.length();
			if(bolength < 8) {
				for (int i = 0; i < 8 - bolength; i++) {
					binaryOctet = '0' + binaryOctet;			//補前導0
				}
			}
			binaryNumber += (binaryOctet);
		}
		return binaryNumber;
	}
	
	/*獲取network地址部分*/
	public static String getNetworkAddress(String cidrAddress) {
		String[] cidrArray = cidrAddress.split("/");
		String binaryNumber = toBinaryNumber(cidrArray[0]);
		int size = Integer.parseInt(cidrArray[1]);
		return binaryNumber.substring(0, size);
	}
	
	/*main方法用於測試*/
	public static void main(String[] args) {
		String ip = "103.20.112.0/20";
		String bn = getNetworkAddress(ip);
		System.out.println(bn);
	}
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM