报数问题

  • 报数问题
  • 解答
    • 1.题目要求
    • 2.解题思路
    • 3.代码实现
      • 3.1 java版本
      • 3.2 c版本
    • 4.问题总结

报数问题

时间限制:3000MS
内存限制:589824KB
题目描述
当n个人排成一条直线,从左到右的编号分别为1到n。现在从第1个人开始报数,在报数过程中,如果有人报到m则出列,下一个人将继续从1开始报数,第n个人报完数之后再接着往回报数,即倒数第2个人继续报下一个数,当报到第1个人后,第2个人在接着报数。如此循环,直到只留下一个人为止、
例如当n=2,m=3时,第1个人报1,第2个人报2,接下来第1个人报3,出列,留下第2个人。
当输入n和m时,请问通过(n-1)轮报数后,最后留下的那个人的编号是多少?

输入描述

单组输入。
输入两个正整数n和m,n<=10000,m<=1000。两个正整数之间用空格隔开

输出描述

输出最后留下的那个人的编号

样例输入

5 3

样例输出

1

解答

  从题目描述中的红色部分,可以发现这道题并不是一个循环的报数问题,在头部和尾部并不是循环一直走的,所以不能使用约瑟夫环来解决这个问题,需要使用双向链表来解决这个问题。

1.题目要求

  首先最重要的是读懂题目在干什么。


  这是题目要求的每次输出的值,一定要注意到最后一个人是向前报数,到第一个人时又要向后报数。最后一次输出1,可以明显的看到可以用双向链表解决这个问题。

2.解题思路

  确定了要使用双向链表后,就得如果创建双向链表了,从这个题中可以看出,一定和头结>点与尾结点有密切的联系,所以我直接创建双向链表时,定义了它的头结点 first 和它的尾结点 last ,然后这样就比较容易的判断到它是否到了头和尾,对于删除哪个结点,这个比较简单,定义一个index变量,在遍历循环时候一直累加,如果是输入m的倍数,则这个结点需要删除。删除时针对头结点和尾结点的删除我特意写了两个方法,因为双向链表的头结点与尾结点的删除和其他的节点删除不一样。最重要的是,向前遍历和向后遍历。这里可以设置一个标志位flag。默认时flag为true,flag为true时向后遍历,flag为false时向前遍历。所以如果走到最后一个节点或者删除了最后一个节点时,flag就需要设为flase向前;如果走到第一个节点或者删除第一个节点,flag就需要设为true,向后遍历。

3.代码实现

3.1 java版本

编程环境:IDEA 2018.2.4 x64
JDK版本:java version “1.8.0_144”
思路在代码中的实现都有些注释,看完注释就行。

import java.util.Scanner;/*** @author zzy* @time 2021/8/17* @description 双向链表 解决报数问题*/
public class DoubleLinkList {/*** 头*/private Node first;/*** 尾*/private Node last;public DoubleLinkList(){first = null;last = null;}/*** 从双向链表的尾部插入数据* @param value 添加int型数字*/public void insertLast(int value){Node newNode = new Node(value);//如果头结点为空,直接让头结点等于新的节点if (first == null) {first = newNode;}else {//建立连接last.next = newNode;newNode.previous = last;}//把最后个节点设置为最新的节点last = newNode;}/*** 判断双向链表是否为空* @return 如果头结点为空既双向链表为空,则返回true*/public boolean isEmpty(){return first == null;}/*** 删除头节点* 要去除两个指针,一个指向下个的next ,一个是next的previous指向前面的* @return*/public Node deleteFirst(){if (first == null) {throw new RuntimeException("链表数据不存在");}Node temp = first;if (first.next == null) {last = null;}else {first.next.previous = null;}first = temp.next;return temp;}/*** 删除尾节点* 要去除两个指针,一个指向下个的next ,一个是next的previous指向前面的* @return*/public Node deleteLast(){if (first == null) {throw new RuntimeException("链表数据不存在");}Node temp = last;if (first.next == null) {last = null;//把第一个删除first = null;}else {last.previous.next = null;}last = temp.previous;return temp;}/*** 显示所有的数据*/public void display(){if (first == null) {return;}Node current = first;while(current != null){current.display();current = current.next;}}public static void main(String[] args) {//新建一个双向链表DoubleLinkList linkList = new DoubleLinkList();//输入nScanner scanner = new Scanner(System.in);int n = scanner.nextInt();//按1-n的序号顺序插入链表,模拟n个人的座位for (int i = 1; i <= n; i++) {linkList.insertLast(i);}//设置一个指针指向头结点Node temp = linkList.first;/*m 为叫到m号出列的人index为标志位,判断是不是到了第m号人num为计算删除了多少值,如果num==n-1,那么说明双向链表中只有一个值,需要退出删除的循环flag标志位判断双向链表向前遍历还是向后遍历 false时向前遍历,true向后遍历 默认向后遍历*/int m, index = 0, num = 0;boolean flag = true;//输入m值m = scanner.nextInt();while (true) {index++;//如果temp走到最后一个节点,将flag赋值为false 既向前遍历if (temp == linkList.last) {flag = false;}else if(temp == linkList.first){flag = true;}if (index % m == 0) {System.out.printf("删除的值: ");temp.display();if (temp == linkList.last) {//如果待删除的节点为尾结点,则删除尾结点linkList.deleteLast();flag = false;} else if (temp == linkList.first) {//如果待删除的节点为头结点,则删除头结点linkList.deleteFirst();flag = true;} else {//如果删除的既不是头也不是尾,则安装正常的节点删除temp.previous.next = temp.next;temp.next.previous = temp.previous;}//计算删除节点的个数num++;}//判断当前遍历的方向if (flag) {//如果为true,则向后遍历temp = temp.next;} else {//如果为false,则向前遍历temp = temp.previous;}if (num == n - 1) {//删除的个数num等于N-1时,说明双向链表中,只有一个值break;}}//输出双向链表中的最后一个值System.out.printf("最后剩余的值:");linkList.display();}
}class Node{public Node previous;public Node next;public long data;public Node(long data){this.data = data;}public void display(){System.out.println(data);}
}

3.2 c版本

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>typedef struct line {int data;struct line* prior;struct line* next;
}line;//初始化
line* initLine(line* head, int n);
//显示
void display(line* head);
//删除头节点
line* delHeadNode(line* head);
//删除尾节点
line* delTailNode(line* head);
//找到尾节点
line* findTailNode(line* head);void solution();int main() {solution();return 0;
}void solution() {line* head = NULL;int n; //人数 scanf("%d", &n);head = initLine(head, n);line* temp = head;line* tail = findTailNode(head);   //确定尾节点 int m, index = 0, num = 0;    //index为标志位,判断是不是到了第m号人 num删除的人数 bool flag = true;  // false时向前遍历,true向后遍历 默认向后遍历scanf("%d", &m);while (1) {index++;//如果temp走到最后一个节点,将flag赋值为false 既向前遍历if (temp == tail) {flag = false;//如果temp走到第一个节点,将flag赋值为true 既向后遍历}else if(temp == head){flag = true;}//找到需要删除的值 if (index % m == 0) {printf("删除的值:%d\n", temp->data);if (temp == tail) {//如果待删除的节点为尾结点,则删除尾结点head = delTailNode(head);//重新确定尾节点 tail = tail->prior;//temp指向新的尾节点 temp = tail;//改变遍历方向 flag = false;}else if (temp == head) {//如果待删除的节点为头结点,则删除头结点head = delHeadNode(head);//temp指向新的头节点 temp = head;//改变遍历方向 flag = true;}else {//如果删除的既不是头也不是尾,则正常的节点删除temp->prior->next = temp->next;temp->next->prior = temp->prior;}num++;}if (flag) {temp = temp->next; //向前遍历 }else {temp = temp->prior;}if (num == n - 1) { //向后遍历 break;}}display(head);}line* initLine(line* head, int n) {head = (line*)malloc(sizeof(line));head->prior = NULL;head->next = NULL;head->data = 1;line* list = head;for (int i = 2; i <= n; i++) {line* body = (line*)malloc(sizeof(line));body->prior = NULL;body->next = NULL;body->data = i;list->next = body;body->prior = list;list = list->next;}return head;
}line* delHeadNode(line* head) {if (head == NULL || head->next == NULL) {return NULL;}head->next->prior = NULL;return head->next;
}line* delTailNode(line* head) {if (head == NULL) {return NULL;}line* temp = head;while (temp != NULL) {if (temp->next == NULL) {temp->prior->next = NULL;break;}temp = temp->next;}return head;
}line* findTailNode(line* head) {if (head == NULL) {return NULL;}line* tail = head;while (1) {if (tail->next == NULL) {break;}tail = tail->next;}return tail;
}
void display(line* head) {line* temp = head;while (temp) {if (temp->next == NULL) {printf("%d\n", temp->data);}else {printf("%d ", temp->data);}temp = temp->next;}
}

4.问题总结

  在做这道题时,最开始只想到了在删除最后一个节点和删除第一个节点时让遍历方向改变。没有在普通遍历到第一个节点和最后一个节点时改变方向,所以一直提示空指针异常,也就是指针指飞了。这么一个小小的问题用了一个小时才解决。最开始看到报数问题就想到的是约瑟夫环,循环链表。仔细一读题,才发现不是。所以算法题不能被某个题所约束啊…

烽火算法题 报数问题(不是约瑟夫环)双向链表 Java实现与C实现相关推荐

  1. 【约瑟夫环】Java实现:100个人开始从1开始报数,每当报数到3,报数3的人离开,求最后留下来人的位置。

    [约瑟夫环]Java实现:100个人开始从1开始报数,每当报数到3,报数3的人离开,求最后留下来人的位置. 原创 2017年03月15日 00:21:36 标签: 约瑟夫环问题 1023 问题背景:约 ...

  2. 算法题-报数游戏(java)

    思路: 首先用一个数组来保存同学信息. for(int h=1;h<=n;h++) { a[h]=h; } 其次依照题目,需要一个数来记录此时是正向报数1->n,还是逆向报数n->1 ...

  3. 约瑟夫环双向链表c语言实,双向链表与约瑟夫环代码

    双向链表 //注意:该文件操作的链表为带头结点双向链表,头结点数据为-1 #include #include #include #define OK 1 #define ERROR 0 typedef ...

  4. 左神算法:环形单链表的约瑟夫问题(Java版)

    本题来自左神<程序员面试代码指南>"环形单链表的约瑟夫问题"题目. 题目 据说,著名犹太历史学家 Josephus 有过以下故事: 在罗马人占领乔塔帕特后,39 个犹太 ...

  5. 约瑟夫环问题--java

    有编号从1到N的N个人坐成一圈报数,报到M的人出局,下一位再从1开始,  如此持续,直止剩下一位为止,报告此人的编号X.输入N,M,求出X. 那么问题就是,对于n个人,最后剩下的是谁呢? 对于5个人的 ...

  6. c语言约瑟夫环问题,C++_详解约瑟夫环问题及其相关的C语言算法实现,约瑟夫环问题 N个人围成一圈 - phpStudy...

    详解约瑟夫环问题及其相关的C语言算法实现 约瑟夫环问题 N个人围成一圈顺序编号,从1号开始按1.2.3......顺序报数,报p者退出圈外,其余的人再从1.2.3开始报数,报p的人再退出圈外,以此类推 ...

  7. 字节跳动3月面试遇到的高频算法题

    本文汇总了牛客2021.3.1~2021.3.30 面经考到的Leetcode题目 最终的高频题榜单数据可以在CodeTop题库(https://codetop.cc)查询,支持按部门.岗位分类筛选. ...

  8. python一只青蛙一次可以_python算法题 python123网站单元四题目

    下面向大家介绍几个python算法题. 一:二分法求平方根 1.题目要求为 2.输入输出格式为 3.博主解题的思路 这道题在c语言中是一道经典的题目,可以用循环,或者递归,在这里我们用python来写 ...

  9. 【算法经典】 约瑟夫环问题

    [前言]本文讨论经典算法问题约瑟夫环问题的递归解法. 一.问题描述 作为算法中的经典问题,约瑟夫环问题自诞生以来有各种各样的变种描述,丢手绢.游戏获胜者.圆圈中最后剩下的数字.点名游戏等等,但都是同样 ...

最新文章

  1. python控制手机发短信_python-在python3中使用容联云通讯发送短信验证码
  2. (3)-JSONObject的过滤设置
  3. Consul etcd ZooKeeper euerka 对比
  4. 第一季8:mpp的部署、sample的编译和测试、完整版根文件(包含mpp)制作
  5. 【基因组学】系统发育分析-进化树的相关知识点
  6. Google Earth Engine个人笔记:2 计算植被覆盖度
  7. 计算机组装大赛活动感悟,计算机组装大赛总结参考
  8. Top 50 有趣网站
  9. 服务器自动关闭远程打印服务,实现远程打印的方法:无线网络打印服务器
  10. 机器认知、人机交互、边缘计算……在这里,他们谈论了关于AI的关键议题...
  11. mybatis <where> <choose>标签
  12. 电子设计教程48:流水灯电路-完整电路设计
  13. JavaScript 实现网页截屏五种方法
  14. 电信业Hadoop应用分析
  15. 【STM32】HAL库-备份寄存器(BKP)
  16. LeanCloud 一至二月变化
  17. C++基础2:ASC码中 ‘A’ 和 ‘a’ 分别在什么位置??
  18. DLNA介绍(包含UPnP,2011/6/20 更新)
  19. word表格分开快捷键_在Word 表格的编辑中,快速拆分表格应按快捷键为______。
  20. openwrt上网行为控制_深信服全网行为管理AC重磅发布!

热门文章

  1. 华师的入学计算机测试题,华师期末考试计算机练习题
  2. WordArt怎样生成中文词云?
  3. WhatsApp群发-WhatsApp协议-WhatsApp群控到底是什么?
  4. HTML5历史状态管理history API-pushState/replaceState与popstate事件
  5. MySQL查询增强--多子句查询
  6. 自动摘要生成 tf-idf+doc2vec+句子聚类
  7. c语言中signal函数详细说明--举例
  8. 三大运营商eSIM商用情况
  9. solaris磁带机 tar 备份
  10. 计算机网络无线局域网设计,《计算机网络》网络课程“无线局域网”单元的设计与开发...