请使用合适的数据结构实现:使用“折返报数”方式的Joseph问题。
所谓“折返报数”Joseph问题是指:
(1)n(n>1)个人排成一队,从1到n依次编号;从1到m循环报数,报到m(m>1)者退出;
(2)游戏先从编号为1的人(队头)开始依次从前向后报数,当报数到达队尾时,立即改为从后往前报数;
(3)当从后往前报数到达队头时,立即改为从前往后报数;
(4)如此“折返”报数,直到队列中只剩下1人为止,即为胜者;
要求:
(1)从键盘输入n和m;
(2)依次输出游戏过程中退出人的编号;
(3)输出获胜者编号;
例:
输入:n=5;m=3
输出:
3 4 2 5
胜者:1
- #include
- using namespace std;
- typedef struct LinkNode {
- int data;
- LinkNode* next;
- LinkNode* pre;
- }*LinkList;
- void InitLinkList(LinkList& L) {
- L = NULL;
- }
- void CreatRear(LinkList& L,int n) {
- L = NULL;
- if (n <= 0)return;
- else {
- L = new LinkNode;
- cin >> L->data;
- L->pre = NULL; L->next = NULL;
- if (n > 1) {
- LinkNode* r = L;
- for (int i = 1; i < n; i++) {
- LinkNode* s = new LinkNode;
- cin >> s->data;
- r->next = s;
- s->pre = r;
- r = r->next;
- }
- r->next = NULL;
- }
- }
- }
- void Returning_Joseph(int n, int m) {
- LinkList L; InitLinkList(L);
- CreatRear(L, n);
- LinkNode* p = L; int isBack = 0;
- for (int i = 0; i < n - 1; i++) {
- int j = 0;
- while (j < m - 1) {
- if (isBack == 0) {
- while (p->next != NULL && j < m - 1) {
- p = p->next; j++;
- }
- if (p->next == NULL)isBack = 1;
- }
- if (isBack == 1) {
- while (p->pre != NULL && j < m - 1) {
- p = p->pre; j++;
- }
- if (p->pre == NULL)isBack = 0;
- }
- }
- cout << p->data << " ";
- LinkNode* r = p;
- if (p->next == NULL || p->pre == NULL) {
- if (p->next == NULL) {
- p = p->pre;
- delete(p->next);
- }
- else {
- p = p->next;
- delete(p->pre);
- }
- }
- else {
- p->pre->next = p->next;
- p->next->pre = p->pre;
- if (isBack == 0)p = p->next;
- else p = p->pre;
- r->next = NULL; r->pre = NULL;
- delete(r);
- }
- }
- cout << endl;
- cout << p->data << "是胜者";
- }
- int main(){
- Returning_Joseph(5, 3);
- }
此题难点在于在约瑟夫问题的基础上摒弃了环的结构,而是改为“折返”,所以自然想到双向链表(无头),但难点在于,如何让程序知道现在是向前遍历还是向后遍历呢?
这里我们设一个初始值 isBack==0,来判断现在是向前还是向后遍历,如果超过了边界,那么更改isBack的值进入到另一个向前遍历的分支即可,如此循环,即可实现“折返”