问题
有m个好人和m个坏人坐成一个圈,前m个人是好人,后m个人是坏人。现在他们开始循环报数,要求从编号为1的人开始报,如果有人报到k则他必须死亡,而死掉的人下一个人则继续从1开始报数。请求出最小的k使得,所有的坏人都死掉而好人都活下来。1<=m<=12.
分析
用首尾相连的链表可以表示“坐成一个圈”的情况:
//链表的节点声明
struct ListNode {
int val;
struct ListNode* next;
};
// 在尾部插入节点
struct ListNode* insertAtTail(struct ListNode* head, int val) {
struct ListNode* new_node = (struct ListNode*)malloc(sizeof(struct ListNode));
new_node->val = val;
new_node->next = NULL;
if (head == NULL) {
return new_node;
}
struct ListNode* p = head;
while (p->next != NULL) {
p = p->next;
}
p->next = new_node;
return head;
}
int main(){
//创建链表1->2->3->4->5->6
struct ListNode* p = NULL;
int m = 3;
for (int x = 1; x <= 2*m; x++)
{
p = insertAtTail(p, x);
}
//遍历到最后一个节点
struct ListNode* q = p;
while (q->next != NULL) {
q = q->next;
}
//首节点和尾节点相连
q->next = p;
...
}
模拟循环报数:
struct ListNode* getCount(struct ListNode* head,int m,int k) {
struct ListNode* p = head;
struct ListNode* prev = NULL;
int i = 1;
//count记录被删除的节点的个数
int count = 0;
//从首节点开始遍历
while(true) {
//如果报数到k,则删除该节点。
if (i % k == 0) {
count++;
prev->next = p->next;
if (p->val <= m) {
break;
}
free(p);
p = prev->next;
//重新从1开始接着报数
i = 1;
}
prev = p;
p = p->next;
i++;
}
printf("\n");
return head;
}
循环k=m+1,m+2,…,2m的情况:
for (int x = m+1; x <= 2*m; x++) {
//复制链表1->2->3->4->5->6
struct ListNode* p2 = NULL;
for (int i = 0; i < 2 * m; i++) {
p2 = insertAtTail(p2, i + 1);
}
q = p2;
while (q->next != NULL) {
q = q->next;
}
q->next = p2;
int k = x;
p = getCount(p, m, k);
p = p2;
}
这种方法是实际的删除,也可以用逻辑删除实现。增加逻辑删除标志位deleteFlag,deleteFlag=0表示未删除,deleteFlag=1表示已删除。