fjut-quiz-2022-10-26/quiz4/quiz4.cpp

137 lines
4.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <iostream>
#include <cstring>
#include <sstream>
#include <limits.h>
/**
* 循环链表的一个节点
* 也代表着一个小人
*/
struct node {
int id; /** 链表数据体,也就是小人的 id */
node* next;
node* prev;
};
/**
* 指定一个循环链表的头,和一个全新节点,
* 把这个节点按照循环链表的规则加入进这个循环链表的头和尾之间.
*
* > 其实循环链表并不能说有一个物理意义的 “头”
* > 所以这里也就是把一个新的
*
* 传入的循环链表 "头" 应该是在一个完整的循环链表当中的。
* 传入的全新节点不需要有前后链接(一般全新的也不会有吧)。
*
* 不要尝试把一个已经在一个循环链表中的节点作为全新节点传入,会产生**不可预见的错误**。
*
* @param first 循环链表的 "头"
* @param curr 全新的节点,会被插在 "头" 的前面
*/
void insert_one (node* first, node* curr) {
node* insertion_prev = (*first).prev;
node* insertion_next = first;
(*insertion_next).prev = curr;
(*insertion_prev).next = curr;
(*curr).next = insertion_next;
(*curr).prev = insertion_prev;
}
/**
* 将一个节点从它所属的循环链表中移出并销毁.
*
* 十分简单,把它的前后节点相连然后销毁掉它自己。
*
* 传入的节点将会被销毁不能再用了!
*
* @param removal 将要被移出循环链表并被销毁的节点
*/
void remove_one (node* removal) {
node* removal_prev = (*removal).prev;
node* removal_next = (*removal).next;
(*removal_next).prev = removal_prev;
(*removal_prev).next = removal_next;
delete removal;
}
int main () {
// 接受用户输入中
/** 用户输入的小人数量 */
int n = 0;
while (n == 0) { // 由于任何输入问题导致没有成功获得输入数据都会重新进行输入请求
std::string input;
int input_n;
std::cout << "请输入小人个数: ";
std::cin >> input;
try { // 尝试解析用户输入
input_n = stoi(input);
if (input_n < 1) throw std::exception();
} catch (std::out_of_range) { // 如果输入的数字太大以至于超出了系统处理范围的报错
std::cout << "你输入的数字 \"" << input << "\" 太大了,它最大只能是 " << INT_MAX << std::endl;
continue;
} catch (std::exception) { // 如果输入的不是一个数字,或者输入的数字不符合要求(当然不会有小于 0 个人0 个人(没有人)也显然不符合预设现实)的报错
std::cout << "你的输入是 \"" << input << "\", 但小人的数量必须是一个大于 0 的整数!" << std::endl;
continue;
}
// 二次确认输入数值(防呆)
std::cout << "小人个数为 " << input_n << " ? [y/n]";
std::cin >> input;
if (input == "y")
n = input_n;
}
// 招募第一个小人(为链表创建 "头",同时也构建出最小的链表体)
node* first = new node;
(*first).id = 1;
(*first).prev = first;
(*first).next = first;
/** 记录这个队里目前有多少人(记得手动操作) */
int count = 1;
std::cout << "招募了小人 #" << 1 << " ." << std::endl;
// 招募从第 2 个开始的剩下的小人(按照顺序一个个把它们加到链表尾)
for (int i = 2; i <= n; i++) {
node* curr = new node;
(*curr).id = i;
insert_one(first, curr);
count++;
std::cout << "招募了小人 #" << 1 << " , 它前面是 #" << (*(*curr).prev).id << " , 它后面是 #" << (*(*curr).next).id << std::endl;
}
/**
* 出队小人的缓冲区.
*
* 一个字符串流,用来暂存小人出队的输出。(等到运算结束后再倒出来)
*/
std::stringstream out_roll;
int counting = 0;
node* next_one = (*first).prev;
while (count > 1) { // 让大家轮流出队直到队里只剩下最后一个人
node* current = next_one;
next_one = (*next_one).next;
counting++;
std::cout << "小人 #" << (*current).id << " 数到了" << counting;
if (counting == 3) {
out_roll << "#" << (*current).id << " ";
remove_one(current);
counting = 0;
count--;
std::cout << " , 出列!还剩下 " << count << "个小人。";
} else {
std::cout << " .";
}
std::cout << std::endl;
}
// 出队总结
std::cout << "=====" << std::endl
<< "最终剩下了小人 #" << (*next_one).id << " !" << std::endl;
std::cout << "小人的出列顺序是: " << out_roll.str() << "." << std::endl;
return 0;
}