《编程珠玑》 读书笔记

教训1:Careful analysis of a small problem can sometimes yield tremendous practical benefits!!!

====================================================================

问题:
输入,输入为一个文件,至多包含n个正整数,每个正整数都小于n,这里n=10 的7次方(最多用40MB主存空间)。
如果输入时某个整数出现了两次,就会产生致命错误。
输出:以增序输出经过排序的整数列表。
约束:只有1MB的可用主存(代码除外);但磁盘空间非常充足。

解决方法:分40次读入输入文件,第一次读入0-249999之间的数(最多占1MB),排序后输出到文件中,
第二次读入250000-499999之间的数,排序后输出到文件,第40次读入排序9750000-9999999之间的整数
并输出到文件,至此所有数据都已经排好序。这个方法确实很巧妙,但我想说的是排序方法,书中指出
用快速排序很快,但我想在没有多余空间的情况下是否能用快速排序呢?并且在目前这种情况下,快速排序
未必是最快的方法。基于排序对象是整数而且无重复数据的这一事实,我们可以设一个250000个元素的整数数组,第一次
输入(0-249999)可以各就各位,即0存入元素0,249999存入元素249999,一遍即可排好序,而且也无须
额外的存储空间,第二次(250000-499999)把每个待排序的数减去250000,一样可以与数组一一对应,第三次则减去500000,
以后以此类推。

 

思路1:1M内存大约可以存储250000个整数。因此我们可以对输入文件遍历40次,第一次遍历过程中将值大于0、小于249999的所有整数读入内存,然后进行快速排序,再写入目标文件;第二次遍历过程中将值大于250000、小于499999的所有整数读入内存,排完序后写入目标文件,后面依此类推;第40次遍历过程中将大于9750000、小于9999999的所有整数读入内存,排完序后写入目标文件。此时目标文件中的整数已按增量排完序,顺序输出即可。

思路2:系统排序
(Command命令行)>sort originalFileName.ext >> targetFileName.ext

思路3:位图数据结构。

#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));}
int  test( int  i) ... { return  a[i >> shift]  &  ( 1 << (i  &  mask));}

 

 


如果不限内存,还可以使用下面两种排序方法:
思路1:C++/STL

int  mail( void )
... {
    
set < int >  s;
    
int  i;
    
set < int > ::iterator j;
    
while (cin >> i) 
        s.insert(i);
    
for (j = s.begin();j != s.end(); ++ j)
        cout
<<* j << " " ;  
    
return   0 ;
}

 

思路2:C/qsort

int  intcomp( int   * x, int   * y)
... {
    
return   * x -* y;
}

int  a[ 10000000 ];
int  main( void )
... {
   
int  i,n = 0 ;
   
while (scanf( " %d " , & a[n]) != EOF)
       n
++ ;
   qsort(a,n,
sizeof ( int ),intcomp);
   
for (i = 0 ;i < n;i ++ )
       printf(
" %d " " ,a[i]);
}

 

-----------------------------------------------------------------------------------------------------------------------------------------------

在我们写程序的的时候,有时候我们经常需要存储许多的二值的信息量,如:一件事情有没有发生、很多灯的亮灭的状态、网络传输中判断重复数据包等。这时我们就需用用一个信息单元来存储1、0 信息来表示有或无。那我们该怎么表示呢?最简单的方法就是使用数组了

char buf [ 256 ];

这个数组中就可以存储256个信息量的状态,每个数组单位中存储一个0或1(buf[0] = 1, buf[1] = 0;)。这里我们有一种更好的方法就是用C语言中的位操作来实现这个需求。使用char类型的8位表示8个信息量,使用每一位01表示不同。这样我们用char buf[32]就可以存储256个二值信息量,大大节约了空间。位操作不能像数组操作那样直接简单,所以我们封装了几组函数如下:

#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 ) ); }
int  test ( int i ) { return a [i >>SHIFT ] &   ( 1 << (i & MASK ) ); }


上面是我一个程序中的例子。这里我们使用的是int类型表示32个信息量。
a[i>>SHIFT]来确定设置a数组的哪一个元素,(1<<(i & MASK))用来确定设置一个a数组元素中的某一位!
我们的需求是判断0 ~ 10000000 之中的数字是否出现过,而set(1)、clr(1)、test(1)函数就可以将表示数字一是否存在的哪一位置为1、清零、判断是否为1。这样我们就可以通过这几个函数来实现位的操作。

 

源代码:

/* Copyright (C) 1999 Lucent Technologies */
/* From 'Programming Pearls' by Jon Bentley */

/* bitsort.c -- bitmap sort from Column 1
*   Sort distinct integers in the range [0..N-1]
*/

#include
<stdio.h>

#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)); }
int  test(int i){ return a[i>>SHIFT] &   (1<<(i & MASK)); }
int main()
{   
int i;
   
for (i = 0; i < N; i++)
        clr(i);


    /*Replace above 2 lines with below 3 for word-parallel init
    int top = 1 + N/BITSPERWORD;
    for (i = 0; i < top; i++)
        a[i] = 0;*/


   
while (scanf("%d", &i) != EOF)
       
set(i);
   
for (i = 0; i < N; i++)
       
if (test(i))
            printf(
"%d/n", i);
   
return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值