这次感觉写得挺一般的,逻辑有点乱,功能与功能之间有点混杂,没有将它们一个个小的功能再细分出来。有待改进。大概用了四个小时,编写调试速度也是,有待提高,加油!
源代码:
// 02_循环链表举例_约瑟夫(Joseph)环问题.cpp
/**
* 问题描述及要求:
* 01.假设有n个人坐在圆桌周围,从第s个人开始报数,报到m的人出列;
* 02.然后再从下一个人开始报数,报到m的人又出列...直到所有的人都出列为止。
* 03.要求按出列的先后顺序输出每个人的编号。
**/
/**
* 问题分析:
* 1.该题目适合使用循环链表实现,这是因为圆桌的顺序数数就相当于一个不断循环的过程。
* 2.题目假设有n个人坐在圆桌周围,那么这里就使用链表来实现,想多少个人都可以,逐个插入。
* 3.根据题目要求,需要用到的数据成员有人的姓名,还有他在圆桌的位置编号(这个可以不要)。
* 这里可以确定需要在类中需要写出的成员函数有:
* 01)构造和析构函数:链表的初始化是一定需要的。
* 02)插入函数:将一个个人的数据插入。
* 03)删除函数,当人需要出列的时候,相当于删除该人的数据。
* 04)输出到屏幕显示的函数:当人出列前,需要先打印出该人的数据(题目要求)。
* 05)查找函数:判断人出列时,需知该人是否在列中。
**/
#include <iostream> // 输入输出需用
#include <string> // 用户姓名存储方式
#include <cstdlib> // system("pause")
using std::cin; // using声明
using std::cout;
using std::endl;
using std::string;
// ------------------------------- 类的结点(linknote)声明及定义 -----------------------------------
class circulatelink; // 类声明,提前使用
class linknote
{
private:
friend class circulatelink; // 使链表成为友元类
linknote * next; // 指向下一结点
string people; // 桌子上的人名
int num; // 人名相应的序号
public:
linknote(linknote * n = NULL): next(n){}
linknote(string p, int number, linknote * n = NULL): next(n), people(p), num(number){}
};
// ------------------------------- 类(circulatelink)声明 -----------------------------------
class circulatelink
{
private:
// 用于知道链表中的数据数量
enum size{defaultmaxsize = 15}; // 默认最大值
int maxsize; // 实际最大值
int leftsize; // 距离第一个数据的数量
// 用于链表的定位指针
linknote * tail; // 尾指针
linknote * fence; // 阑栅
linknote * head; // 头指针
public:
// 构造析构函数
circulatelink(int m = defaultmaxsize):
maxsize(m), leftsize(0), head(NULL), fence(NULL), tail(NULL){}
~circulatelink()
{
for (fence = head; leftsize != 0; --leftsize)
{
fence = head;
head = head -> next;
delete fence;
}
leftsize = 0;
maxsize = 0;
fence = NULL;
tail = NULL;
head = NULL;
}
// 功能函数
int getmaxsize() {return maxsize;}
int getleftsize() {return leftsize;}
void append(string p); // 插入函数
void deldata(const int & startnumber, const int & runnumber); // 删除函数
void print(const int & startnumber, const int & runnumber); // 显示函数
bool search(const int & number); // 查找函数
};
// ------------------------------- 类(circulatelink)定义 -----------------------------------
/**
* 函数名称:append
* 函数参数:一个string型的变量p.
* 函数功能:接受一个string型形参p插入到循环链表尾中,并使插入的姓名对应的号码为当前的链表大小加1。
* 该函数执行完成后,尾指针指向头指针,fence指针不变,maxsize不变,leftsize加1.
**/
void circulatelink::append(string p) // 插入函数
{
if (head ==NULL && tail == NULL)
{
head = new linknote(p, leftsize + 1, NULL);
tail = head;
tail -> next = head;
}
else
{
tail -> next = new linknote(p, leftsize + 1, head);
tail = tail -> next;
}
++leftsize;
return;
}
/**
* 函数名称:search
* 函数参数:一个常量int型变量number
* 函数功能:在链表中查找是存在number元素,如果有,则返回真,没,则返回假
* 子函数结束后,fence指向头指针或number元素的上一指针。
**/
bool circulatelink::search(const int & number) // 查找函数
{
if (maxsize == 0)
return false;
int i;
for (leftsize = 0, fence = tail, i = 0; i != maxsize; fence = fence -> next, ++i)
{
leftsize++;
if (fence -> next -> num == number)
return true;
}
return false;
}
/**
* 函数名称:deldata
* 函数参数:1.startnumber 整型常量,用于接受开始计算的用户名号码的编号。
* 2.runnumber 整型常量,用于接受题目要求中所说的报到数目。
* 函数功能:按照startnumber和runnumber计算出程序所要删除的元素的位置及将其删除。
**/
void circulatelink::deldata(const int & startnumber, const int & runnumber) // 删除函数
{
// 该函数结合search函数使用,这里不进行调用,因为在主函数中已调用
/* 该步已在print函数中执行
for (int i = 0; i != runnumber; ++i)
{
fence = fence -> next; // 根据要求将fence移位
leftsize++;
}
*/
linknote * temp = fence -> next;
if (temp == head)
{
head = fence -> next;
tail -> next = head;
leftsize = 0;
}
if (temp == tail)
{
leftsize = maxsize - 1;
tail = fence;
}
fence -> next = fence -> next -> next;
delete temp;
--maxsize;
if (maxsize == 0)
head = fence = tail = NULL;
return;
}
/**
* 函数名称:print
* 函数参数:1.startnumber 整型常量,用于接受开始计算的用户名号码的编号。
* 2.runnumber 整型常量,用于接受题目要求中所说的报到数目。
* 函数功能:按照startnumber和runnumber计算出程序所要显示的函数并完成出列前的显示功能。
**/
void circulatelink::print(const int & startnumber, const int & runnumber) // 删除函数
{
// 该函数结合search函数使用,这里不进行调用,因为在主函数中已调用
for (int i = 0; i != runnumber; ++i)
{
fence = fence -> next; // 根据要求将fence移位
leftsize++;
}
cout << "-> 本次需要离开圆桌的是:" << fence -> next -> people << endl;
return;
}
//------------------------------------------------------------------------------------------------------
// ------------------------------------------ 整个程序流程(main)开始 -------------------------------
int main()
{
// 程序开始 -- 对用户说明
cout << " ------------------- 欢迎一起进行约瑟夫(Joseph)环问题的探讨 ------------------- \n";
cout << "-> 题目说明:\n"
<< " 假设有n个人坐在圆桌周围,从第s个人开始报数,报到m的人出列,"
<< "然后再从下一个人开始报数,报到m的人又出列...直到所有的人都出列为止。\n";
cout << "-> 实验开始:\n"
<< "1. 开始安排座位\n"
<< "2. 开始报到\n"
<< "3. 退出\n";
cout << "-> 选择:";
int choice();
int select(choice());
// 子功能的实现
circulatelink seat; // 先声明变量及需要用到的函数
void plansit(circulatelink & seat); // 实现功能2插入元素到链表
void outsit(circulatelink & seat); // 实现功能3按顺序出链表
start:
switch(select)
{
case 3: break;
case 1: plansit(seat);
case 2:
{
if (select == 2)
{
cout << " ->请先选择1号功能,现在默认跳到1号功能。\n";
select = 1;
goto start; // 这里为了设计方便,简单地使用了if-goto语句
}
else
outsit(seat);
}
}
cout << "问题探讨成功,";
system("pause");
return 0;
}
// --------------------------- 主函数中的功能子函数 --------------------------------------------
/**
* 函数名称:choice
* 函数参数:无
* 函数返回类型:int
* 函数说明:本函数主要用于主菜单的功能选择,
* 在该函数内,会要求用户输入一个范围1~3的正确的int型数值
* 最后返回该int型的数据。
**/
int choice ()
{
int ch;
cin >> ch;
while(!cin || ch > 3 || ch < 1)
{
cin.sync();
cin.clear();
cout << "\n -> INPUT ERROR, ENTER AGAIN:";
cin >> ch;
cout << endl;
}
cout << endl;
return ch;
}
/**
* 函数名称:plansit
* 函数参数:引用circulate类型的参数一个
* 函数返回类型:void
* 函数说明:本函数功能为将15个数据插入到circulate链表中。
**/
void plansit(circulatelink & seat)
{
seat.append("people 01");
seat.append("people 02");
seat.append("people 03");
seat.append("people 04");
seat.append("people 05");
seat.append("people 06");
seat.append("people 07");
seat.append("people 08");
seat.append("people 09");
seat.append("people 10");
seat.append("people 11");
seat.append("people 12");
seat.append("people 13");
seat.append("people 14");
seat.append("people 15");
cout << "-> 已成功为15位people安排座位,现在继续实现2号功能...\n\n";
return;
}
/**
* 函数名称:outsit
* 函数参数:引用circulate类型的参数一个
* 函数返回类型:void
* 函数说明:本函数功能为将链表中数据按题目要求方法依次出列。
**/
void outsit(circulatelink & seat)
{
for (int i = 1; seat.getmaxsize() != 0; ++i)
{
// 01输入出列人号码
int outsitnumber;
bool errorsign(false);
cout << "\n-> 请输入你希望第"<< i << "位开始报数的人的号码:";
do
{
cin >> outsitnumber;
// 001确定出列人输入正确
if( !cin)
{
cin.sync();
cout << "\n -> INPUT ERROR, ENTER AGAIN:";
cin.clear();
errorsign = true;
}
// 002确定出列人位于链表中
else if (seat.search(outsitnumber) == false)
{
cout << "-> 该表中的" << outsitnumber << "已出列,请重新输入需要开始报数的人的号码:";
errorsign = true;
}
else
errorsign = false;
}while(errorsign);
// 02确定输入报数号码
int runnumber;
cout << "-> 请输入报数号码:";
cin >> runnumber;
while(!cin)
{
cin.sync();
cin.clear();
cin >> runnumber;
}
// 03开始出列
seat.print(outsitnumber, runnumber);
seat.deldata(outsitnumber, runnumber);
cout << endl;
}
return;
}
调试运行过程:
------------------- 欢迎一起进行约瑟夫(Joseph)环问题的探讨 -------------------
-> 题目说明:
假设有n个人坐在圆桌周围,从第s个人开始报数,报到m的人出列,然后再从下一个人
开始报数,报到m的人又出列...直到所有的人都出列为止。
-> 实验开始:
1. 开始安排座位
2. 开始报到
3. 退出
-> 选择:5
-> INPUT ERROR, ENTER AGAIN:2
->请先选择1号功能,现在默认跳到1号功能。
-> 已成功为15位people安排座位,现在继续实现2号功能...
-> 请输入你希望第1位开始报数的人的号码:15
-> 请输入报数号码:1
-> 本次需要离开圆桌的是:people 01
-> 请输入你希望第2位开始报数的人的号码:1
-> 该表中的1已出列,请重新输入需要开始报数的人的号码:0
-> 该表中的0已出列,请重新输入需要开始报数的人的号码:2
-> 请输入报数号码:5
-> 本次需要离开圆桌的是:people 07
-> 请输入你希望第3位开始报数的人的号码:2
-> 请输入报数号码:0
-> 本次需要离开圆桌的是:people 02
-> 请输入你希望第4位开始报数的人的号码:3
-> 请输入报数号码:0
-> 本次需要离开圆桌的是:people 03
-> 请输入你希望第5位开始报数的人的号码:4
-> 请输入报数号码:5
-> 本次需要离开圆桌的是:people 10
-> 请输入你希望第6位开始报数的人的号码:5
-> 请输入报数号码:0
-> 本次需要离开圆桌的是:people 05
-> 请输入你希望第7位开始报数的人的号码:6
-> 请输入报数号码:934
-> 本次需要离开圆桌的是:people 15
-> 请输入你希望第8位开始报数的人的号码:4
-> 请输入报数号码:0
-> 本次需要离开圆桌的是:people 04
-> 请输入你希望第9位开始报数的人的号码:6
-> 请输入报数号码:0
-> 本次需要离开圆桌的是:people 06
-> 请输入你希望第10位开始报数的人的号码:8
-> 请输入报数号码:0
-> 本次需要离开圆桌的是:people 08
-> 请输入你希望第11位开始报数的人的号码:9
-> 请输入报数号码:7
-> 本次需要离开圆桌的是:people 12
-> 请输入你希望第12位开始报数的人的号码:9
-> 请输入报数号码:8
-> 本次需要离开圆桌的是:people 09
-> 请输入你希望第13位开始报数的人的号码:11
-> 请输入报数号码:2
-> 本次需要离开圆桌的是:people 14
-> 请输入你希望第14位开始报数的人的号码:11
-> 请输入报数号码:2
-> 本次需要离开圆桌的是:people 11
-> 请输入你希望第15位开始报数的人的号码:7
-> 该表中的7已出列,请重新输入需要开始报数的人的号码:6
-> 该表中的6已出列,请重新输入需要开始报数的人的号码:13
-> 请输入报数号码:9
-> 本次需要离开圆桌的是:people 13
问题探讨成功,请按任意键继续. . .