1. 程式人生 > >(2011.11.20)02_迴圈連結串列舉例_約瑟夫(Joseph)環問題.cpp

(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

問題探討成功,請按任意鍵繼續. . .


相關推薦

(2011.11.20)02_迴圈連結串列舉例_(Joseph)問題.cpp

        這次感覺寫得挺一般的,邏輯有點亂,功能與功能之間有點混雜,沒有將它們一個個小的功能再細分出來。有待改進。大概用了四個小時,編寫除錯速度也是,有待提高,加油! 原始碼: // 02_迴圈連結串列舉例_約瑟夫(Joseph)環問題.cpp /** * 問題描

Java用迴圈連結串列寫的

package com; import java.util.Scanner; interface SeqList{//規範化實現介面類的操作//初始化連結串列ListNode initList();//插入一個一個節點ListNode insertNode(int val,

連結串列面試題----相關

約瑟夫環相關連結串列面試題 繼上篇部落格之------------ 什麼是約瑟夫環 約瑟夫環 判斷是否成環 求環的長度 環的入口點

《C語言》環形連結串列與《問題》

環形連結串列與《約瑟夫問題》 Main.c A_List.h A_List.c Main.c #include "A_List.h" #include <time.h> void main() { /*********

資料結構開發(11):雙向迴圈連結串列的實現

0.目錄 1.雙向迴圈連結串列的實現 2.小結 1.雙向迴圈連結串列的實現 本節目標: 使用 Linux 核心連結串列實現 StLib 中的雙向迴圈連結串列 template <typename T> class DualCircleList; StLib 中雙向迴圈

順序表模板和雙向帶頭迴圈連結串列模板

類的類名和型別 從在類內定義順序表模板的建構函式和類內宣告類外定義建構函式來區別類名和 型別: #include<iostream> #include<stdlib.h> #include<string> using namespace std;

[領卓教育]迴圈連結串列

#include <stdio.h> #include <stdlib.h> #include <string.h> #define N 10 /************迴圈連結串列****************/ typedef struct node

C 將一個單鏈表拆成3個迴圈連結串列,其中一個是純數字,一個純字母,一個其他字元

前面相關操作在這呢,這個函式依託於此 //結構體 typedef struct Node { ElementType data; struct Node * next; } LNode, * LinkNode; //將一個單鏈表拆成3個迴圈連結串列,其中一個是純數字

雙鏈表(雙向帶頭迴圈連結串列

雙向連結串列 雙向連結串列也叫雙鏈表,是連結串列中的一種。它的每個資料結點中都有兩個指標,分別指向直接前驅和直接後繼,所以從連結串列的任意一個結點都可以很方便的訪問到它的前驅結點和後繼結點,一般我們都構造雙向迴圈連結串列。 ## 帶頭結點和不帶頭結點   其實這個帶頭結點和不帶

迴圈連結串列解決的問題

約瑟夫環問題簡介  約瑟夫環問題的原來描述為,設有編號為1,2,……,n的n(n>0)個人圍成一個圈,從第1個人開始報數,報到m時停止報數,報m的人出圈,再從他的下一個人起重新報數,報到m時停止報數,報m的出圈,……,如此下去,直到所有人全部出圈為止。當任意給定n和m後,設計演算法求n個人出

js資料結構 -- 連結串列, 雙向連結串列迴圈連結串列

陣列作為js常用的資料結構,存取元素都非常的方便,但是其內部的實現原理跟其他計算機語言對陣列的實現都是一樣的。 由於陣列在記憶體中的儲存是連續的,所以當在陣列的開頭或者中間插入元素時,內部都需要去移動其他元素的位置,這個移動元素的成本很高。 連結串列也是儲存有序的元素集合,但不同

C語言_雙向迴圈連結串列的基本操作

目錄: 1、初始化 2、頭部插入 3、頭部刪除 4、尾部插入 5、尾部刪除 6、列印連結串列 7、任意位置插入 8、查詢值為data的節點 9、指定位置刪除 10、銷燬連結串列 ###1、初始化: 建立一個節點,給節點賦值為0;因為是迴圈連結串列,所以讓它的_pNext

魔術師發牌問題(迴圈連結串列

問題描寫敘述:        魔術師手中有A、2、3……J、Q、K十三張黑桃撲克牌。在表演魔術前,魔術師已經將他們依照一定的順序疊放好(有花色的一面朝下)。魔術表演過程為:一開始,魔術師數1,然後把最上面的那張牌翻過來,是黑桃A;然後將其放到桌面上

基於java自帶連結串列結構實現迴圈連結串列

背景 有些場景下,需要迴圈連結串列,如某些狀態是從一個到下一個,最後再回到開始。此種情況下,可以採用迴圈連結串列來實現。 程式碼 package com.cxyzy.tencentfacerec; import java.util.Iterator; import java.u

資料結構與演算法JavaScript描述讀書筆記(js實現連結串列-迴圈連結串列

迴圈連結串列 迴圈連結串列同單鏈表,只需設定head.next = head就可以實現迴圈連結串列 其它方法不變,但是遍歷方法需要改一下 function display() { var cur = this.head; var str = ''; //如果不設定

python 資料結構與演算法 day02 單向迴圈連結串列

1. 實現單向迴圈連結串列 class Node(): """定義結點""" def __init__(self,item): self.item=item self.next=None class SingleLoopLinkList(object):

帶有頭結點的單向迴圈連結串列

題目:         建立帶有頭結點的迴圈連結串列,實現插入 、查詢 、 刪除等功能 。 原始碼: #include<iostream> #include<malloc.h> using namespace std; ty

資料結構 筆記:迴圈連結串列的實現

什麼事迴圈連結串列? -概念上 ·任意資料元素都有一個前驅和一個後繼 ·所有的資料元素的關係構成一個邏輯上的環 -實現上 ·迴圈連結串列是一種特殊的單鏈表 ·尾結點的指標域儲存了首結點的地址 迴圈連結串列的實現思路 -通過模板定義CircleList類,繼承自L

資料結構(三)——單向迴圈連結串列的java實現

單向迴圈連結串列結構就是連結串列的最後一個指標不再是null,而是指向整個連結串列的第一個結點,使連結串列形成一個環。 上程式碼 package likend; /** * Created by yxf on 2018/3/27. * 單向迴圈連結串列 */ publ

無頭單向非迴圈連結串列基本操作實現

之前寫了動態順序表的實現,但動態順序表還是存在以下問題 中間/頭部的插入刪除,時間複雜度為O(N) 增容需要申請新空間,拷貝資料,釋放舊空間。會有不小的消耗。 增容一般是呈2倍的增長,勢必會有一定的空間浪費。例如當前容量為100,滿了以後增容到200, 我們再繼續插入