约瑟夫环问题

N个人围成一圈顺序编号,从1号开始按1、2、3......顺序报数,报p者退出圈外,其余的人再从1、2、3开始报数,报p的人再退出圈外,以此类推。请按退出顺序输出每个退出人的原序号。

输入格式:

输入只有一行,包括一个整数N(1<=N<=3000)及一个整数p(1<=p<=5000)。

输出格式:

按退出顺序输出每个退出人的原序号,数据间以一个空格分隔,但行尾无空格。

方法一:

        不使用结构体和指针解决,创建固定数组,数组序号+1即每个人的编号

#include
int main() {
    int m = 0, n = 0, j = 1, y = 0;
    scanf("%d %d", &m, &n);
    int peoples[3001] = { 0 };//确定数组最大容量,将所有序号初值赋0,表示未出局
    for (int x = 0; y < m; x++) {//当y=m表示所有编号均被打印,则结束循环
        if (x == m) {//当x等于最后一个人的下一个编号时,将其替换成第一个编号的人;
            x = 0;
        }
        if (peoples[x]) {//若该编号已经退出,则跳过
            continue;
        }
        //若是满足要求的人,打印其编号,再将其值赋1,表示已退出
        if (j % n == 0) {
            peoples[x] = 1;
            if(y==m-1) printf("%d",x+1);
            else printf("%d ", x + 1);
            y++;
        }
        j++;
    }
}

约瑟夫环问题_第1张图片 

约瑟夫环问题_第2张图片

 此方法有着许多缺陷,例如如此创建数组会浪费空间,且每次遍历都需从0到m,最后下来时间复杂度为O(n^2),所以如此方法不是最优解;

方法二:

        使用循环链表解决此问题(在VS上可运行)

#include
#include
//链表创建
typedef struct Node {
	int data;
	struct Node* next;
}Node, * LNode;
//循环链表添加元素
void addnode(LNode* L, int n) {
	LNode p = *L;
	for (int x = 0; x < n; x++) {//n为所需元素个数
		LNode newnode = (LNode)malloc(sizeof(Node));
		newnode->next = NULL;
		newnode->data = x + 1;
		p->next = newnode;
		p = newnode;
	}
	p->next = (*L)->next;
}
int main() {
	LNode T = (LNode)malloc(sizeof(Node));
	int n = 0, m = 0, x = 1;
	scanf("%d %d", &n, &m);
	addnode(&T, n);//调用上方定义的函数
	LNode p = T;
	LNode q;
	//遍历输出每个符合条件的人
	while (T->next != NULL) {
		if (x == m) {
			x = 1;
			//打印并删除被选中的编号
			q = p->next;
			p->next = q->next;
			printf("%d ", q->data);
			free(q);
		}
		x++;
		p = p->next;
	}
}

约瑟夫环问题_第3张图片

使用循环链表实现,既解决了空间浪费问题,又降低了时间复杂度,不需要再遍历已退出的元素.

你可能感兴趣的:(算法,数据结构,c语言)