C++ Primer Plus章节编程练习第十六章

1

题目

  回文指的是顺读和逆读都一样的字符串。例如,“tot” 和“otto" 都是简短的回文。编写一个程序,让用户输入字符串,并将字符串引用传递给一个bool函数。如果字符串是回文,该函数将返回true,否则返回false。此时,不要担心诸如大小写、空格和标点符号这些复杂的问题。即这个简单的版本将拒绝“Otto."和“Madam, I'm Adam"。请查看附录F中的字符串方法列表,以简化这项任务。

题解

#include<iostream>
#include<string>
using namespace std;
bool isPalindrome(const string& s);
int main(){
    cout<<"Please enter a string for check if it is palindrome <quit to quit>: ";
    string str;
    while(cin>>str&&str!="quit"){
        puts(isPalindrome(str)?"Yes!":"NO!");
        cout<<"Please enter next string for check <quit to quit>: ";
    }
    cout<<"Bye!"<<endl;
    return 0;
}
bool isPalindrome(const string& s){
    int low=0,high=s.size()-1;
    while(low<high){
        if(s[low]!=s[high])
            return false;
        low++,high--;
    }
    return true;
}

2

题目

  与编程练习1中给出的问题相同,但要考忠诸如大小写、空格和标点符号这样的复杂问题。即“Madam, 'm Adam"将作为回文来测试。例如,测试函数可能会将字符串缩略为“madamimadam", 然后测试倒过来是否一样。不要忘了有用的cctype库,您可能从中找到几个有用的STL函数,尽管不一定非要使用它们。

题解

#include<iostream>
#include<string>
#include<cctype>
using namespace std;
void stringFilter(string &s);
bool isPalindrome(const string& s);
int main(){
    cout<<"Please enter a string for check if it is palindrome <quit to quit>: ";
    string str;
    while(getline(cin,str)&&str!="quit"){
        stringFilter(str);
        puts(isPalindrome(str)?"Yes!":"NO!");
        cout<<"Please enter next string for check <quit to quit>: ";
    }
    cout<<"Bye!"<<endl;
    return 0;
}
void stringFilter(string &s){       //过滤字符串
    int index=0;
    for(auto ch:s)
        if(isalpha(ch))
            s[index++]=tolower(ch);   
    s=s.substr(0,index);
}
bool isPalindrome(const string& s){
    int low=0,high=s.size()-1;
    while(low<high){
        if(s[low]!=s[high])
            return false;
        low++,high--;
    }
    return true;
}

3

题目

  修改程序清单16.3, 使之从文件中读取单词。一种方案是,使用vector 对象而不是string数组。这样便可以使用push_back()将数据文件中的单词复制到vector对象中,并使用size()来确定单词列表的长度。由于程序应该每次从文件中读取一个单词,因此应使用运算符>>而不是getline()。文件中包含的单词应该用空格、制表符或换行符分隔。

题解

#include<iostream>
#include<fstream>
#include<vector>
#include<string>
#include<cstdlib>
#include<ctime>
#include<cctype>
using namespace std;
int main(){
    fstream input("3.txt");     //改为从文件中读取构建wordlist
    string word;
    vector<string> wordlist;
    while(input>>word) wordlist.push_back(word);
    int NUM=wordlist.size();
    srand(time(0));
    char play;
    cout<<"Will yor play a word game? <y/n> ";
    cin>>play;
    play=tolower(play);
    while(play=='y'){
        string target = wordlist[rand()%NUM];
        int length=target.length();
        string attempt(length,'-');                         //玩家猜测的占位符
        string badchars;
        int guesses=6;
        //输出提示信息,包括随机的单词长度和玩家允许错误的次数
        cout<<"Guess my secret word. It has "<<length<<" letters, and you guess\n"
            <<"one letter at a time. You get "<<guesses<<" wrong guess.\n";
        cout<<"Your word: "<<attempt<<endl;
        //进行猜测
        while(guesses>0&&attempt!=target){
            char letter;
            cout<<"Guess a letter: ";
            //如何是已经猜测过的则输出提示信息
            cin>>letter;
            if(badchars.find(letter)!=string::npos||attempt.find(letter)!=string::npos){
                cout<<"You already guessed that. Try again.\n";
                continue;
            }
            int loc=target.find(letter);
            if(loc==string::npos){                  //如果不是目标答案中的字符,则保存为错误字符
                cout<<"Oh, bad guess!\n";
                guesses--;
                badchars+=letter;
            }
            else{                                   //如果猜中了,则讲临时占位符中的该字符全部替换为正确的字符
                cout<<"Good guess!\n";
                attempt[loc]=letter;
                loc=target.find(letter,loc+1);
                while(loc!=string::npos){
                    attempt[loc]=letter;
                    loc=target.find(letter,loc+1);
                }
            }
            cout<<"Your word: "<<attempt<<endl;
            if(attempt!=target){                   //输出错误字符
                if(badchars.length()>0)
                    cout<<"Bad choices: "<<badchars<<endl;
                cout<<guesses<<" bad guesses left\n";
            }
        }
        if(guesses>0)                              //输出最终的猜测结果
            cout<<"That's right!\n";
        else
            cout<<"Sorry the word is "<<target<<".\n";
        cout<<"Will you play anoter? <y/n> ";
        cin>>play;
        play=tolower(play);
    }
    input.close();
    cout<<"Bye\n";
    return 0;
}

4

题目

  编写一个具有老式风格接口的函数,其原型如下:

int reduce(long ar[],int n);

  实参应是数组名和数组中的元素个数。该函数对数组进行排序,删除重复的值,返回缩减后数组中的元素数目。请使用STL函数编写该函数(如果决定使用通用的unique()函数,请注意它将返回结果区间的结尾)。使用一个小程序测试该函数。

题解

#include<bits/stdc++.h>
using namespace std;
int reduce(long ar[],int n);
int main(){
    long ar[]={19,9,9,4,20,4,5,6,8,3,11,22,11,0,43,0};
    int size=16;
    int num=reduce(ar,size);
    for(int i=0;i<num;i++){
        if(i) putchar(' ');
        cout<<ar[i];
    }
    return 0;
}
int reduce(long ar[],int n){
    int index=0;
    sort(ar,ar+n);
    for(int i=1;i<n;i++){
        if(ar[i]!=ar[index]){
            index++;
            ar[index]=ar[i];
        }
    }
    return index+1;
}

5

题目

  问题与编程练习4相同,但要编写一个模板函数:

template <class T>
int reduce(T ar[], int n);

  在一个使用long实例和string实例的小程序中测试该函数。

题解

#include<bits/stdc++.h>
using namespace std;
template<class T>
int reduce(T ar[],int n);
int main(){
    long ar[]={19,9,9,4,20,4,5,6,8,3,11,22,11,0,43,0};
    string words[]={"tom","cat","tim","tom","dog"};
    int size=16;
    int num=reduce(ar,size);
    for(int i=0;i<num;i++){
        if(i) putchar(' ');
        cout<<ar[i];
    }
    cout<<endl;
    num=reduce(words,5);
    for(int i=0;i<num;i++){
        if(i) putchar(' ');
        cout<<words[i];
    }
    cout<<endl;
    return 0;
}
template<class T>
int reduce(T ar[],int n){
    int index=0;
    sort(ar,ar+n);
    for(int i=1;i<n;i++){
        if(ar[i]!=ar[index]){
            index++;
            ar[index]=ar[i];
        }
    }
    return index+1;
}

6

题目

  使用STL queue模板类而不是第12章的Queue类,重新编写程序清单12.12所示的示例。

题解

#include<iostream>
#include<cstdlib>
#include<ctime>
#include<queue>
using namespace std;
//******************************Customer定义******************************
class Customer{
private:
    long arrive;
    int processtime;
public:
    Customer(){arrive=processtime=0;}
    void set(long when);                //设置顾客的相关时间
    long when()const{return arrive;}    //读取到达时间
    int ptime()const{return processtime;}   //读取服务处理时间
};
//******************************Customer实现******************************
void Customer::set(long when){          //设置到达时间,服务处理时间为随机生成
    processtime=rand()%3+1;
    arrive=when;
}

//********************************Queue定义*******************************
typedef Customer Item;
const int MIN_PER_HR=60;
bool newcustomer(double x);         //判断是否为新的顾客
int main(){
    srand(time(0));                 //初始化随机数
    cout<<"Case Studay: Bank of Heather Automatic Teller\n";
    cout<<"Enter maximum size of queue: ";
    int qs;
    cin>>qs;
    queue<Item> line;               //队列
    cout<<"Enter the number of simulation hours: ";
    int hours;
    cin>>hours;                     //模拟时长
    long cyclelimmit=MIN_PER_HR*hours;
    cout<<"Enter the average number of customers per hour: ";
    double perhour;                 //每小时顾客人数
    cin>>perhour;
    double min_per_cust;            //每个顾客最短的花费
    min_per_cust=MIN_PER_HR/perhour;
    Item temp;                      //存储一个用户信息
    long turnaways=0;               //表示离开的人数
    long customers=0;               //业务人数数量
    long served=0;                  //被服务的人数
    long sum_line=0;                //计算队长
    long wait_time=0;               //当前到下一个人的等待时间
    long line_wait=0;               //队伍等待时间
    //开始模拟
    for(int cycle=0;cycle<cyclelimmit;cycle++){
        if(newcustomer(min_per_cust)){ //如果有新顾客
            if(line.size()>=qs)
                turnaways++;            //因为队伍过长离开的人数累加
            else{
                customers++;            //业务人数计数
                temp.set(cycle);        //初始化信息并入队
                line.push(temp);
            }
        }
        //如果不需要等待且队伍非空,则出队
        if(wait_time<=0&&!line.empty()){
            temp=line.front();
            line.pop();
            wait_time=temp.ptime();//更新到下个人所需的等待时间
            line_wait+=cycle-temp.when();
            served++;               //被服务的人数计数
        }
        if(wait_time>0)             //如果等待时间大于0,则随着cycle自动递减
            wait_time--;
        sum_line+=line.size();//累加每一时刻的队长
    }

    //输出结果
    if(customers>0){
        cout<<"customers accepted: "<<customers<<endl;
        cout<<"  customers served: "<<served<<endl;
        cout<<"         turnaways: "<<turnaways<<endl;
        cout<<"average queue size: ";
        cout.precision(2);
        cout.setf(ios_base::fixed,ios_base::floatfield);
        cout<<(double)sum_line/cyclelimmit<<endl;
        cout<<" average wait time: "
            <<(double)line_wait/served<<" minutes\n";
    }
    else
        cout<<"No customers!\n";
    cout<<"Done!\n";
    return 0;
}
bool newcustomer(double x){
    return rand()*x/RAND_MAX<1;
}

7

题目

  彩票卡是一个常见的游戏。卡片上是带编号的圆点,其中一些圆点被随机选中。编写一个lotto()函数,它接受两个参数。第一个参数是彩票卡上圆点的个数,第二个参数是随机选择的圆点个数。该函数返回一个vector 对象,其中包含(按排列后的顺序)随机选择的号码。例如,可以这样使用该函数:

vector<int> winners;
winners = Lotto(51,6);

  这样将把一个矢量赋给winner,该矢量包含1~51中随机选定的6个数字。注意,仅仅使用rand()无法完成这项任务,因它会生成重复的值。提示: 让函数创建一个包含所有可能值的矢量,使用random_shuffle(),然后通过打乱后的矢量的第一个值来获取值。编写一个小程序来测试这个函数。

题解

#include<bits/stdc++.h>
using namespace std;
vector<int> Lotto(int t,int c);
int main(){
    int total,choice;
    cout<<"Enter the total number: ";
    cin>>total;
    cout<<"Enter the number of choice (no more than "<<total<<"): ";
    cin>>choice;
    vector<int> winners=Lotto(total,choice);
    for(int i=0;i<winners.size();i++){
        if(i) putchar(' ');
        cout<<winners[i];
    }
    cout<<"Bye!\n";
    return 0;
}
vector<int> Lotto(int t,int c){
    srand(time(0));
    vector<int> result;
    for(int i=0;i<c;i++)
        result.push_back(rand()%t+1);
    random_shuffle(result.begin(),result.end());
    return result;
}

8

题目

  Mat和Pat希望邀请他们的朋友来参加派对。他们要编写一个程序完成下面的任务。
  ● 让Mat输入他朋友的姓名列表。姓名存储在一个容器中,然后按排列后的顺序显示出来。
  ● 让Pat输入她朋友的姓名列表。姓名存储在另一个容器中,然后按排列后的顺序显示出来。
  ● 创建第三个容器,将两个列表合并,删除重复的部分,并显示这个容器的内容。

题解

#include<set>
#include<string>
#include<iostream>
using namespace std;
void getName(set<string> &s,string str){
    cout<<str<<", Please enter the name of your friends <quit with quit>: ";
    string name;
    while(getline(cin,name)&&name!="quit"){
        s.insert(name);
        cout<<str<<", Please enter next name of your friends <quit with quit>: ";
    }
}
int main(){
    set<string> MatFriend,PatFriend;
    string name;
    getName(MatFriend,"Mat");
    getName(PatFriend,"Pat");
    set<string> finallist(MatFriend);
    finallist.insert(PatFriend.begin(),PatFriend.end());
    cout<<"All friend of you as following: "<<endl;
    for(auto &name: finallist)
        cout<<name<<" ";
    return 0;
}

9

题目

  相对于数组,在链表中添加和删除元素更容易,但排序速度更慢。这就引出了一种可能性:相对于使用链表算法进行排序,将链表复制到数组中,对数组进行排序,再将排序后的结果复制到链表中的速度可能更快;但这也可能占用更多的内存。请使用如下方法检验上述假设。
  a. 创建大型vector对象vi0,并使用rand()给它提供初始值。
  b. 创建vector对象vi和list对象h,它们的长度都和初始值与vi0相同。
  c. 计算使用STL算法sort()对vi进行排序所需的时间,再计算使用list的方法sort()对li进行排序所需的时间。
  d. 将li重置为排序的vi0的内容,并计算执行如下操作所需的时间:将li的内容复制到vi中,对vi进行排序,并将结果复制到li中。
  要计算这些操作所需的时间,可使用ctime库中的clock()。正如程序清单5.14演示的,可使用下面的语句来获取开始时间:

clock_t start=clock();

  再在操作结束后使用下面的语句获取经过了多长时间:

clock_t end=clock();
cout<<(double)(end-start)/CLOCKS_PRE_SEC;

  这种测试并非绝对可靠,因为结果取决于很多因素,如可用内存量、是否支持多处理以及数组(列表)的长度(随着要排序的元素数增加,数组相对于列表的效率将更明显)。另外,如果编译器提供了默认生成方式和发布生成方式,请使用发布生成方式。鉴于当今计算机的速度非常快,要获得有意义的结果,可能需要使用尽可能大的数组。例如,可尝试包含100000、1000000和10000000个元素。

题解

#include<bits/stdc++.h>
using namespace std;
void getTime(int size){
    const int MAXSIZE=size;
    vector<int> vi0(MAXSIZE);
    srand(time(0));
    for(auto& item:vi0) item=rand();
    vector<int> vi(vi0);
    list<int> li(vi0.begin(),vi0.end());
    clock_t start,end;

    start=clock();
    sort(vi.begin(),vi.end());
    end=clock();
    cout<<"Algorithm sort() with vi costs time: "<<(double)(end-start)/CLOCKS_PER_SEC<<endl;

    start=clock();
    li.sort();
    end=clock();
    cout<<"sort() of list costs time: "<<(double)(end-start)/CLOCKS_PER_SEC<<endl;    

    start=clock();
    li.assign(vi0.begin(),vi0.end());
    int index=0;
    for(auto item:li)               //把li复制到vi中
        vi[index++]=item;
    sort(vi.begin(),vi.end());      //vi排序
    index=0;
    for(auto& item:li)               //把li复制到vi中
        item=vi[index++];
    end=clock();
    cout<<"Copy li to vi and use sort() of algorithm costs time: "<<(double)(end-start)/CLOCKS_PER_SEC<<endl;
}
int main(){
    int numbers[]={100000,1000000,10000000};
    for(int i=0;i<3;i++){
        cout<<"When number is "<<numbers[i]<<endl;
        getTime(numbers[i]);
        cout<<endl;
    }
    return 0;
}

10

题目

  请按如下方式修改程序清单16.9 (vect3.cpp)。
  a.在结构Review中添加成员price。
  b.不使用vector来 存储输入,而使用vector<shared_ptr>。别忘了,必须使用new返回的指针来初始化shared_ptr。
  c.在输入阶段结束后,使用一个循环让用户选择如下方式之一显示书籍:按原始顺序显示、按字母表顺序显示、按评级升序显示、按评级降序显示、按价格升序显示、按价格降序显示、退出。
  下面是一种可能的解决方案:获取输入后,再创建一个shared_ptr矢量,并用原始数组初始化它。定义一个对指向结构的指针进行比较的operator<()函数,并使用它对第二个矢量进行排序,让其中的shared_ptr按其指向的对象中的书名排序。重复上述过程,创建按rating和price排序的shared_ptr矢量。请注意,通过使用rbegin()和rend(),可避免创建按相反的顺序排列的shared_ptr矢量。

题解

#include<iostream>
#include<string>
#include<vector>
#include<memory>
#include<algorithm>
using namespace std;
struct Review{
    std::string title;
    int rating;
    double price;
};
bool operator<(shared_ptr<Review>& r1,shared_ptr<Review>& r2);
bool sortByRating(shared_ptr<Review>& r1,shared_ptr<Review>& r2);
bool sortByPrice(shared_ptr<Review>& r1,shared_ptr<Review>& r2);
shared_ptr<Review> FillReview();
void ShowReview(shared_ptr<Review>& rr);
int main(){
    vector<shared_ptr<Review>> books;
    shared_ptr<Review> temp;
    while(temp=FillReview())
        books.push_back(temp);
    if(books.size()==0) cout<<"No entries.";
    else{
        cout<<"Please enter the rule of sort <5 for quit>:"<<endl
            <<"1) Default   2) Title"<<endl
            <<"3) Rating    4) Price"<<endl
            <<"5) Quit"<<endl;
        int choice=-1;
        while(cin>>choice&&choice!=5){
            vector<shared_ptr<Review>> ttemp(books);
            switch(choice){
                case 1: cout<<"Origin order:\nBook\tRating\tPrice\n";
                        break;
                case 2: cout<<"Sorted by title:\nBook\tRating\tPrice\n";
                        sort(ttemp.begin(),ttemp.end());break;
                case 3: cout<<"Sorted by rating:\nBook\tRating\tPrice\n";
                        sort(ttemp.begin(),ttemp.end(),sortByRating);break;
                case 4: cout<<"Sorted by price:\nBook\tRating\tPrice\n";
                        sort(ttemp.begin(),ttemp.end(),sortByPrice);break;
            }
            for_each(ttemp.begin(),ttemp.end(),ShowReview);
            cout<<"Please enter the rule of sort <5 for quit>:"<<endl
                <<"1) Default   2) Title"<<endl
                <<"3) Rating    4) Price"<<endl
                <<"5) Quit"<<endl;
        }
    }
    cout<<"Bye.\n";
    return 0;
}
bool operator<(shared_ptr<Review>& r1,shared_ptr<Review>& r2){
    return r1->title<r2->title;
}
bool sortByRating(shared_ptr<Review>& r1,shared_ptr<Review>& r2){
    return r1->rating<r2->rating;
}
bool sortByPrice(shared_ptr<Review>& r1,shared_ptr<Review>& r2){
    return r1->rating<r2->price;
}
shared_ptr<Review> FillReview(){
    std::cout<<"Enter book title (quit to quit): ";
    shared_ptr<Review> onebook(new Review);
    std::getline(std::cin,onebook->title);
    if(onebook->title=="quit")
        return nullptr;

    std::cout<<"Enter the book rating: ";
    std::cin>>onebook->rating;
    if(!std::cin)                               //如果没有读到符合要求的数据则返回false
        return nullptr;

    std::cout<<"Enter the book price: ";
    std::cin>>onebook->price;
    if(!std::cin)
        return nullptr;
    while(std::cin.get()!='\n')                 //吸收多余字符
        continue;
    return onebook;
}
void ShowReview(shared_ptr<Review>& rr){
    std::cout<<rr->title<<"\t"<<rr->rating<<"\t"<<rr->price<<std::endl;
}

当珍惜每一片时光~