链接:https://www.nowcoder.com/questionTerminal/f9533a71aada4f35867008be22be5b6e
来源:牛客网
有一个数组a[N]顺序存放0~N-1,要求每隔两个数删掉一个数,到末尾时循环至开头继续进行,求最后一个被删掉的数的原始下标位置。以8个数(N=7)为例:{0,1,2,3,4,5,6,7},0->1->2(删除)->3->4->5(删除)->6->7->0(删除),如此循环直到最后一个数被删除。输入描述:
每组数据为一行一个整数n(小于等于1000),为数组成员数,如果大于1000,则对a[999]进行计算。
输出描述:
一行输出最后一个被删掉的数的原始下标位置。
示例1
输入
8
输出
6
第一种思路【队列实现】:
因为是每隔两个数删除一个数,所以我们可以先将n个数(0到n-1)依次入队列;然后每次依次取两个数出队列,然后重新入队列,再删除队头元素,一直循环,直到队列只剩一个数,取得此时的队头元素 。
//队列实现
private static int deleteNum2(int n) {
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < n; i++) {//1. 依次将n个数入队列
queue.add(i);
}
while (queue.size() != 1) {
int count = 2;
while (count-- != 0) {
int tmp = queue.peek();//此步取得队首元素是为了后面让它重新入队列
queue.poll();//队头元素出队列
queue.add(tmp);//让刚取得的队首元素入队列
}
queue.poll();
}
return queue.peek();//取得此时的队头元素
}
第二种思路【约瑟夫环实现】:
我们可以让n个数构成一个有n个节点的循环链表,然后从第一个节点开始,每隔一个节点删除一个节点,直到循环链表中只剩一个节点,返回此节点的val值。
需要注意的是,为了删除某一个节点,我们需要每走一步都保存一下此节点的前一个节点。
如何判断循环链表只剩一个节点了:当一个节点的next指向自己时表明只剩一个节点了。
//约瑟夫环实现
private static int deleteNum(int n) {
ListNode root = new ListNode(0);
ListNode tail = root;//连接每个节点所用
for (int i = 1; i < n; i++) {//构成一个单链表
tail.next = new ListNode(i);
tail = tail.next;
}
tail.next = root;//让尾节点的next指向第一个节点,以构成循环链表
ListNode node = root;
while (node.next != node) {//如果node.next==node,表明此时循环链表只剩一个节点
ListNode pre = null;//用来记录每个节点的前一个节点,方便删除这个节点
int count = 2;
while (count-- != 0) {
pre = node;//记录node节点的前一个节点
node = node.next;
}
pre.next = node.next;//删除node节点,即让node节点的前一个节点的next指向node节点的next
node = pre.next;//从node节点的下一个节点继续
}
return node.val;
}
最后附上两种方法的完整代码:
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class DeleteNum {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
int n = scanner.nextInt();
if (n > 1000) {
n = 999;
}
System.out.println(deleteNum1(n));
}
}
//约瑟夫环实现
private static int deleteNum1(int n) {
ListNode root = new ListNode(0);
ListNode tail = root;//连接每个节点所用
for (int i = 1; i < n; i++) {//构成一个单链表
tail.next = new ListNode(i);
tail = tail.next;
}
tail.next = root;//让尾节点的next指向第一个节点,以构成循环链表
ListNode node = root;
while (node.next != node) {//如果node.next==node,表明此时循环链表只剩一个节点
ListNode pre = null;//用来记录每个节点的前一个节点,方便删除这个节点
int count = 2;
while (count-- != 0) {
pre = node;//记录node节点的前一个节点
node = node.next;
}
pre.next = node.next;//删除node节点,即让node节点的前一个节点的next指向node节点的next
node = pre.next;//从node节点的下一个节点继续
}
return node.val;
}
//队列实现
private static int deleteNum2(int n) {
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < n; i++) {//1. 依次将n个数入队列
queue.add(i);
}
while (queue.size() != 1) {
int count = 2;
while (count-- != 0) {
int tmp = queue.peek();//此步取得队首元素是为了后面让它重新入队列
queue.poll();//队头元素出队列
queue.add(tmp);//让刚取得的队首元素入队列
}
queue.poll();
}
return queue.peek();//取得此时的队头元素
}
}
class ListNode {
int val;
ListNode next;
public ListNode(int val) {
this.val = val;
}
}
了解完按固定间隔删数之后,假如现在有另外一种需求:
有一个数组a[N]顺序存放0~N-1,要求第一次间隔一个数删掉一个数,第二次间隔两个数删掉一个数,第三次间隔三个数删掉一个数,...... ,到末尾时循环至开头继续进行,求最后一个被删掉的数的原始下标位置。以10个数为例:{0,1,2,3,4,5,6,7,8,9};0->1(删除)->2->3->4(删除)->5->6->7->8(删除)->9->0->1->2->3(删除)。如此循环,直到最后一个数被删除。
假如输入为10,则输出为9.
有了上边按固定间隔删数数的思路之后,在解答这道题的时候,我们只需要每次把删除的间隔改改就行啦。
完整源代码如下:
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Delete {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
while (scanner.hasNext()){
int n=scanner.nextInt();
System.out.println(delete(n));
}
scanner.close();
}
private static int delete1(int n) {
ListNode root = new ListNode(0);
ListNode tail = root;//连接每个节点所用
for (int i = 1; i < n; i++) {//构成一个单链表
tail.next = new ListNode(i);
tail = tail.next;
}
tail.next = root;//让尾节点的next指向第一个节点,以构成循环链表
ListNode node = root;
int count=0;
while (node.next!=node){//如果node.next==node,表明此时循环链表只剩一个节点
count+=1;//每次删除数之前改变间隔
int c=count;//因为每次删数时间隔都会变,所以我们需要用一个数来记录它,以此让节点往后走间隔步
ListNode pre=null;
while (c--!=0){
pre=node;//记录node节点的前一个节点
node=node.next;
}
pre.next = node.next;//删除node节点,即让node节点的前一个节点的next指向node节点的next
node = pre.next;//从node节点的下一个节点继续
}
return node.val;
}
private static int delete(int n) {
Queue<Integer> queue=new LinkedList<>();
for(int i=0;i<n;i++){
queue.add(i);
}
int count=0;
while (queue.size()!=1){
count+=1;//每次删除数之前改变间隔
int c=count;//因为每次删数时间隔都会变,所以我们需要用一个数来记录它,以此让队列出入元素
while (c--!=0){
int tmp = queue.peek();//此步取得队首元素是为了后面让它重新入队列
queue.poll();//队头元素出队列
queue.add(tmp);//让刚取得的队首元素入队列
}
queue.poll();
}
return queue.peek();
}
}
class ListNode {
int val;
ListNode next;
public ListNode(int val) {
this.val = val;
}
}