這篇文章是《讀薄<編程珠璣>》系列博客的第一篇,在這篇文章中,我總結了在書中出現的一些問題以及一些解決方案。
問題集合
0x01:一個最多包含n個正整數的文件,每個數都小於n,其中n=107,並且沒有重複。最多有1MB內存可用。要求用最快方式將它們排序並按升序輸出
0x02:使用位邏輯運算來實現位向量
0x03:儘可能快的生成位於 0~n-1 之間的 k 個隨機不同順序的整數
0x04:如果在問題
0x01
中需要1.25MB 的內存空間來進行排序,而我們只有1MB 空間該如何處理?0x05:如果在問題
0x01
中,每個數字出現的次數不是1次,而是不多於10次,該如何處理?0x06:如果說我們的位向量非常的稀疏,可能10000位裏只有100位得到了使用,這樣的話大量的時間都會浪費在初始化空間上,該如何解決這個問題?
0x07:如何組織電話號碼的存儲,來能夠支持高效的插入和檢索操作?
方案集合
0x01:把文件一次讀入,出現的數字在位向量對應索引處中標註爲1,讀取完文件之後,將位向量從低位向高位依次將爲1的索引輸出即可。(具體實現詳見:http://blog.luoyuanhang.com/2016/05/15/I-%E4%BD%8D%E5%90%91%E9%87%8F%E7%9A%84%E5%AE%9E%E7%8E%B0%E4%B8%8E%E5%BA%94%E7%94%A8/)
0x02:
#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 10000000
int a[1 + N/BITSPERWORD];
void set(int i){ a[i>>SHIFT] |= (1<<(i & MASK)); }
void clr(int i){ a[i>>SHIFT] &= ~(1<<(i & MASK)); }
void set(int i){ a[i>>SHIFT] &= (1<<(i & MASK)); }
(詳細分析請見:位向量的實現與應用)
- 0x03:
首先,生成一個大小爲 N 的數組,每個值都等於它的索引值。
然後,遍歷該數組 0~K-1 的位置,將遍歷的第 i 個位置的值與隨機的 第 K~N-1 的數字進行交換。
代碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 100
#define K 80
void swap(int *i, int *j);
int randint(int m, int n);
int main(void)
{
srand((unsigned)time(NULL));
int x[N];
int i;
for(i = 0; i < N; i++)
{
x[i] = i;
}
for(i = 0; i < K; i++)
{
swap(&x[i], &x[randint(i+1,N-1)]);
}
for(i = 0; i < K; i++)
{
printf("%d ", x[i]);
}
}
int randint(int m, int n)
{
return (rand()%(n-m+1) + m);
}
void swap(int *i, int *j)
{
*j = *j ^ *i;
*i = *i ^ *j;
*j = *j ^ *i;
}
0x04:我們可以使用2趟排序,第一次排序前一半,然後把前一半的位圖結果存到硬盤,然後再處理後一半。這種方法需要讀兩遍文件。
0x05:我們可以在位圖中使用4位來表示該數字出現的次數,例:
0000
表示沒有出現,1010
表示出現10次。0x06:我們可以對位向量已經使用的位進行標註,只有將要使用的位才進行初始化。
我們藉助兩個 n 元向量和一個變量 top(初始爲0) 來標識初始化向量,下面的代碼實現了對數組元素 i 的首次訪問:
from[i] = top;
to[top] = i;
data[i] = 1;
top++;
from[i] = top;
表示將 i 在 to 中的索引值存放到 from 的第 i 位中
to[top] = i;
表示 to 的第 top 位存放的是 i 的值,即 data 中的第 i 位被標註
判斷第 i 位是否被初始化的方法:
(from[i] < top) && (to[from[i]] == i)
單憑第一個條件並不能確定第 i 位已被初始化因爲 from 中的第 i 位有可能沒被初始化,是一個隨機的值。如果 to 中的第 from[i] 位等於 i 就能夠說明 data[i] 已經被初始化。
- 0x07:使用電話號碼的後兩位來組織一個散列表,因爲電話號碼的後兩位隨機性比較強適合作爲散列函數。
本文的版權歸作者 羅遠航 所有,採用 Attribution-NonCommercial 3.0 License。任何人可以進行轉載、分享,但不可在未經允許的情況下用於商業用途;轉載請註明出處。感謝配合!