• 编程珠玑第一章习题


    第一章习题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程序的一半

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

  • 相关阅读:
    Combine 框架,从0到1 —— 4.在 Combine 中使用计时器
    Combine 框架,从0到1 —— 4.在 Combine 中使用通知
    Combine 框架,从0到1 —— 3.使用 Subscriber 控制发布速度
    Combine 框架,从0到1 —— 2.通过 ConnectablePublisher 控制何时发布
    使用 Swift Package Manager 集成依赖库
    iOS 高效灵活地配置可复用视图组件的主题
    构建个人博客网站(基于Python Flask)
    Swift dynamic关键字
    Swift @objcMembers
    仅用递归函数操作逆序一个栈(Swift 4)
  • 原文地址:https://www.cnblogs.com/juranus1412/p/12590235.html
Copyright © 2020-2023  润新知