1. 程式人生 > >BAT面試上機題從3億個ip中找出訪問次數最多的IP詳解

BAT面試上機題從3億個ip中找出訪問次數最多的IP詳解

我們面臨的問題有以下兩點:
1)資料量太大,無法在短時間內解決;
2)記憶體不夠,沒辦法裝下那麼多的資料。
而對應的辦法其實也就是分成1)針對時間,合適的演算法+合適的資料結構來提高處理效率;2)針對空間,就是分而治之,將大資料量拆分成多個比較小的資料片,然後對其各個資料片進行處理,最後再處理各個資料片的結果。
原文中也給出一個問題,"從3億個ip中訪問次數最多的IP",就試著來解決一下吧。
1)首先,生成3億條資料,為了產生更多的重複ip,前面兩節就不變了,只隨機生成後面的2節。

	private static String generateIp() {
		return "192.168." + (int) (Math.random() * 255) + "."
				+ (int) (Math.random() * 255) + "\n";
	}
	private static void generateIpsFile() {
		File file = new File(FILE_NAME);
		try {
			FileWriter fileWriter = new FileWriter(file);
			for (int i = 0; i < MAX_NUM; i++) {
				fileWriter.write(generateIp());
			}
			fileWriter.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

1個char是一個Byte,每個ip大概是11Btye,所以生成的ip檔案,大概是3,500,000 KB,如下:


2)檔案生成了,那麼我們現在就要假設記憶體不是很夠,沒有辦法一次性裝入那麼多的資料,所以要先把檔案給拆分成多個小檔案。
在這裡採取的是就是Hash取模的方式,將字串的ip地址給轉換成一個長整數,並將這個數對3000取模,將模一樣的ip放到同一個檔案,這樣就能夠生成3000個小檔案,每個檔案就只有1M多,在這裡已經是足夠小的了。
首先是hash跟取模函式:

	private static String hash(String ip) {
		long numIp = ipToLong(ip);
		return String.valueOf(numIp % HASH_NUM);
	}
 
	private static long ipToLong(String strIp) {
		long[] ip = new long[4];
		int position1 = strIp.indexOf(".");
		int position2 = strIp.indexOf(".", position1 + 1);
		int position3 = strIp.indexOf(".", position2 + 1);
 
		ip[0] = Long.parseLong(strIp.substring(0, position1));
		ip[1] = Long.parseLong(strIp.substring(position1 + 1, position2));
		ip[2] = Long.parseLong(strIp.substring(position2 + 1, position3));
		ip[3] = Long.parseLong(strIp.substring(position3 + 1));
		return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3];
	}

2.1)將字串的ip轉換成長整數
2.2)對HASH_NUM,這裡HASH_NUM = 3000;
下面是拆檔案的函式:

	private static void divideIpsFile() {
		File file = new File(FILE_NAME);
		Map<String, StringBuilder> map  = new HashMap<String,StringBuilder>();
		int count = 0;
		try {
			FileReader fileReader = new FileReader(file);
			BufferedReader br = new BufferedReader(fileReader);
			String ip;
			while ((ip = br.readLine()) != null) {
				String hashIp = hash(ip);
				if(map.containsKey(hashIp)){
					StringBuilder sb = (StringBuilder)map.get(hashIp);
					sb.append(ip).append("\n");
					map.put(hashIp, sb);
				}else{
					StringBuilder sb = new StringBuilder(ip);
					sb.append("\n");
					map.put(hashIp, sb);
				}
				count++;
				if(count == 4000000){
					Iterator<String> it = map.keySet().iterator();					
					while(it.hasNext()){
						String fileName = it.next();
						File ipFile = new File(FOLDER + "/" + fileName + ".txt");
						FileWriter fileWriter = new FileWriter(ipFile, true);
						StringBuilder sb = map.get(fileName);				
						fileWriter.write(sb.toString());;
						fileWriter.close();
					}
					count = 0;
					map.clear();
				}
			}
			br.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

2.3)在這裡,我們如果每讀取一個ip,經過hash對映之後,就直接開啟檔案,將其加到對應的檔案末尾,那麼有3億條ip,我們就要讀寫檔案3億次,那IO開銷的時候就相當大,所以我們可以先拿一個Map放著,等到一定的規模之後,再統一寫進檔案,然後把map清空,繼續對映,這樣的話,就能夠提高折分的速度。而這個規模,就是根據能處理的記憶體來取的值的,如果記憶體夠大,這個值就可以設定大點,如果記憶體小,就要設定小一點的值,IO開銷跟記憶體大小,總是需要在這兩者之間的取個平衡點的。
可以看到,這樣我們拆分成了3000個小檔案,每個檔案只有1100KB左右,所耗的時間如下,17分鐘到18分鐘左右:

Start Divide Ips File: 06:18:11.103
End:                   06:25:44.134

而這種對映可以保證同樣的IP會對映到相同的檔案中,這樣後面在統計IP的時候,就可以保證在a檔案中不是最多次數的ip(即使是第2多),也不會出現在其它的檔案中。
3)檔案拆分了之後,接下來我們就要分別讀取這3000個小檔案,統計其中每個IP出現的次數。

	private static void calculate() {
		File folder = new File(FOLDER);
		File[] files = folder.listFiles();
		FileReader fileReader;
		BufferedReader br;
		for (File file : files) {
			try {
				fileReader = new FileReader(file);
				br = new BufferedReader(fileReader);
				String ip;
				Map<String, Integer> tmpMap = new HashMap<String, Integer>();
				while ((ip = br.readLine()) != null) {
					if (tmpMap.containsKey(ip)) {
						int count = tmpMap.get(ip);
						tmpMap.put(ip, count + 1);
					} else {
						tmpMap.put(ip, 0);
					}
				}	
				fileReader.close();
				br.close();
				count(tmpMap,map);
				tmpMap.clear();
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		count(map,finalMap);		
		Iterator<String> it = finalMap.keySet().iterator();
		while(it.hasNext()){
			String ip = it.next();
			System.out.println("result IP : " + ip + " | count = " + finalMap.get(ip));
		}
		
	}		
 
	private static void count(Map<String, Integer> pMap, Map<String, Integer> resultMap) {
		Iterator<Entry<String, Integer>> it = pMap.entrySet().iterator();
		int max = 0;
		String resultIp = "";
		while (it.hasNext()) {
			Entry<String, Integer> entry = (Entry<String, Integer>) it.next();
			if (entry.getValue() > max) {
				max = entry.getValue();
				resultIp = entry.getKey();
			}
		}
		resultMap.put(resultIp,max);	
	}

3.1)第一步要讀取每個檔案,將其中的ip放到一個Map中,然後呼叫count()方法,找出map中最大訪問次數的ip,將ip和最多訪問次數存到另外一個map中。
3.2)當3000個檔案都讀取完之後,我們就會產生一個有3000條記錄的map,裡面儲存了每個檔案中訪問次數最多的ip,我們再呼叫count()方法,找出這個map中訪問次數最大的ip,即這3000個檔案中,哪個檔案中的最高訪問量的IP,才是真正最高的,好像小組賽到決賽一樣。。。。
3.3)在這裡沒有用到什麼堆排序和快速排序,因為只需要一個最大值,所以只要拿當前的最大值跟接下來的值判斷就好,其實也相當跟只有一個元素的堆的堆頂元素比較。
下面就是我們的結果 。

Start Calculate Ips: 06:37:51.088
result IP : 192.168.67.98 | count = 1707
End: 06:54:30.221

到這裡,我們就把這個ip給查找出來了。
其實理解了這個思路,其它的海量資料問題,雖然可能各個問題有各個問題的特殊之處,但總的思路我覺得應該是相似的。

相關推薦

BAT面試上機3ip訪問次數IP

我們面臨的問題有以下兩點:1)資料量太大,無法在短時間內解決;2)記憶體不夠,沒辦法裝下那麼多的資料。而對應的辦法其實也就是分成1)針對時間,合適的演算法+合適的資料結構來提高處理效率;2)針對空間,就是分而治之,將大資料量拆分成多個比較小的資料片,然後對其各個資料片進行處理,最後再處理各個資料片的結果。原文

1ip訪問次數IP

問題一:怎麼在海量資料中找出重複次數最多的一個演算法思想:方案1:先做hash,然後求模對映為小檔案,求出每個小檔案中重複次數最多的一個,並記錄重複次數。        然後找出上一步求出的資料中重複次數最多的一個就是所求(如下)。問題二:        網站日誌中記錄了使用

HashMap出現次數的鍵

import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.

ip出現次數IP(分治法)

/* 1,hash雜湊 2,找到每個塊出現次數最多的(默認出現均勻)—–>可以用字典樹 3,在每個塊出現最多的資料中挑選出最大的為結果 */ 問題一: 怎麼在海量資料中找出重複次數最多的一個 演算法思想: 方

100數字大的10

1、首先一點,對於海量資料處理,思路基本上是:必須分塊處理,然後再合併起來。 2、對於每一塊必須找出10個最大的數,因為第一塊中10個最大數中的最小的,可能比第二塊中10最大數中的最大的還要大。 3、分塊處理,再合併。也就是Google MapReduce 的基本思想。Google有很多的伺服器,每個伺服器

python兩種方法實現1000萬隨機數top n元素(附c語言版)

轉載請註明地址:http://blog.csdn.net/echoutopia/article/details/51731269 很早之前看到一道面試題: 有一個長度為1000w個數組,每個元素互不重複,找出其中top n元素。 我感覺重複或者不重複都差不多,所以沒管不重複

有10整數,要求選取重複次數的100整數

(1)如果沒有記憶體限制,且假設是32位無符號的整數。最方便的辦法就是建立一個整形陣列,int hash[2^32](贊不考慮程式的虛地址空間上限),然後對這10億個數進行一次遍歷,這樣,可以得到這2^32個數各自出現的次數,再對這個hash陣列進行取第k大元素,100次後,就可以取出這出現次數最多的前100

十萬個數據,重複次數的十資料並列印

利用集合 ,找出十萬個數據中,重複次數最多的十個資料並列印. 先通過Hashmap儲存, key為資料,value為它出現的次數. 然後用優先順序佇列,儲存型別為Map.Entry,重寫比較器類,利用value進行比較. 優先順序佇列中利用小根堆形式,只儲存十個. 接下來遍歷其他的,如果比小

python(dict字典相關知識以及小例子:生成一個列表,存放100隨機整數,出現次數的數字)

一、什麼是字典? #字典的使用 #子字典是一個容器類,可以用來儲存資料 #列表儲存資料特點:1、有序的 2、每一個都有一個索引,通過索引可以對資料進行查詢,修改,刪除 #字典儲存資料: key:v

一道算法-1到n整數1出現的次數

參考 操作 包括 amp blank 一位 時間 www 沒有 1. 題目描述 輸入一個整數n,求從1到n這n個整數的十進制表示中1出現的次數。例如輸入12,從1到12這些整數中包含1的數字有1,10,11和12,1一共出現了5次。 2. 題目來源 第一次看到是在《劍指Of

如何幾千檔案尋找指定的內容

今天在處理資料時遇到這麼個問題,如何從幾千個txt檔案中找到我想要的內容呢? 這是我的實現思路。 讀取檔案 選中指定的內容段 在新路徑下儲存內容段並命名為之前的檔名 以下是用python實現的程式碼。有詳細註釋! #!/usr/bin/env python3 # -

資料結構與演算法——有1整數,大的1000,要求時間越短越好,空間佔用越少越好

有1億個整數,找出最大的1000個,要求時間越短越好,空間佔用越少越好(迅雷筆試) 首先要明白1億個整數佔用多大的記憶體,按每個整數4個位元組來算,用400000000B,大約400000KB,大約4

編寫程式鍵盤得到三整數,其中的大數

為了更加了解指標,此次採用指標來判斷大小 從鍵盤輸入三個整數,除了宣告外不允許出現整數型別變數 #include<stdio.h> int main(void){ int num1 = 0,num2 = 0,num3 = 0; int *p_nu

100整數,位數

100億個整數,記憶體足夠,如何找到中位數?記憶體不足,如何找到中位數? (1)當記憶體足夠時: 採用快排,找到第n大的數。 • 隨機選取一個數,將比它小的元素放在它左邊,比它大的元

C語言:p所指字符串ASCII碼大的字符,將其放在第一位置上,並將該字符前的原字符向後順序移動。

lose 使用數組 max code 數據 scanf 打開文件 stdio.h ted //fun函數:從p所指字符串中找出ASCII碼最大的字符,將其放在第一個位置上,並將該字符前的原字符向後順序移動。 1 #include <stdio.h>

javascript實現:在N字串長的公子串

  1 'use strict' 2 3 function 找出最長公子串 (...strings) { 4 let arraiesOfSubStrings = [] 5 arrayOfStrings.reduce((accumulator, currentVal

網易雲音樂音樂外鏈製作背景音樂

從網易雲音樂中找出音樂外鏈 最近想把做的網頁放上點背景音樂,因為要放到github page上不想下載,一直想要找網上的連結,無意中Network找到了。ヾ(≧O≦)〃嗷~ 先上案例: 步驟

TOP-K排序演算法,海量不重複資料大/小的K個數

如題,TOP-K排序的主要功能是找出一堆不重複資料中的最小或最大的幾個數,此處我們介紹這種型別題目的某種解法: 最大最小堆,最大堆結構裡面的每一個數不都是小於root的值麼?和我們要解決的問題很像。由此,我們可以構造一個堆,並且用它來儲存我們需要找的那幾個數。有這麼一個動態

n整數連續m個數加和是大Java版

即上一篇Python版取連續加和最大的整數後,本篇部落格帶來Java版取連續加和最大的整數。總體的思路入上一次部落格中所述,就不在過多的闡述,關鍵就在於如何應用Java API寫出相同邏輯的程式碼。

n整數連續m個數加和是大Python版

最近在看資料時看到了一個如標題所示的面試題,面試題是Java版,正好最近在學Python,就先用Python實現了。畢竟life is short,use python(玩笑話,演算法設計思路是共同的