DS 链表-学生宿舍管理 深大C++数据结构题目详解

前言

大家好,我是 JaYLove0589,今天给大家带来 C++语言,数据结构的一道题目详解。这次题当初难倒了我好长时间,花了我几天几晚时间,查阅各种资源,最后才大功告成,里面满满写着我的笔记和心得。

代码

#include 
#include 
using namespace std;

/// 单间寝室结点
struct SDormNode {
    string stuName;
    string dormNo;
};

/// 宿舍类,集成了可用宿舍和已用宿舍
class CDorm {
public:
    /// 类构造函数, 假设某校有20间宿舍,宿舍编号101,102,...,120。每间只住一名学生
    CDorm(int free_len_ = 0);
    ~CDorm();
    /// 初始宿舍状态
    void Initial();
    /// 为学生分配宿舍,从可用宿舍链表头摘取一间宿舍,
    /// 按宿舍号升序挂在已用宿舍链表中。
    /// @param name_ 学生姓名
//    void Assign(string name_);
    void Assign(const string& name_); //此写法更好
    /// 学生退宿舍,删除已用宿舍链表中对应结点,
    /// 挂在可用宿舍链表尾部。
    /// @param no_ 宿舍号
    void Return(const string& no_);
    /// 输出可用宿舍链表信息
    void Display_free() const;
    /// 输出已用宿舍链表信息
    void Display_used() const;
    void Cmd();
    
public:
//    用两个链表(已用宿舍链表和可用宿舍链表)维护宿舍的管理,实现宿舍分配、宿舍交回。
    list<SDormNode> m_used;
    list<SDormNode> m_free;
    
};

CDorm::CDorm(int free_len_) {
//    重定义容器大小,可用宿舍有 free_len_间
    m_free.resize(free_len_);
//    char no[3] = {'1', '0', '1'};
    string no("101");
//    编号依次递增, 溢出则抛出异常
    auto no_add = [&no] {
        if (no[2] != '9') {
            ++no[2];
        } else {
            no[2] = '0';
            if (no[1] != '9') {
                ++no[1];
            } else {
                no[1] = '0';
                if (no[0] != '9') {
                    ++no[0];
                } else {
                    no[0] = '0';
                    throw "Overflow!";
                }
            }
        }
    };
//    依次给空宿舍编号
//    const auto e = m_free.end();
//    const auto e = m_free.cend(); //此写法更好, cbegin, 相当于const_iterator, 指针所指物是固定的
//    for (auto i = m_free.begin(); i != m_free.end(); ++i) {
//    for (auto i = m_free.begin(), e = m_free.endl(); i != e; ++i) {
    
//    for (auto i = m_free.begin(); i != e; ++i) { //此写法效率更高, 建议, 尽量不要把函数写进循环内
//        i->dormNo = no;
//        no_add();
//    }
    for (auto &&i : m_free) { //此写法效率更更高,尽量用精简解法,可刷 LeetCode .
        i.dormNo = no;
        no_add();
    }
    
}

CDorm::~CDorm() {
    ;
}

/// 第一行输入used_len,表示已用宿舍used_len间
/// 后跟used_len行数据,每行格式为:宿舍号 学生姓名
void CDorm::Initial() {
    int used_len;
    cin >>used_len;
    m_used.resize(used_len);
//    迭代器从首地址依次到尾地址的下一地址
//    const auto e = m_used.cend();
//    for (auto i = m_used.begin(); i != e; ++i) {
//        cin >>i->stuName >>i->dormNo;
    for (auto &&i : m_used) {
        cin >>i.stuName >>i.dormNo;
        
/*        正确做法一(遍历容器)
        auto j = m_free.begin();
        while (j != m_free.end()) { //做插入,删除时, 建议不要用 for 循环,要用 while, 养成习惯;或者用 for 循环,不要把++i 写进 for头部
            if (i->dormNo == j->dormNo) { // 删去 j (可用宿舍)中相同的宿舍号
//                erase函数:删除iterator指定的节点,使作为参数的迭代器失效,并返回指向该迭代器下一参数的迭代器。
//                即, 在执行完此函数的时候iterator也被销毁了,导致关于iterator的操作就会报错
                j = m_free.erase(j); // 或者 m_free.erase(j++); 错误用法: m_free.erase(j);
            } else {
                ++j;
            }
        }
 */
        
//        正确做法二( lambdab 表达式, 此做法更好, 简洁, 迅捷):
        m_free.remove_if([=](SDormNode j) -> bool {return j.dormNo == i.dormNo; } );

/*        正确做法三(函数对象):
        class CDelete {
        public:
            CDelete(string no_) : m_no(no_) { }
            bool operator() (SDormNode j) {
                return j.dormNo == m_no;
            }
            
        private:
            string m_no;
        };
        m_free.remove_if(CDelete(i->dormNo));
 */
    }
}

void CDorm::Assign(const std::string& name_) {
    m_free.front().stuName = name_;
    
    const string &no = m_free.front().dormNo; //不变的量尽量写成常量引用
//    auto i = m_used.begin(), e = m_used.end();
    auto i = m_used.begin();
    const auto e = m_used.cend(); //迭代器, 数据类型为 list::iterator
    while (i != e) { //按宿舍号升序挂在已用宿舍链表中
        if (no < i->dormNo) {
            break;
        }
        ++i;
    }
//    splice函数说明:在list间移动元素:
//    将x的元素移动到目的list的指定位置,高效的将他们插入到目的list并从x中删除。
//    目的list的大小会增加,增加的大小为插入元素的大小。x的大小相应的会减少同样的大小。
//    指向被删除元素的迭代器会失效。
    m_used.splice(i, m_free, m_free.begin());
}

void CDorm::Return(const std::string& no_) {
    auto i = m_used.begin();
    const auto e = m_used.cend();
    while (i != e) { //找到需要交回的宿舍
        if (no_ == i->dormNo) {
            i->stuName.clear(); //清空交回宿舍的学生姓名
            break;
        }
        ++i;
    }
    m_free.splice(m_free.end(), m_used, i);
}

void CDorm::Display_free() const {
    /*
    const auto s = m_free.cbegin(), e = m_free.cend();
    for (auto i = s; i != e; ++i) { //按格式依次输出
//        if (i != m_free.begin()) {
        if (i != s) { //此写法效率相比上面的if 语句高
            cout <<"-";
        }
        cout <dormNo;
    }
     */
    bool first = true; //此写法可读性更好
    for (auto &&i : m_free) { //精简写法
        if (first) {
            first = false;
        } else {
            cout <<"-";
        }
        cout <<i.dormNo;
    }
    cout <<endl;
}

void CDorm::Display_used() const {
    /*
    const auto s = m_used.cbegin(), e = m_used.cend();
    for (auto i = s; i != e; ++i) { //同上
        if (i != s) {
            cout <<"-";
        }
        cout <stuName <<"(" <dormNo <<")";
    }
     */
    bool first = true;
    for (auto &&i : m_used) {
        if (first) {
            first = !first;
        } else {
            cout <<"-";
        }
        cout <<i.stuName <<"(" <<i.dormNo <<")";
    }
    cout <<endl;
}

void CDorm::Cmd() { //根据输入,进行不同操作
    string cmdName;
    cin >>cmdName;
    if (cmdName == "assign") {
        string stuName;
        cin >>stuName;
        Assign(stuName);
    } else if (cmdName == "return") {
        string dormNo;
        cin >>dormNo;
        Return(dormNo);
    } else if (cmdName == "display_free") {
        Display_free();
    } else if (cmdName == "display_used") {
        Display_used();
    }
}

/// 假设某校有20间宿舍,宿舍编号101,102,...,120。每间只住一名学生。初始部分宿舍已用。
/// 用两个链表(已用宿舍链表和可用宿舍链表)维护宿舍的管理,实现宿舍分配、宿舍交回。
/// 约定已用宿舍链表按宿舍号升序链接。初始可用宿舍链表也按宿舍号升序链接。
/// 宿舍分配从可用宿舍链表中摘取第一间宿舍分配给学生。学生交回的宿舍挂在可用宿舍链表最后。
int main() {
    CDorm dd(20);//初始有 20 间宿舍
    dd.Initial();
    
    int cmdTime;
    cin >>cmdTime;
    for (int i = 0; i < cmdTime; ++i) { //操作次数cmdTime,后跟cmdTime行操作
        dd.Cmd();
    }
//    for (auto &&i : cmdTime) { //语法错误, 此写法不可
//        dd.Cmd();
//    }
    
    return 0;
}

/**
 样例输入
5
李明  103
张三  106
王五  107
钱伟  112
章立  118
8
assign 李四
assign 赵六
return 118
return 101
assign 马山
display_used
assign 林立
display_free
 
 样例输出
赵六(102)-李明(103)-马山(104)-张三(106)-王五(107)-钱伟(112)
108-109-110-111-113-114-115-116-117-119-120-118-101
 */

结尾

代码很长,希望大家看完后有所收获、思考、启迪。

如果你觉得有帮助的话,别忘了点赞关注转发支持我,谢谢!

如果你觉得代码有争议的话,欢迎在下方评论指出。

我是 JaYLove0589。比心❤️

你可能感兴趣的:(C++,数据结构,数据结构,c++)