约瑟夫(joseph):编号为1,2,。。。,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m值,从他在顺时针方向的下一个人开始重新从1报数,如此下去,直到所有人全部出列为止。试设计一个程序显示出列顺序。
〖基本要求〗:利用单向循环链表存储结构模拟此过程,按照出列的顺序打印出各人的编号。
〖测试数据〗:m的初值为20,密码:3,1,7,2,4,8,6
目录
简洁版
只实现循环链表功能
1.头文件
#include<stdio.h>
#include<stdlib.h>
2.结构体
typedef struct LinkList{
int data;
int table;
struct LinkList* next;
}PNode;
3. 创建空链表(无头)
PNode* creatNullList(void) {
PNode* llist = (PNode*)malloc(sizeof(PNode));
return llist;
}
4.插入操作
void insert_llist(PNode* llist, int n, int a[]) {
PNode* p = llist; //防止第一节点丢失
p->data = a[0]; //将第一节点赋值,不为空(密码)
p->table = 1; //为了可以打印出下标
int i;
for ( i = 1; i < n; i++) { //从第二结点开始循环
PNode* q = (PNode*)malloc(sizeof(PNode));
q->next = llist; //q->next = p;
q->table = i + 1;
q->data = a[i];
p->next = q; //第一节点产生
p = q; //p指针向后移动
}
}
5.打印链表操作
void dump_llist(PNode* llist) {
printf("[1] : %d\n", llist->data); //由于循环链表,while条件限制,
//先打印第一结点
int no = 2;
PNode* pnode = llist->next;
while (pnode != llist) {
printf("[%d] : %d\n", no, pnode->data);
pnode = pnode->next;
no++;
}
}
6. joseph--具体操作+打印出列操作
void joseph_llist(PNode* llist, int value, int sum) {
PNode* p = NULL;
PNode* q = NULL;
p = llist;
while (sum) { //sum == 0 全部出列
int count = 1;
while (count != value) { //循环到报数人结束循环
q = p;
p = p->next;
count++;
}
printf("\n[%d] : %d", p->table,p->data);
value = p->data; //准备继续循环条件
q->next = p->next;
p = q->next; //删除结点,最后free链表,这里没有free
sum--;
}
}
7.主函数
int main() {
PNode* llist = creatNullList();
int a[] = { 3,1,7,2,4,8,6 };
insert_llist(llist, 7, a);
dump_llist(llist);
joseph_llist(llist, 20, 7);
free(llist);
}
8.运行代码-----正确
说一下错误吧:
链表的开始可以有头和无头,上面为无头,下面为有头,链表为循环链表,有头会多算一个结点,将头链表算进去,怎么会打印出正确链表...
由于没有头节点,所以会将第一个结点拿出来单独赋值,打印,注意一下
/*为什么要单独赋值,不单独赋值joseph函数应该有问题*-*,但我没看出来 0^0,
^-^希望你可以找出原因*/
en,细节细节,写博客的时候发现我打印 table 排的不对,又稍加修改,作业更加完美,哈哈。
1. 头文件
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
2. 声明
typedef struct Cirnode {
int data;
struct Cirnode* next;
}CNode,* Link;
//以下两个函数可以简化
//创建空循环链表
Link createNullLink(void) {
Link head = (Link)malloc(sizeof(CNode));
if (head != NULL) {
head->next = head;
}
else {
printf("Out of space");
}
return head;
}
//入列
void insertCNode(Link head, int a[], int n) {
Link rear = head;
for (int i = 0; i < n; i++) {
Link p = (Link)malloc(sizeof(CNode));
p->data = a[i];
p->next = head;
rear->next = p;
rear = p;
}
}
3.创建一个循环单链表
Link newLink(int a[], int n) {
Link head = (Link)malloc(sizeof(CNode));
head->next = head;
Link rear = head;
for (int i = 0; i < n; i++) {
Link node = (Link)malloc(sizeof(CNode));
node->data = a[i];
node->next = head;
rear->next = node;
rear = node;
}
return head;
}
4. 循环单链表的链表数
int length(Link head) {
Link p = head;
int count = 0;
while (p->next != head) {
count++;
p = p->next;
}
return count;
}
5. 打印该链表
void displayNode(Link head) {
Link p = head->next;
while (p != head) {
printf("%d->", p->data);
p = p->next;
}
printf("head \n");
}
6. 删除操作
void deleteCNode(Link head, Link p) {
Link q = head;
while (q->next != p) {
q = q->next;
}
q->next = p->next;
free(p);
}
7. 出列操作
bool joseph_CNode(Link head, int n, int num) { // 链表 第几个人出列 链表有几个人
int count = num;
Link p = head->next; //p开始循环
int m = n; //报m的出列
int value; //获取出列人的值
m = m % count; //从p开始,第m个人出列,排除了链表不够长的情况
for (int i = 0; ; ) {
i++;
if (i >= m) {
break;
}
p = p->next;
}
value = p->data; //获取出列人的值
printf("%d->", value); //打印出列人
deleteCNode(head, p); //删除出列人
count--; //总人数-1;
while (p->next != head) { //使用递归,打印其他的人
return joseph_CNode(head, value, count);
}
}
8. 主函数
int main() {
int a[] = { 3,1,7,2,4,8,6 };
Link head = newLink(a , 7);
displayNode(head);
printf("%d \n", length(head));
joseph_CNode(head, 20, );
printf("head");
}
与上面版本答案不一样,上面版本正确
注:在出列操作for循环中,做了修改,先加一再判断;不然就是报第21号的人出列;
我理解错题意了...以为打印密码,结果是打印下标,下标需要在结构体的时候多建一个变量就可以,我用了table