1. 圆圈中最后剩下的数字

0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。

例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。

  1. 示例 1
  2. 输入: n = 5, m = 3
  3. 输出: 3
  4. 示例 2
  5. 输入: n = 10, m = 17
  6. 输出: 2
  7. 限制:
  8. 1 <= n <= 10^5
  9. 1 <= m <= 10^6

思路:

  • 关键是找到对应索引位置
  • 将一个组数视作环处理

    • 通过索引取余操作
    • 为了防止数组越界,就需要保持取余的那个数的变化
      1. class Solution {
      2. public int lastRemaining(int n, int m) {
      3. // 初始化
      4. List<Integer> list = new ArrayList<>();
      5. for(int i=0;i<n;i++){
      6. list.add(i);
      7. }
      8. int cur = 0;
      9. int size = n;
      10. //
      11. while(size!=1){
      12. int index = (cur+m-1)%size;
      13. list.remove(index);
      14. cur = index;
      15. size--;
      16. }
      17. return list.get(0);
      18. }
      19. }
  • 时间过于长, 需要优化

  • 尝试通过数组进行优化,因为list进行remove是一个数组重建的过程,耗时过长
  • 尝试使用了环形链表去解决,也是效率低
    class Solution {
      public int lastRemaining(int n, int m) {
          // 初始化
          ListNode head = new ListNode(0);
          ListNode cur = head;
          for(int i=1;i<n;i++){
              ListNode node = new ListNode(i);
              cur.next = node;
              cur = cur.next;
          }
          cur.next = head;
          // 执行n-1次
          // ListNode temp = head;
          for(int j=0;j<n-1;j++){
              for(int k=0;k<m-1;k++){
                  cur = cur.next;
              }
              cur.next = cur.next.next;
          }
          return cur.val;
      }
    }
    

优秀解法: