编程珠玑(2)第一章学习之位图排序


        上研快半年了,由于第一年属于上课阶段,导师也没怎么管,感觉自己还是本科那种碌碌无为的状态,知识停于书本,没有真正写出过什么像样的产品。叫我如何不焦虑!焦虑也没办法啊,所以就到网上买了本《编程珠玑》(第2版),下决心认认真真学习。对于我这样愚笨的人虽不见得会有奇效,但总比继续停在原地的好。于是,我打算以此博客为证,在今年7月之前(也就是2013年6月31日为止):将每一章(总共十五章)的问题用此博客记录下来,能用代码实现的一定实现,并附上自己对此问题的思考。

        

         今天是第一章,问题如下

          输入:一个最多包含n个正整数的文件,每个数都小于n,其中n=10^7,。如果在输入文件中有任何整数重复出现就是致命错误。没有其他数据与该整数关联。

          输出:按升序排列的输入整数列表。

          约束:最多有(大约)1MB的内存空间可用,有充足的磁盘存储空间可用。运行时间最多几分钟,运行时间为10秒就不需要进一步优化了。


        对问题的分析:

        由于文件最多包含n=10^7个整数,而每个整数可能都是7位数,所以如果每个数用7个字节来存储,1MB的存储空间大约可以存储143 000个号码。于是我们将每个号码用32位整数来表示,这样1MB的存储空间能存储250 000个号码。原书中提到使用归并排序和快排,但主要由于存储空间的限制,需要多次读取磁盘文件,这样大大增加了程序运行时间,皆不可取。

        通过对原问题的仔细观察,我们发现原问题排序的整数有以下特点:1、输入数据限制在相对较小的范围内(所有整数都小于10^7);2、数据没有重复;3、对于每条记录而言,除了单一整数外,没有任何其他关联数据。

        问题的解决:

        我们可以用位图或位向量表示整数集合。例如,我们可以用20为字符串来表示所有元素都小于20的简单的非负整数集合,如下字符串来表示集合{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个字符串来表示我们要排序的小于10^7的整数呢?当且仅当字符串中表示该位的整数存在时,该字符串位的值为1,否则值为0。这样,通过一次整数文件的输入,我们就确定了字符串,再根据字符串,我们就能一次输出升序的整数序列。

        通过位图,我们可以使原本只能表示一个整数的WORD(即32bit),能表示32个整数。而排序的时间复杂度为O(n)。达到了时间和空间的双赢。

        一些想法

         通过对问题的细致观察和理解,解题方法往往自然而然地浮出水面。遇到问题,切忌毛毛躁躁地打开编程软件,建立项目,然后像挤牙膏一样慢慢抠,最后不见得符合要求,只能是瞎忙活。多花几个小时理解问题,比拿着半截就开跑,最后苦思冥想不得其解要好得多。不紧要对问题有一个整体把握,还有注意其中的细节,抓出具体问题的特点。本题中关于排序,要是以前,我肯定毫不犹豫地去拿着什么归并,快排就开跑,到最后什么都做不出来。发散思维也很重要,排序不一定就非得书本上那几种排序方法才能解决。另外:程序能有多简单,就多简单,因为简单的程序通常比具有相同功能的浮渣程序更可靠、更安全、更健壮、更高效,而且更易于实现和维护。

         

        下面实现的主要代码:

         头文件:h1.h

#ifndef H1_H
#define H1_H

#include<string.h>
#include<ctype.h>
#include<malloc.h> /* malloc()等 */
#include<limits.h> /* INT_MAX等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include<stdlib.h> /* atoi() */
#include<io.h> /* eof() */
#include<math.h> /* floor(),ceil(),abs() */
#include<process.h> /* exit() */
#include<time.h>

#define MAXSIZE 100
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
/* #define OVERFLOW -2 因为在math.h中已定义OVERFLOW的值为3,故去掉此行 */
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */


#define BITSPERWORD 32
#define SHIFT 5
#define N 10000000
#define MASK 0x1f

#endif

bitMapSort.cpp

#include "h1.h"

void set(int *bitMap, int i){

	bitMap[i>>SHIFT] |= (1<<(i & MASK));
}

void clr(int *bitMap, int i){

	bitMap[i>>SHIFT] &= ~(1<<(i & MASK));
}

int test(int *bitMap, int i){
	
	return bitMap[i>>SHIFT] & (1<<(i & MASK));
}

int randInt(int m, int n){ //generate random digits between m to n.
	
	return ((rand()%(n-m))+m);
}

void swapDigit(int *a, int m, int n){

	int temp;

	temp = a[m];
	a[m] = a[n];
	a[n] = temp;
}

void generateArray(int *a, int m, int n){ //Generate m random digits and  store in the pre-m cells.

	int i;
	int k = 0;

	for(i=0; i<n; i++){
		a[i] = i;
	}

	for(i=0; i<m; i++){
		swapDigit(a, i, randInt(i, n-1));
	}
}


int main(){

	int *a = (int*)malloc(sizeof(int)*N);
	int *bitMap = (int*)malloc(sizeof(int)*(1+N/BITSPERWORD));
	int i;
	int m = 20;//generate 20 random digits for testing.

	generateArray(a, m, N);

	for(i=0; i<m; i++){
		printf("%d ", a[i]);
	}
	printf("\n");

	for(i=0; i<N; i++){
		clr(bitMap, i);
	}

	for(i=0; i<m; i++){
		set(bitMap, a[i]);
	}

	for(i=0; i<N; i++){
		if(test(bitMap, i)){
			printf("%d ", i);
		}
	}
	free(a);
	free(bitMap);

	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值