设计题目 1: 约瑟夫生者死者游戏
双向循环链表实现:
链表采用带头结点的结构,便于链表的插入和判空。
插入:直接利用头结点的前驱指针,可以找到链表的尾巴,然后呢通过头和尾进行插入。
判空:令 头结点的后驱结点等于自身 为空。
删除:需要注意将删除结点的前驱结点的后继指针和后继结点的前驱指针改掉
实现思路:文章来源:https://www.toymoban.com/news/detail-771742.html
由于链表带头结点,所以但头结点不包含信息,循环报数时(即以此遍历链表),会误将头结点作为玩家,因此头结点是一个冗余的结点,所以在游戏开始前,需要将头结点去掉,然后默认将编号为1的玩家当作头结点。游戏结束后指针一般不是指向编号最小的那个,所以可以通过前驱指针遍历,当当前结点的前驱结点的编号大于自身时循环结束,这样就找到编号最小的人。游戏结束后不要忘了将把链表改为有结点的链表,保证链表的完整性。文章来源地址https://www.toymoban.com/news/detail-771742.html
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#define ElemType int
// 双向循环链表实现
// 带头结点-不用于保存数据 ,用于判空即 头结点的前驱和后继相等
typedef struct _Node{
struct _Node* pre;// 前驱
ElemType elem;// 编号
struct _Node* next;// 后继
} *List,Node;
// 创建一个空链表
List creatList(){
List list = (List)malloc( sizeof(Node) );
assert( list != NULL );
list->pre = list->next = list;// 指向自身便于后续插入
return list;
}
// 尾插
void push_back( List list, ElemType elem ) {
assert( list );
Node* node = (Node*)malloc( sizeof(Node) );
assert( node );
node->elem = elem;
node->next = list;// 新结点的后继是表头
node->pre = list->pre;// 新结点的后继是表头的前驱
list->pre->next = node;// 将老的尾部的next指向新结点
list->pre = node;// 将表头前驱指向新结点
}
// 从头遍历一遍链表
void printList( List list ){
Node* start = list->next;
while( start != list ){
printf("%d ",start->elem);
start = start->next;
}
printf("\n");
}
// 在传入结点所在的链表,将该结点删除
int erase( Node* node ){
assert( node != NULL );
if( node->pre == node ){// 空链表,删除失败
return 0;
}
node->pre->next = node->next;// 改变前驱结点的后继
node->next->pre = node->pre;// 改变后继结点的前驱
free( node );
return 1;
}
// 游戏,返回的链表保存了嘎的人的编号
List game1( List* list, int n, int m, int k ){
// 由于传入的链表是带有头结点的,多了一个空数据,不利于后续遍历
// 所以改造成无头结点的
if( (*list)->pre == *list ) {// 无玩家,直接退出
return ;
}
Node* t = *list;// 保存头结点,便于后续恢复
// 改造为无头结点的链表
*list = (*list)->next;
(*list)->pre = t->pre;
t->pre->next = *list;
free( t );
List die = creatList();
// 开始推人
int cnt = 1;
while( n > k ){
if( cnt == m ){
t = (*list)->next;
push_back( die, (*list)->elem );
erase( *list );
*list = t;
cnt = 1;
n--;
} else {
cnt++;
*list = (*list)->next;
}
}
// 恢复链表
// 首先找到最小元素位置
while( (*list)->pre->elem < (*list)->elem ){
(*list) = (*list)->pre;
}
// printf("*****%d\n",(*list)->elem);
// 将头结点插入
t = (Node*)malloc(sizeof(Node));
// 先让 头结点 主动与链表 逻辑链接
t->next = *list;
t->pre = (*list)->pre;
// 再让 链表 与头结点 逻辑链接
(*list)->pre->next = t;
(*list)->pre = t;
// 最后,链表指向头结点
*list = t;
return die;
}
// 游戏,返回的链表保存了嘎的人的编号
List game2( List* list, int n, int m, int k, int q ){
// 由于传入的链表是带有头结点的,多了一个空数据,不利于后续遍历
// 所以改造成无头结点的
if( (*list)->pre == *list ) {// 无玩家,直接退出
return ;
}
Node* t = *list;
*list = (*list)->next;
(*list)->pre = t->pre;
t->pre->next = *list;
free( t );
List die = creatList();
// 开始推人
int cnt = 1;
int isSwitch = 1;//初始为顺时针
while( n > q ){
if( isSwitch ) {
if( cnt == m ){
t = (*list)->next;
push_back( die, (*list)->elem );
erase( *list );
isSwitch = 0;
cnt = 1;
n--;
*list = t;
} else {
cnt++;
*list = (*list)->next;
}
} else {
if( cnt == k ){
t = (*list)->pre;
push_back( die, (*list)->elem );
erase( *list );
isSwitch++;
cnt = 1;
n--;
*list = t;
} else {
cnt++;
*list = (*list)->pre;
}
}
}
while( (*list)->pre->elem < (*list)->elem ){
(*list) = (*list)->pre;
}
// 将头结点插入
t = (Node*)malloc(sizeof(Node));
// 先让 头结点 主动与链表 逻辑链接
t->next = *list;
t->pre = (*list)->pre;
// 再让 链表 与头结点 逻辑链接
(*list)->pre->next = t;
(*list)->pre = t;
// 最后,链表指向头结点
*list = t;
return die;
}
int getLength( List list ){
Node* node = list->next;
int cnt = 0;
while( node != list ){
cnt++;
node = node->next;
}
return cnt;
}
int main(){
int n,m,k,i,q;
printf("第一个游戏的输入:(总人数,间隔,剩余人数)\n");
scanf("%d %d %d",&n,&m,&k);
List list1 = creatList();
List list2 = creatList();
for( i = 1; i <= n; i++ ){
push_back( list1, i );
}
// printf("长度:%d\n",getLength(list1));
// printf("长度:%d\n",getLength(list1));
List die1 = game1( &list1, n, m, k );
printf("题目一:\n");
printf("剩余旅客编号:(%d人,按编号升序 ) ",getLength(list1));
printList( list1 );
printf("离开旅客编号:(%d人按先离开次序) ",getLength(die1));
printList( die1 );
printf("\n");
printf("第二个游戏的输入:(总人数,顺时针间隔,逆时针间隔,剩余人数)\n");
scanf("%d %d %d %d",&n,&m,&k,&q);
for( i = 1; i <= n; i++ ){
push_back( list2, i );
}
List die2 = game2( &list2, n, m, k, q );
printf("题目二:\n");
printf("剩余旅客编号:(%d人,按编号升序 ) ",getLength(list2));
printList( list2 );
printf("离开旅客编号:(%d人按先离开次序) ",getLength(die2));
printList( die2 );
return 0;
}
到了这里,关于约瑟夫死者游戏-C语言实现-双向循环链表的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!