在我们学习了线性表一段时间后,来试图实现“俄罗斯轮盘赌”这个小游戏,很多人应该都在电影里见过图1这样的游戏场景。游戏的道具是一把左轮手枪,其规则也很简单:在左轮手枪中的 6 个弹槽中随意放入一颗或者多颗子弹,在任意旋转转轮之后,关上转轮。游戏的参加者轮流把手枪对着自己,扣动扳机:中枪或是怯场,即为输的一方;坚持到最后的即为胜者。
解题思路
解决此似问题的核心在于建立轮流循环机制,使用线性表的顺序存储结构和链式存储结构都能实现,根据游戏规则,采用顺序存储结构时,必须使数组的首尾建立连接,即当需要从数组中最后一个位置寻找下一个位置时,要能够跳转到数组的第一个位置(使用取余运算可以解决)。采用链式存储结构时,只需将链表首尾相连,使用循环链表即可轻松解决问题。
同时我们应该明白,采用链式存储结构对于求此类问题是最容易理解的,同时也避免了当参与人数较多时,不必像顺序存储结构那样,因为删除某个游戏成员而频繁地移动其他游戏成员数据。
顺序存储结构模拟轮盘赌
具体C语言实现代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef struct GameMan{
int number;
}GameMan;
int main(){
int PersonNum;//游戏人数
int round=1;//游戏回合
int PersonPos=1;//起始人位置
int BulletPos;//子弹位置
int i;//中枪人位置
srand((int)time(0));//使用当前时间作为rand()函数的随机数的种子
printf("请输入本次游戏人数(<100): ");
scanf("%d",&PersonNum);
printf("\n为编号为 1-%d 的游戏人员分配位置!\n\n",PersonNum);
GameMan GameMans[100];//存储游戏人员编号的数组
for (int j=1;j<=PersonNum;j++) {//依次为参加者分配编号
GameMans[j].number=j;
}
//当只剩余一个人时,此场结束
while (PersonNum!=1) {
BulletPos=rand()%6+1;//左轮旋转后的子弹位置(利用随机数1~6表示)
printf("第 %d 轮游戏,从编号为 %d 的人开始,枪在第 %d 次扣动扳机时会响!\n\n",round,GameMans[PersonPos].number,BulletPos);
for (i=PersonPos;i<PersonPos+BulletPos-1;i++);//找到每轮退出的人的位置 i
//由于参与者排成的是环,当人数少时可能会回到开头,所以需要对求得 i 值进行取余处理
//当 i=0时,实际上指的是位于数组结尾的参与者,需要重新调整 i 的值
if(!(i=i%PersonNum)){i=PersonNum;PersonPos=1;}
else PersonPos=i;//PersonPos表示的是下一轮开始的位置
printf("编号为 %d 的赌徒退出赌博,剩余赌徒编号依次为:",GameMans[i].number);
//使用顺序存储时,如果删除元素,需要将其后序位置的元素进行全部前移
PersonNum--;//此时参与人数减一
//先将i位置后成员向前依次移动
for (int j=i;j<=PersonNum;j++) {
GameMans[j]=GameMans[j+1];
}
//再打印剩余成员
for (int k=1; k<=PersonNum; k++) {
printf("%d ",GameMans[k].number);
}
printf("\n\n");
round++;//到回合数加一
}
printf("最终胜利的游戏人员编号是:%d \n\n",GameMans[1].number);
}
程序运行结果如下:
请输入本次游戏人数(<100): 5
为编号为 1-5 的游戏人员分配位置!
第 1 轮游戏,从编号为 1 的人开始,枪在第 1 次扣动扳机时会响!
编号为 1 的赌徒退出赌博,剩余赌徒编号依次为:2 3 4 5
第 2 轮游戏,从编号为 2 的人开始,枪在第 2 次扣动扳机时会响!
编号为 3 的赌徒退出赌博,剩余赌徒编号依次为:2 4 5
第 3 轮游戏,从编号为 4 的人开始,枪在第 6 次扣动扳机时会响!
编号为 2 的赌徒退出赌博,剩余赌徒编号依次为:4 5
第 4 轮游戏,从编号为 4 的人开始,枪在第 5 次扣动扳机时会响!
编号为 4 的赌徒退出赌博,剩余赌徒编号依次为:5
最终胜利的游戏人员编号是:5
请按任意键继续. . .
链式存储结构模拟轮盘赌
具体C语言实现代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//链表节点单元
typedef struct GameMan{
int Man;
struct GameMan * next;
}GameMan;
//建立游戏人员循环链表
void InitGameManLine(GameMan **GameMans,int PersonNum){
*GameMans=(GameMan *)malloc(sizeof(GameMan));//头指针指向首元节点
//节点初始化
(*GameMans)->next=NULL;
(*GameMans)->Man=1;
GameMan *tail=*GameMans;//指向链表尾
for (int i=2;i<=PersonNum;i++){
GameMan *newnode=(GameMan *)malloc(sizeof(GameMan));//申请新节点
//节点初始化
newnode->next=NULL;
newnode->Man=i;
tail->next=newnode;//将新节点链接到链表尾
tail=tail->next;//移动tail到尾指针
}
tail->next=*GameMans;//将链表成环
}
//输出链表中所有游戏成员
void display(GameMan *GameMans){
GameMan *temp=GameMans;
while (temp->next!=GameMans){
printf("%d ",temp->Man);
temp=temp->next;
}
printf("%d\n\n",temp->Man);
}
int main() {
GameMan *GameMans=NULL;//游戏成员链表头指针
int round=1;
int PersonNum;
int BulletPos;
srand((int)time(0));//使用当前时间作为rand()函数的随机数的种子
printf("请输入本次游戏人数(<100): ");
scanf("%d",&PersonNum);
printf("\n为编号为 1-%d 的游戏人员分配位置!\n\n",PersonNum);
InitGameManLine(&GameMans,PersonNum);
GameMan* lineNext=GameMans;//用于记录每轮开始的位置
//仅当链表中只含有一个结点时,即头结点时,退出循环
while(GameMans->next!=GameMans){
BulletPos=rand()%6+1;
printf("第 %d 轮游戏,从编号为 %d 的人开始,枪在第 %d 次扣动扳机时会响!\n\n",round,lineNext->Man,BulletPos);
GameMan *temp=lineNext;
//遍历循环链表,找到将要删除结点的上一个结点
for (int i=1;i<BulletPos-1;i++){
temp=temp->next;
}
//如果子弹位置BulletPos==1,则要找到当前节点的上一节点
if(BulletPos==1){
while(temp->next!=lineNext){
temp=temp->next;
}
}
printf("编号为 %d 的赌徒退出赌博,剩余赌徒编号依次为:",temp->next->Man);
//将要删除结点从链表中删除,并释放其占用空间
GameMan * del=temp->next;//记录删除节点
temp->next=temp->next->next;//从链表中移除该节点
if(del==GameMans)GameMans=GameMans->next;//如果删除的是头节点,将头节点的下一节点作为头节点
free(del);//释放del节点
display(GameMans);
//下一轮开始的位置
lineNext=temp->next;
round++;//回合次数加一
}
printf("最终胜利的游戏人员编号是:%d \n\n",GameMans->Man);
return 0;
}
程序运行结果如下:
请输入本次游戏人数(<100): 5
为编号为 1-5 的游戏人员分配位置!
第 1 轮游戏,从编号为 1 的人开始,枪在第 3 次扣动扳机时会响!
编号为 3 的赌徒退出赌博,剩余赌徒编号依次为:1 2 4 5
第 2 轮游戏,从编号为 4 的人开始,枪在第 3 次扣动扳机时会响!
编号为 1 的赌徒退出赌博,剩余赌徒编号依次为:2 4 5
第 3 轮游戏,从编号为 2 的人开始,枪在第 4 次扣动扳机时会响!
编号为 2 的赌徒退出赌博,剩余赌徒编号依次为:4 5
第 4 轮游戏,从编号为 4 的人开始,枪在第 4 次扣动扳机时会响!
编号为 5 的赌徒退出赌博,剩余赌徒编号依次为:4
最终胜利的游戏人员编号是:4
请按任意键继续. . .