ZZU数据结构与算法实验_0
问题描述
任给正整数 n n n、 k k k,将 1 1 1~ n n n成环形排列,按顺时针方向从 1 1 1开始计数;记满 k k k时输出该位置上的数字(并从环中删去该数字),然后从下一个数字开始计数,直到所有数字均被输出为止。例如当 n = 10 , k = 3 n=10,k=3 n=10,k=3时输出 3 , 6 , 9 , 2 , 7 , 1 , 8 , 5 , 10 , 4 3,6,9,2,7,1,8,5,10,4 3,6,9,2,7,1,8,5,10,4。试写一算法,对输入的任意正整数 n , k n,k n,k,输出相应的置换
Solve_0
用循环链表模拟整个计数、删数的过程
时间复杂度
O
(
n
⋅
k
)
O(n·k)
O(n⋅k)
代码:
#include<stdio.h>
#include<stdlib.h>
typedef struct Node {
int data;
struct Node* next;
}Node;
Node* Init_List(Node *head) {
Node *p;
head->data = 1;
head->next = NULL;
p = head;
return p;
}
Node* push_back(Node *p, int n) {
for (int i = 2; i <= n; i++) {
Node *r = (Node*)malloc(sizeof(Node));
r->data = i;
r->next = NULL;
p->next = r;
p = r;
}
return p;
}
int main() {
int n, k;
scanf("%d %d", &n, &k);
Node *head, *p, *q, *r;
head = q = r = (Node*)malloc(sizeof(Node));
p = Init_List(head);
p= push_back(p, n);
p->next = head;
p = p->next;
while (p->next != p) {
for (int i = 1; i < k; i++) {
q = p;
p = p->next;
r = p;
}
printf("%d ", p->data);
q->next = p->next;
p = p->next;
free(r);
}
printf("%d", p->data);
return 0;
}
Solve_1
将环从
1
1
1~
n
n
n展开,我们定义一个变量p
,记录当前所在位置,定义一个变量t
,记录当前序列长度,每次删去第
(
p
+
k
−
2
)
%
t
+
1
(p + k - 2) \% t + 1
(p+k−2)%t+1直到删空,我们可以用树状数组储存序列,在树状数组上二分找到序列中第
x
x
x小的那个数,输出后从树状数组上删去即可
时间复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
代码:
#include <stdio.h>
#define int long long
const size_t N = (size_t)1e6 + 5;
int n, k;
int tr[N];
int lowbit(int x) {
return x & -x;
}
void ini() {
for (int i = 0; i <= n; i++) {
tr[i] = lowbit(i);
}
}
void add(int pos, int x) {
for (int i = pos; i < n; i += lowbit(i)) {
tr[i] += x;
}
}
int ffind(int k) {
int ans = 0, now = 0;
for (int i = 20; i >= 0; i--) {
ans += (1 << i);
if (ans >= n || tr[ans] + now >= k) ans -= (1 << i);
else now += tr[ans];
}
return ans + 1;
}
void solve() {
scanf("%d %d", &n, &k);
ini();
int p = 1, t = n;
while(t) {
p = (p + k - 2) % t + 1;
int ans = ffind(p);
add(ans, -1);
printf("%d ", ans);
t--;
}
}
signed main() {
solve();
return 0;
}