【刷題】獲取一億數據獲取前100個最大值


【來自程序員面試寶典】有1千萬條短信,找出重復出現最多的前10條

題目:

  • 有1千萬條短信,有重復,以文本文件的形式保存,一行一條,有重復。請用5分鍾時間,找出重復出現最多的前10條。

解析:

  • 對於本題來說,某些面試者想用數據庫的辦法來實現:首先將文本導入數據庫,再利用select語句某些方法得出前10條短信。但實際上用數據庫是滿足不了5分鍾解決這個條件的。這是因為1千萬條短信即使1秒鍾錄入1萬條(這已經算是很快的數據錄入了)5分鍾才300萬條。即使真的能在5分鍾內錄入完1千萬條,也必須先建索引,不然sql語句5分鍾內肯定得不出結果。但對1千萬條記錄建索引即使在5分鍾之內都不可能完成的。所以用數據庫的辦法是不行的。
  • 這種類型的題之所以會出現,這是因為互聯網公司無時無刻都在需要處理由用戶產生的海量數據/日志,所以海量數據的題現在很熱,基本上互聯網公司都會考。重點考察的是你的數據結構設計和算法的基本功。類似題目是如何根據關鍵詞搜索訪問最多的前10個網站。

答案:

方法1:

  • 可以用哈希表的方法對1千萬條分成若干組進行邊掃描邊建散列表。第一次掃描,取首字節,尾字節,中間隨便兩字節作為Hash Code,插入到hash table中。並記錄其地址和信息長度和重復次數,1千萬條信息,記錄這幾個信息還放得下。同Hash Code且等長就疑似相同,比較一下。相同記錄只加1次進hash table,但將重復次數加1。一次掃描以后,已經記錄各自的重復次數,進行第二次hash table的處理。用線性時間選擇可在O(n)的級別上完成前10條的尋找。分組后每份中的top10必須保證各不相同,可hash來保證,也可直接按hash值的大小來分類。

方法2:

  • 可以采用從小到大排序的方法,根據經驗,除非是群發的過節短信,否則字數越少的短信出現重復的幾率越高。建議從字數少的短信開始找起,比如一開始搜一個字的短信,找出重復出現的top10並分別記錄出現次數,然后搜兩個字的,依次類推。對於對相同字數的比較常的短信的搜索,除了hash之類的算法外,可以選擇只抽取頭、中和尾等幾個位置的字符進行粗判,因為此種判斷方式是為了加快查找速度但未能得到真正期望的top10,因此需要做標記;如此搜索一遍后,可以從各次top10結果中找到備選的top10,如果這top10中有剛才做過標記的,則對其對應字數的所有短信進行精確搜索以找到真正的top10並再次比較。

方法3:

  • 可以采用內存映射的辦法,首先1千萬條短信按現在的短信長度將不會超過1G空間,使用內存映射文件比較合適。可以一次映射(當然如果更大的數據量的話,可以采用分段映射),由於不需要頻繁使用文件I/O和頻繁分配小內存,這將大大提高數據的加載速度。其次,對每條短信的第i(i從0到70)個字母按ASCII嘛進行分組,其實也就是創建樹。i是樹的深度,也是短信第i個字母。

分析

  • 該問題主要是解決兩方面的內容,一是內容加載,二是短信內容比較。采用文件內存映射技術可以解決內容加載的性能問題(不僅僅不需要調用文件I/O函數,而且也不需要每讀出一條短信都分配一小塊內存),而使用樹技術可以有效減少比較的次數。

【來自程序員面試寶典】執行數據庫查詢時,如果要查詢的數據有很多,假設有1000萬條,用什么方法可以提高查詢效率(速度)?在數據庫方面或Java代碼方面有什么優化的辦法?

1.在數據庫設計方面

  • (1) 建立索引

  • (2) 分區(MySql,比如按時間分區)

  • (3) 限制字段長度

2.在數據庫I/O方面

  • (1) 增加緩沖區

  • (2) 如果設計標的級聯,不同的表存儲在不同的磁盤上,以增加I/O的讀寫效率

3.在SQL語句方面

  • (1) 優化SQL語句,減少比較的次數

  • (2) 限制返回的條目數(MySql中使用limit)

4.在Java方面

  • 如果是反復使用的查詢,使用PreparedStatement減少查詢的次數。

java代碼:

概述:獲取一億數據獲取前100個最大值

    1. 假設數組為 array[N] (N = 1 億),首先利用quicksort的原理把array分成兩個部分,左邊部分比 array[N - 1] (array中的最后一個值,即pivot) 大, 右邊部分比pivot 小。然后,可以得到 array[array.length - 1] (即 pivot) 在整個數組中的位置,假設是 k.
    1. 如果 k 比 99 大,我們在數組[0, k - 1]里找前 100 最大值。 (繼續遞歸)
    1. 如果 k 比 99 小, 我們在數組[k + 1, ..., N ]里找前 100 - (k + 1) 最大值。(繼續遞歸)
    1. 如果 k == 99, 那么數組的前 100 個值一定是最大的。(退出)
import java.util.Random;
 
public class TopHundredQuickSort {
 
	public static void main(String[] args) {
		// the size of the array
		int number = 100000000;
		// the top k values
		int k = 100;
		// the range of the values in the array
		int range = 1000000001;
		
		// input for minHeap based method
		int[] array = new int[number];
		
		Random random = new Random();
		for(int i=0; i<number; i++){
			array[i] = random.nextInt(range);
		}
		
		TopHundredQuickSort topHundred = new TopHundredQuickSort();
		
		// start time
		long t1 = System.currentTimeMillis();
		topHundred.tophundred(array, 0, array.length-1, k);
		
		// end time
		long t2 = System.currentTimeMillis();
		
		System.out.println("The total execution time of quicksort based method is" + (t2-t1) + " millisecond");
		
		// print out the top k largest values in the top array
		System.out.println("The top " + k + " largest values are:");
		for(int i=0; i<k; i++){
			System.out.println(array[i]);
		}
	}
 
	private void tophundred(int[] array, int start, int end, int k) {
		int switchPointer = start;
		// array最后一個值作為pivot
		int pivot = array[end];  
		for(int i=start; i<end; i++){
			if(array[i] >= pivot){
				swap(array, switchPointer, i);
				switchPointer++;
			}
		}
		
		// 交換后 array左邊的值比pivot大   右邊的值比pivot小
		swap(array, end, switchPointer);
		
		if(switchPointer < k-1){
			tophundred(array, switchPointer+1, end, k);
		}else if(switchPointer == k-1){
			return;
		}else{
			tophundred(array, 0, switchPointer-1, k);
		}
		
	}
 
	private void swap(int[] array, int i, int j) {
		int temp = array[i];
		array[i] = array[j];
		array[j] = temp;
	}
 
}

參考鏈接

END


免責聲明!

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



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