题目:用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至全部输出。
分析:该题目的通用解法是利用循环链表,当然也可以利用循环数组。易于扩展的做法是,定义循环链表的数据结构,并实现循环链表的基本操作,利用这些基本操作如插入、删除、初始化、构建链表等操作;简易的实现方法则是仅适合该题目的解法。
采用无头的循环链表,如果有头,则需要在每次遍历中跳过头节点。因为采用了无头的链表,因此在插入或者删除时,需要针对头节点特殊处理:
1、插入时,链表为空,则当前待插入的节点为链表头;
2、删除时,由于题目只要求输出删除节点,链表又是循环链表,因此可以不考虑删除节点为头节点的情况。不过,为了删除当前节点,我们需要保存其前驱结点的指针。
代码如下:
#include <iostream>
#include "joseph.h"
using namespace std;
void joseph()
{
int m = 0, n = 0;
cin >> n >> m;
Node head = NULL;
Node current = NULL;
for (int i = 1; i <= n; ++i) {
Node tempNode = new ListNode();
tempNode->data = i;
if (head == NULL) { //构建链表,特殊处理头节点
head = tempNode;
head->next = head;
current = head;
} else {
current->next = tempNode;
tempNode->next = head;
current = current->next;
}
}
Node prev = current; //此处有些取巧,我们从链表头开始遍历链表,其前驱应为表尾,链表构建完成过后current指针恰好为表尾,因此先保存该指针
current = head; //再给current赋值,指向表头
int cnt = 1; //初始化计数器,当前节点计数器为1
while (current != current->next) { //循环条件为直至链表剩余一个元素,此时current != current->next
if (cnt == m) {
cnt = 1; //当前节点需要删除,重置计数器
prev->next = current->next; //更改当前节点的前驱结点的后继结点为当前节点的后继
cout << current->data << '\t';
delete current;
current = prev->next; //重新赋值current
} else {
++cnt;
prev = current;
current = current->next;
}
}
cout << current->data << endl; //输出链表最后一个元素
delete current;
}