题目描述:
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
思路分析:
看到转圈循环,首先就想到用循环链表来解决问题。
所以先创建一个节点类,然后根据小朋友的个数生成对应的节点数,并将他们首尾连接起来。
生成链表以后,根据题意来分析,对于每次点到的节点将会剔除出该循环链表,所以需要用两个指针来记录需要剔除的节点以及该节点的上一个节点pre,并将上一个节点的Next指针指向该节点的下一个节点,完成剔除工作。
以此类推,对于循环链表而言,循环终结的终点是对于最后一个剔除的节点,其前一个节点的next指针必然会指向自己,所以循环结束标志就是pre.next = pre;
代码实现如下:
//先尝试链表的方式来解决问题
//循环结束的标志是当前节点的下一个指针指向自己
public class Solution {
public int LastRemaining_Solution(int n, int m) {
if( n < 1 || m < 1){
return -1;
}
if(n==1){
return 0;
}
//先形成一个循环链表
LinkNode head = new LinkNode(0);
LinkNode next = null;
LinkNode cur = head;
for(int i = 1 ; i < n ; i++ ){
next = new LinkNode(i);
cur.next = next;
cur = next;
if( i== n-1){
next.next = head;
}
}
//需两个指针分别来记录当前位置和前一个的位置,
cur = head;
LinkNode pre = next;
while(pre.next != pre){
int count = m;//计数器
while(count-1 > 0){//当不符合条件时,节点依次往后推进。
pre = cur;
cur = cur.next;
count--;
}
//剔除对应的节点
pre.next = cur.next;
cur = cur.next;
}
return pre.val;
}
//创建链表的节点类
public class LinkNode{
int val;
LinkNode next = null;
LinkNode(int val){
this.val = val;
}
}
}