【实验目的】

帮助学生熟练掌握线性表的基本操作在顺序和链式两种存储结构上的实现,其中以各种链表的操作和应用作为重点内容。

【实验内容及要求】

  1. 问题描述:约瑟夫问题的一种描述是:编号为1,2,…,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m的值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。试设计一个程序求出出列顺序。
  2. 基本要求:利用单向循环链表存储结构模拟此过程,按照出列的顺序印出各人的编号。
  3. 测试数据:m的初值为20;n=7,7个人的密码依次为:3,1,7,2,4,8,4,首先m值为6(正确的出列顺序应为6,1,4,7,2,3,5)。
  4. 实现提示:程序运行后,首先要求用户指定初始报数上限值,然后读取各人的密码。可设n≤30。此题所用的循环链表中不需要“头结点”,请注意空表和非空表的界限。

【实验代码】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#include <iostream>
using namespace std;

struct circularList
{
int number;
int passward;
circularList *next;
};

circularList * createList(int n); // 创建循环链表
int getLength(circularList *); // 获取链表长度
int getIndex(circularList *, circularList *); // 获取指定元素索引
circularList * deleteList(circularList *, int); // 删除结点

int main() {
int m, n; // m为报数初始上限值,n为总人数
cout << "请输入报数初始上限值:" << endl;
cin >> m;
cout << "请输入参与报数的总人数" << endl;
cin >> n;

if(n < 1 || m < 1)
{
cout << "人数或初始报数值非法!" << endl;
return 0;
}

circularList * josephRing;
josephRing = createList(n);
circularList * head = josephRing;

while(true)
{
if(getLength(josephRing) == 1)
{
deleteList(head,1);
cout << "报数完毕!" << endl;
break;
}

for(int i = 0; i < m - 1; i++)
{
josephRing = josephRing->next;
}

m = josephRing->passward;

if(getIndex(head,josephRing) == 1)
{
josephRing = head = deleteList(head,getIndex(head,josephRing));
} else
{
josephRing = deleteList(head,getIndex(head,josephRing));
}
}

return 0;
}

circularList * createList(int n) // 创建循环链表
{
circularList * head, * q, * p; // q为了新建结点,p为了在各个结点中运行,head是链表头部结点
head = q = NULL;

cout << "请依次输入每个人的密码: " << endl;
for(int i = 0; i < n; i++)
{
q = new circularList;
q->number = i + 1;
cin >> q->passward;

if(head == NULL)
{
head = q;
}
else
{
p->next = q;
}
p = q;
}
p->next = head;
cout << "循环链表创建成功! " << endl;

return head;
}

int getLength(circularList * head)
{
circularList * p = head;
int n = 0;

if(p->next == head)
{
return 1;
}

while(true)
{
n++;
p = p->next;
if(p->next == head)
{
n++;
break;
}
}
return n;
}

int getIndex(circularList * head, circularList * jos)
{
int n = 0;
circularList * p = head;

if(p->next == head)
{
return 1;
}

while(true)
{
if(p == jos)
{
n++;
break;
}
n++;
p = p->next;
}
return n;
}

circularList * deleteList(circularList * head, int n) // 删除结点
{
circularList * p = head, * q; // p为了在各个结点中运行,q为了储存要删除的结点,head是链表头部结点
if(n < 0 || n > getLength(head))
{
cout << "索引超出范围,删除失败!" << endl;
return head;
}

if(n == 1)
{
for(int i = 0; i < getLength(head) - 1; i++)
{
p = p->next;
}
q = p->next;
p->next = q->next;
head = p->next;
}
else
{
while(n-- > 2)
{
p = p->next;
}
q = p->next;
p->next = q->next;
}

cout << q->number << " " << "出列!" << endl;
delete q;

return p->next;
}