(2011.11.20)02_循环链表举例_约瑟夫(Joseph)环问题.cpp

        这次感觉写得挺一般的,逻辑有点乱,功能与功能之间有点混杂,没有将它们一个个小的功能再细分出来。有待改进。大概用了四个小时,编写调试速度也是,有待提高,加油!

源代码:

// 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

问题探讨成功,请按任意键继续. . .


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值