(Boolan)STL與泛型編程作業一

題目:一個整數集 , 該整數集可以有重復元素, 初始為空集合 。可以指定如下指令:

  1. add x 將x加入整數集
  2. del x 刪除整數集中所有與x相等的元素
  3. ask x 對整數集中元素x的情況詢問
    下面為指令解釋,并要求進行如下輸出:
  4. add 輸出操作后集合中x的個數
  5. del 輸出操作前集合中x的個數
  6. ask 先輸出0或1表示x是否曾被加入集合(0表示不曾加入),再輸出當前集合中x的個數,中間用空格格開。
    提示
    請使用STL中的 set 和multiset 來完成該題

輸入
第一行是一個整數n,表示命令數。0<=n<=100000。
后面n行命令。
輸出
共n行,每行按要求輸出。

樣例輸入( //第一行是整數,表示為命令數目)
7
add 1
add 1
ask 1
ask 2
del 2
del 1
ask 1
樣例輸出
1
2
1 2
0 0
0
2
1 0

對于該題目我提供了兩個方法來解決問題

方法一:

說明:
  • 該程序是通過一組函數直接實現題目要求的結果
  • 為了保證結果能一次性輸出,在程序中采用了queue來暫存操作的輸出結果,最后統一輸出
  • 為了方便您批改作業,我特地制作了在線測試的程序,程序的在線測試地址:http://rextester.com/VYZSW2883
    • 在線調試幫助:
      • 如何執行

點擊鏈接進入頁面,點擊按鈕



即可執行程序了
- 執行不正確,沒有輸出(如何輸入)

如果輸出不正常,或者沒有輸入輸出,或者不知道如何輸入,可以按下圖執行
:方法一代碼
#include <set>
#include <queue>
#include <iostream>
#include <string>
#include <algorithm>
#include <sstream>

using namespace std;

void controller(multiset<int> &s, queue<string> &rs, set<int> &record); //通過該函數協調流程
int add(multiset<int> &s, queue<string> &rs, set<int> &record, const int &content); //添加數據
int del(multiset<int> &s, queue<string> &rs, set<int> &record, const int &content); //刪除數據
int ask(queue<string> &s, multiset<int> &rs, set<int> &record, const int &content, int type = 0);   // 查詢數據,type用于區分是否為查詢
template<class T>
void printMultiset(queue<T>);   //打印輸出結果

int main()
{
    int count;
    multiset<int> s = {};   //創建一個空的容器,用于存放記錄
    queue<string> resultSet;    //創建一個空隊列,用于存放輸出記錄
    set<int> record = {};       //創建一個空set,用于存放保存記錄
    //cout << "請輸入需要操作的步數: \n";
    cin >> count;
    if (count <= 0) count = 1;
    //cout << "還需要輸入 " << count << "步操作\n";

    while (count)   //依據輸入,循環執行,通過controller調度順序
    {
        //cout << "還需要輸入 " << count << "步操作\n";
        
        controller(s, resultSet,record);
        count--;
    }

    cout << "\n\n*****************************************\n\n";
    printMultiset(resultSet);   //打印日志

    return 0;
}

void controller(multiset<int> &s, queue<string> &resultSet, set<int> &record)
{
    string cmd;     //命令
    int content;    //內容
    cin >> cmd >> content;
    if ("add" == cmd) {
        add(s, resultSet, record, content);
    }
    else if("del" == cmd)
    {
        del(s, resultSet, record, content);
    }
    else if("ask" == cmd)
    {
        ask(resultSet, s, record, content, 1);//由controller調用
    }
    else 
    {
        cout << "輸入錯誤,請重新輸入\n";
    }
}

int add(multiset<int> &s, queue<string> &rs, set<int> &record, const int &content)
{
    s.insert(content);//在集合中加入元素
    ask(rs, s, record, content);//輸出操作后集合中x的個數
    record.insert(content);
    return 0;
}

int ask(queue<string> &rs, multiset<int> &target, set<int> &record, const int &content, int type)//type用于區分是否為存入雙參數
{
    int count = target.count(content);
    int history = count ? count: record.count(content);//如果count為0,檢測該元素是否被刪除
    stringstream result;
    result << count;
    string s, scount;
    result >> scount;   //通過stringstream來構造字符串
    if (type) 
    {   //需要存入雙參數
        const string a = "1 ", b = "0 ";
        s = (history ? a : b) + scount;
    }
    else 
    {   //存入查詢結果即可
        s = scount;
    }

    rs.push(s);//ask的結果保存
    
    return count;
}

int del(multiset<int> &s, queue<string> &rs, set<int> &record, const int &content)
{
    //刪除前需要查詢情況
    ask(rs, s, record, content);
    s.erase(content);//刪除元素
    return 0;
}

template<class T>
void printMultiset(queue<T> s)
{
    while (!s.empty()) 
    {   //打印日志
        cout << s.front() << endl;
        s.pop();//彈出打印完的日志  
    }
}

方法二:

說明:
  • 這個方法主要是采用了面向對象和泛型編程的思路來實現題目的要求

  • 這個方法,分別有三個單例的class,來封裝了multiset、set、queue三個容器,分別來實現保存數據,保存插入記錄和輸出信息的功能

  • 封裝后的最大優勢就是,使用者極其簡單可以使用封裝的過程


    分裝后如何使用
  • 為了方便您批改作業,我特地制作了在線測試的程序,程序的在線測試地址:http://rextester.com/SRG5586

    • 在線調試幫助:
      • 如何執行

點擊鏈接進入頁面,點擊按鈕



即可執行程序了
- 執行不正確,沒有輸出(如何輸入)

如果輸出不正常,或者沒有輸入輸出,或者不知道如何輸入,可以按下圖執行
#ifndef __SET_TEST__HEADER__
#define __SET_TEST__HEADER__

#include <set>
#include <string>
#include <queue>
#include <iostream>
#include <sstream>

using namespace std;

template<typename T>
class Multiset;     //包裝了multiset,用于存放數據
template<typename T>
class Recorder;     //記錄器,存放記錄不會重復,所以包裝了set,如果存入了某個數據,在此記錄,如果修改了原始數據,此處還有記錄,說明該數據被添加過
class ControllerLog;//日志器,包裝了queue,用于存放輸出信息

template<typename T>
class Multiset  //multiset的包裝類
{
private:
    multiset<T>* s; //實際的容器的指針,用于存放數據
    static Multiset<T>* ms; //單例模式自己的指針
    Recorder<T>* recorder;  //記錄器指針,將添加的數據放入在此
    ControllerLog* log;     //用于控制日志的存儲,以便一次輸出
    Multiset();//單例
public:
    void add(const T&); //添加的方法
    void del(const T&); //刪除的方法
    void ask(const T&); //查詢的方法
    static Multiset<T>* getInstance();  //單例模式
    ~Multiset();//析構函數流程
    string geneLog(const T& content, int type = 0);//生成日志,type用于控制不同的日至需求(查詢的日志和其他不同)
    void controller();//控制器,用于控制
    void run();//對外的方法,直接調用該方法即可
};

template<typename T>
class Recorder  //記錄器,set的包裝類
{
private:
    static Recorder<T>* recorder;//單例
    set<T>* s;  //被包裝的set
    Recorder(); //單例
public:
    static Recorder<T>* getInstance();//單例
    void addRecord(const T&);//添加一條記錄
    int count(const T&) const;//統計數量
    ~Recorder();//析構函數
};

class ControllerLog //日志器,queue的包裝類
{
private:
    queue<string>* q; //被包裝對象的指針
    static ControllerLog* cl;//單例
    ControllerLog();//單例
public:
    static ControllerLog* getInstance();//單例
    void addLog(const string &log);//添加一條日至
    void print();//輸出所有的日志
    ~ControllerLog();//析構函數
};

template<typename T>
Multiset<T>* Multiset<T>::ms = NULL;//靜態成員,需要賦值

template<typename T>
Recorder<T>* Recorder<T>::recorder = NULL;

ControllerLog* ControllerLog::cl = NULL;

#endif // !__SET_TEST__HEADER__



template<typename T>
inline Multiset<T>::Multiset()
{   //構造對象時,構造指針指向的對象
    if (!s)
        s = new multiset<T>();
    if (!log)
        log = ControllerLog::getInstance();
    if (!recorder)
        recorder = Recorder<T>::getInstance();
}

template<typename T>
inline Multiset<T>* Multiset<T>::getInstance()
{   //單例(餓漢模式)
    if (!ms)
        ms = new Multiset<T>();
    return ms;
}

template<typename T>
inline string Multiset<T>::geneLog(const T& content, int type)
{   
    int count = s->count(content);  //查詢數據表格中的數量
    int history = count ? count : recorder->count(content); //如果查詢到的count為0,則在Recorder中查詢是否存入過
    stringstream result;    //通過stringstream來將得到的int轉換為string
    result << count;
    string sresult, scount;
    result >> scount;
    if (type) {     //如果是查詢,則依此拼接字符串
        const string a = "1 ", b = "0 ";
        sresult = (history ? a : b) + scount;
    }
    else {      //非查詢,依此拼接字符串
        sresult = scount;
    }
    return sresult;
}

template<typename T>
inline void Multiset<T>::controller()
{
    //控制器接受控制臺輸入
    string cmd;
    T content;
    cout << "請依次輸入命令:\n";
    cin >> cmd >> content;
    //依據輸入執行不同的方法
    if ("add" == cmd) {
        add(content);
    }
    else if ("del" == cmd)
    {
        del(content);
    }
    else if ("ask" == cmd)
    {
        ask(content);
    }
    else
    {
        cout << "輸入錯誤,請重新輸入\n";
    }
}

template<typename T>
inline void Multiset<T>::run() {
    //對外方法,直接調用即可
    int count = 0;
    cout << "請輸入命令的條數: ";
    cin >> count;
    count = count >= 0 ? count : 7;
    string x;
    int content;
    //循環接收輸入
    while (count) {
        controller();
        count--;
    }
    //打印結果
    cout << "\n\n輸出:**************************************\n\n";
    log->print();
}

template<typename T>
inline Multiset<T>::~Multiset()
{   //析構函數釋放指針的內存
    if (s) delete s;
    if (log) delete log;
    if (recorder) delete recorder;
    if (ms) delete ms;//最后釋放自己
}

template<typename T>
inline void Multiset<T>::add(const T &content)
{   //添加到容器中
    s->insert(content);
    recorder->addRecord(content);//通過記錄器,添加一條記錄
    log->addLog(geneLog(content));//添加一條輸出日志
}

template<typename T>
inline void Multiset<T>::del(const T &content)
{   //通過日志器添加一條日志
    log->addLog(geneLog(content));
    s->erase(content);//數據容器中,清除內容
}

template<typename T>
inline void Multiset<T> ::ask(const T &content)
{
    //添加一條查詢記錄,以便輸出
    log->addLog(geneLog(content, 1));
}

template<typename T>
inline Recorder<T>::Recorder() {
    if (!s)s = new set<T>();//構造對象時,構造指針指向的對象
}

template<typename T>
inline Recorder<T>* Recorder<T>::getInstance()
{
    if (!recorder) recorder = new Recorder<T>();//單例(餓漢模式)
    return recorder;
}

template<typename T>
inline Recorder<T>::~Recorder()
{   // 釋放指針指向的對象
    if (s) delete s;
    if (recorder) delete recorder;//最后釋放自己
}

template<typename T>
inline void Recorder<T>::addRecord(const T& content)
{
    s->insert(content);//在記錄表中添加一個記錄
}

template<typename T>
inline int Recorder<T>::count(const T& content) const
{
    return s->count(content);//返回記錄表中的數目
}

inline void ControllerLog::addLog(const string &log)
{
    q->push(log);//加入一條日志
}

inline void ControllerLog::print() {
    while (!q->empty())
    {   //遍歷打印全部日至
        cout << q->front() << endl;
        q->pop();//將打印完的條目彈出隊列
    }
}

inline ControllerLog::ControllerLog() {
    if (!q) q = new queue<string>();    //單例(餓漢模式)
}

inline ControllerLog* ControllerLog::getInstance()
{
    if (!cl) cl = new ControllerLog();  //單例
    return cl;
}

inline ControllerLog::~ControllerLog()
{
    if (q) delete q;
    if (cl) delete cl;  //最后釋放自己
}


int main()
{
    Multiset<int>* s = Multiset<int>::getInstance();//單例模式獲取對象
    s->run();//調用方法執行

    system("pause");
    return 0;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,973評論 19 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,455評論 25 708
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,767評論 18 399
  • 高中的暑假總是那么少,只有短短的8天。有時候陳小凡會想到為什么自己沒有生在戰爭時期,那樣就可以不用上學了。但也就只...
    道滁閱讀 224評論 0 1