編程珠璣 -- 第一章 位圖排序

前言:趁著京東618,也瘋狂的剁手了一回,狂買了幾本編程相關的書籍。從12年開始接觸編程,一直到現在,斷斷續續的也學習了4,5種語言,像什么C/C++/C#,Object-C, Java, PHP等。不過一直以來的目的主要都是為了工作,需要什么學習什么,沒有什么具體的目標,或者說沒有深入的去研究一下算法,編程思想之類的。剛好,最近的工作不是很飽和,就找一些相關的書籍充實一下自己,提高一下。目前到手的有《編程珠璣》,《代碼的整潔之道》,《計算機程序設計》等,現在就講講讀后感,或者說一些習題的代碼實現等。

第一章

主要是一些基礎性的介紹,其中涉及到的問題也就是一個排序,如何使用最短的時間,最小的空間,去完成一個大型的整形數組的排序。

具體問題如下

題目:一個最多包含n個正整數的文件,每個數都小于n,其中n=10^7,且所有正整數都不重復。求如何將這n個正整數升序排列。
約束:最多有1MB的內存空間可用,有充足的磁盤存儲空間。


方法1(多通道法):

第1遍遍歷文件,將文件中范圍在1~ 249 999的正整數讀取進入1MB內存,排序(可以使用各種排序方法),將排序后的正整數存儲在磁盤文件temp中
第2遍遍歷文件,將文件中范圍在250 000~499 999的正整數讀取進入1MB內存,排序,將排序后的正整數加入存儲在磁盤文件temp中
….
第40遍遍歷文件,將文件中范圍在10^7-250 000~10^7的正整數讀取進入1MB內存,排序,將排序后的整數加入存儲在磁盤文件temp中
輸出temp文件

方法2(位圖法):

我們想使用hash映射,將對應的正整數映射到位圖集合中。即將正整數映射到bit集合中,每一個bit代表其映射的正整數是否存在。
比如{1,2,3,5,8,13}使用下列集合表示:
0 1 1 1 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0

我們可以使用具有10^7位的字符串來表示這個文件。其中,當且僅當整數i在文件中存在時候,第i位為1

接著,我們來完成具體的方法實現,首先我們約束一些基本的全局變量

// int 類型有4個字節,即32位
#define BITSPERWORD 32

// 定義右移的位數(即除以32,表示右移5位)
#define SHIFT 5

// 16進制31 二進制的話就是 0001 1111
#define MASK 0x1F

// 整數的最大值
#define N 10000000

// 最大數量
#define K 1000000

/// 大型數組必須是全局變量,或者使用static,vector修飾,否則會直接報錯,棧的空間不夠用
// 存放輸入數據,即隨機整數數組
int a[N];

// 位圖排序使用的數組
int sortBit[1 + N/BITSPERWORD];

在完成具體的排序工作之前,我們還有一個準備工作需要完成,就是實現一個隨機整數的數組獲取。

/*
 *  交換兩個數組內元素位置
 */
void swap(int *a, int *b)
{
    if(*a != *b){                 // 異或操作 交換兩個數字的位置
        *a ^= *b;
        *b ^= *a;
        *a ^= *b;
    }
    
}

/******************************************************************************
 *函數名稱:void generateDiffRandV1(int a[], int n)
 *函數功能:產生互不相同的隨機數
 *入口參數:
 *返 回 值:無
 *備 注:以空間換時間
 *******************************************************************************/
void generateDiffRandV1(int a[], int n, int k)
{
    int i;
    time_t t;
    
    for (i = 0; i < n; i++){
        a[i] = i;
    }
    
    srand((int)time(&t));
    for (i = 0; i < k; i++){
        swap(&a[i], &a[i+rand()%(n-i)]);
    }
}

/******************************************************************************
 *函數名稱:void generateDiffRandV2(int a[], int n)
 *函數功能:產生互不相同的隨機數(產生隨機數的范圍是1~n-1)
 *入口參數:
 *返 回 值:無
 *
 *思 路:先生成一個放置座號的數組,然后從中隨機抽取,抽取后為防止重復,立即歸零。
 * :每次生成座號,只需判斷是否為0 即可,大大提高了程序執行的效率。
 *******************************************************************************/
void generateDiffRandV2(int a[], int n)
{
    int *flag =(int *)malloc(sizeof(int) * n);
    static int flag_once = 0;
    int i, index;
    
    for(i = 0; i < n; i++)
        flag[i] = i+1;
    if(!flag_once){
        srand((unsigned)time(0));
        flag_once = 1;
    }
    
    for(i = 0; i < n;){
        index = rand() % n;
        if(flag[index] != 0){
            a[i++] = flag[index]-1;
            flag[index] = 0;
        }
    }
    free(flag);
}
  • 第一種情況:該數組中每一個數字最多出現一次

通過構建新的整形數組,來存放相關的位圖信息。

整形的長度為4個字節,即32位,則數組長度為N/32 + 1。而每一個整形元素都可以表示相對應的區間內,數字是否存在。比如說待排序集合{1,2,3,5,8,13}可以表示為:0-1-1-1 0-1-0-0 1-0-0-0 0-1-0-0,在整形數組中,則表示為8494, 0-0-1-0 0-0-0-1 0-0-1-0 1-1-1-0 。這個時候,我們就需要查找該位置對應的數組里面的位置。首先,i/32(從位圖的方面理解就是,i>>5,向右位移5位),即表示在數組中的下標。然后i%32,對32取余(位圖算法就表示i & 0x1F),獲取他在整形的二進制文件中的位置,得到結果后,設為1。
比如13,二進制位0001 0101 , 右移5位,結果為0,則該位圖信息應該存放在sortBit[0],同時,13對32取模,即余數為13,位圖運算為0001 0101 & 1111 1111 = 0001 0101,即在13位我們需要設置為1,那么,將1左移到13位,即0000 0001 << 0001 0101 = 0010 0000 0000 0000 假設之前的位圖信息為0-0-0-0 0-0-0-1 0-0-1-0 1-1-1-0,即表示1,2,3,5,8存在,兩者取或,0-0-1-0 0-0-0-0 0-0-0-0 0-0-0-0 | 0-1-1-1 0-1-0-0 1-0-0-0 0-0-0-0 = 0-0-1-0 0-0-0-1 0-0-1-0 1-1-1-0

具體的代碼如下:

/*
 *  設置對應的位置為1
 */
void set(int i) {
    /// i & MASK 相當于 除以32然后取余
    sortBit[i>>SHIFT] |= (1<<(i & MASK));
}

/*
 *  初始化數據為0
 */
void clr(int i) {
    /// i & MASK 相當于 除以32然后取余
    sortBit[i>>SHIFT] &= ~(1<<(i & MASK));
}

/*
 *  查找該數字是否存在
 */
int test(int i) {
    /// i & MASK 相當于 除以32然后取余
    return sortBit[i>>SHIFT] & (1<<(i & MASK));
}

通過C++的bitset來完成

// 導入相關類庫
#include <bitset>

void sortByBitSet(int size) {
    int n = 0;
    
    // 位數可以多1位,避免數據丟失
    bitset<1 + N> bit;                     //初始默認所有二進制位為0
    
    while(n < size)
    {
        bit.set(a[n], 1);                   //將第n位置1
        n ++;
    }
    
    for(int i = 0; i <= size + 1; i++)
    {
        if(bit[i] == 1)
            cout << i << "  ";
    }
    
    cout << endl;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 內排序的歸并排序是采用二路歸并。 將已有序的子序列合并,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有...
    Arya鑫閱讀 14,295評論 0 10
  • 第一部分、十道海量數據處理面試題 1、海量日志數據,提取出某日訪問百度次數最多的那個IP。 此題,在我之前的一篇文...
    零一間閱讀 952評論 0 5
  • 前幾天與好幾個月沒見面的大學同學聚了聚,彼此交流著最近的想法,對工作,對生活,對愛情的。 畢業以后的我們有著各自的...
    風卿云談閱讀 797評論 0 2
  • 當需要獲取當前顯示的Fragment的時候,我們可以如下。 但是多層嵌套Fragment的時候,還是會有些問題,以...
    菠蘿魚_lc閱讀 22,175評論 0 3
  • 又到了離別的季節,我時常覺得,離別不需要悲傷,也不必悲傷。如果有人時常惦記自然是好的,但就算沒人惦記,也無妨。我們...
    緣神閱讀 228評論 0 0