编程珠玑第一章习题

第一章习题1解析

以下代码均使用MSVC 6.0编译运行通过,为了便于学习,在原代码的基础上进行了一定的修改。

1.如何使用一个具有库的语言来实现一种排序算法以表示和排序集合,将代码实现可用

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 int comp(const void *x,const void *y){
 4     return (*(int*)x-*(int*)y);
 5 }
 6 int a[5]={45,25,64,10,4};
 7 void main(){
 8     int i,n=0;
 9     printf("排序前列表为:
");
10     for(i=0;i<5;i++){
11         printf("%d
",a[i]);
12     }        
13     qsort(a,5,sizeof(int),comp);
14     printf("排序后列表为:
");
15     for(i=0;i<5;i++){
16         printf("%d
",a[i]);    
17     }
18 
19 }

在使用原书中代码会报错:

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

error C2664: 'qsort' : cannot convert parameter 4 from 'int (int *,int *)' to 'int (__cdecl *)(const void *,const void *)'
None of the functions with this name in scope match the target type

意思是不能把int (int *,int *)自动转换为int (__cdecl *)(const void *,const void *),按提示错误,在声明函数时改写为上述函数正确写法即可。

 用C++标准模板库中的set容器完成相同的任务:

#include <iostream>
#include<set>
using namespace std;
int main(void){
    set<int> S;
    int i,n;
    cin>>n;
    for(i=0;i<n;i++){
        int x;
        cin>>x;
        S.insert(x);
    }
    set<int>::iterator j;//迭代器用于遍历    
    for(j=S.begin();j!=S.end();j++)
        cout<<*j<<"
";
     //s.end()没有值
    //s.begin()返回容器的第一个元素
     cout<<"s.begin()   "<<*S.begin ()<<endl;
    //lower_bound()返回第一个大于或等于给定关键值的元素
    cout<<"lower_buond 10 "<<*S.lower_bound (10)<<endl;    
    //upper_bound()返回第一个大于给定关键值的元素
    cout<<"upper_bound 10 "<<*S.upper_bound (10)<<endl;
    //find()查找一个元素,如果容器中不存在该元素,返回值等于s.end()
    cout<<"find() 10 "<<*S.find (10)<<endl;
   if(S.empty())
        cout<<"容器为空"<<endl;
    if(S.count(10))
        cout<<"10在容器中"<<endl;

   cout<<"s.size() "<<S.size ()<<endl; return 0; }

2和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));}//指定bit位设为1
void clr(int i){a[i>>SHIFT]&=~(1<<(i&MASK));}//指定bit位设为0
int test(int i){return a[i>>SHIFT]&(1<<(i&MASK));}//取存入的值,5bit一组

补充C++运算符知识:

<<二进制左移运算符。左操作数的值向左移动右操作数指定的位数。如A原十进制值为60,二进制值为00111100,A << 2 将得到 240,即为 1111 0000

相当于60*4,也就是A*2*2,右移相当于做乘法

>>二进制右移运算符。左操作数的值向右移动右操作数指定的位数。A >> 2 将得到 15,即为 0000 1111

相当于60/4,也就是A/2*2,左移相当于做除法

2的倍数的乘除法用位移实现

&如果同时存在于两个操作数中,二进制 AND 运算符复制一位到结果中。给定B为13,二进制值为00001101,则(A & B) 将得到 12,即为 0000 1100

那么i&0x1f,0x1f十进制值为31,二进制值00011111,当一个数和该数做&运算,如果余数在二进制位有值,则结果位是1,否则为1。

&得到的是32的余数。

void set(int i){a[i/32]|=(1<<(i%32));}
void clr(int i){a[i/32]&=~(1<<(i%32));}
int test(int i){a[i/32]&(i<<(i%32));}

用2的5次方也就是32的原因是,int型为4个字节,32位,a为 int型数组,那么除以32可以知道是a数组中哪一个下标,32取模得到的是int具体的某一位

我的理解是这样的:

 因此,a[i>>SHIFT]是第i位代表数组a的第几个int型数上  (1<<(i & MASK))是第i位在该int上的第几位上、

set将数组a中第几个int的数的第几个位(bit)赋值为1,clr赋值为0,test用于遍历。

整体代码思路是先将数组中的每个int数的bit位清零,然后循环输入数值,bit位赋值1,最后遍历输出。位图如示意图显示。

 修订可运行代码:

#include<stdio.h>
#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 128
int a[1+4];
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(void){
    int i;
    for(i=0;i<N;i++)
        clr(i);
    printf("请输入4个数");
    for(int j=0;j<4;j++)
    {
        scanf("%d",&i);
        set(i);
    }        
    for(i=0;i<N;i++)
        if(test(i))
            printf("%d
",i);
    return 0;
}
View Code

此题解参考了https://blog.csdn.net/xuzhengzheng32/article/details/15026637给出的方法并进行修正补充

 最后分析十分精妙:当n为10000000,且输入文件包含1000000个整数时,通用C++程序所需的内促成你和CPU时间是专用C程序的50倍,但是代码量仅有C程序的一半

该方法有个问题是,当有重复值出现时,结果只保存一次该值,也就是只输出一次该值

原文地址:https://www.cnblogs.com/juranus1412/p/12590235.html