循环链表实现约瑟夫环
首先我们先上图帮助理解:(环状顺序队列)
top
为队首指针
rear
为队尾指针
- 我们需要注意,顺序队列在判断数组中数据是否存满时,会出现下面情况:
- 当队列里面啥也没有的时候,队列的头指针等于队列的尾指针(
top==rear
); - 而队列里面没有多余空间存储数据的时候,队列的头指针也等于队列的尾指针(
top==rear
);
- 由上我们发现:
- 顺序队列的存储状态不同,但是判断条件相同(
top==rear
)。
-
我们为了区分二者,最简单的解决办法是:牺牲掉数组中的一个存储空间
-
判断数组满员的条件是:尾指针的下一个位置和头指针相遇,就说明数组满了(
top==(rear+1)
) -
循环队列的操作(i. size为顺序队列申请的空间大小 ii.
Q[ ]
为数据集合)
- 入队(移动尾指针
rear
)rear=(rear+1)%size
Q[rear]=入队元素
- ❗注意:队满时不可入队
top=(rear+1)%size
- 出队(移动队首指针
top
)top=(top+1)%size
Q[top]=出队元素
- ❗注意:队空不可出队
top==rear
- 经上分析,我们可以发现队列为空时跳出循环
实现:
#include<stdio.h>
#include<stdlib.h>
void JosephusRing(int *Q,int n,int s){
//为了不影响主方法中所创建的队列,所以令声明两个指向元素并初始化
int top=0,rear=n;
int i;
while(top-rear){
for(i=0;i<s-1;i++){
//(n+1)--因为填充数值是从 1 开始,所以指向元素移动时,队尾指向元素的下一个指向为队首元素的指向,即实际存储空间大小为 n+1
top=(top+1)%(n+1);
rear=(rear+1)%(n+1);
Q[rear]=Q[top];
}
top=(top+1)%(n+1);
printf("%3d",Q[top]);
}
}
int main(void){
int *Q;
int n,s;
//输入存储个数
printf("please input a num:\n");
scanf("%d",&n);
//输入出队元素(第几个)
printf("please input s:\n");
scanf("%d",&s);
//申请存储空间
Q=(int *)malloc((n+1)*sizeof(int));
//为了方便我们填充数值为1到n
for(int i=1;i<n+1;i++) Q[i]=i;
//调用方法
JosephusRing(Q,n,s);
}
输出结果展示:
- 由于上述代码实现的时间复杂度为O(n2)
为了降低时间复杂度,我们可以引入一个计数器cnt
//降低时间复杂度
#include<stdio.h>
#include<stdlib.h>
void JosephusRing(int *Q,int n,int s){
int top=0,rear=n;
int cnt=1;
while(top-rear){
if(cnt-s){
top=(top+1)%(n+1);
rear=(rear+1)%(n+1);
Q[rear]=Q[top];
cnt++;
}else{
top=(top+1)%(n+1);
printf("%3d",Q[top]);
cnt=1;
}
}
}
int main(void){
int *Q;
int n,s;
printf("please input a num:\n");
scanf("%d",&n);
printf("please input s:\n");
scanf("%d",&s);
Q=(int *)malloc((n+1)*sizeof(int));
for(int i=1;i<n+1;i++) Q[i]=i;
JosephusRing(Q,n,s);
}
运行结果同上!