经典算法题收集一

2023-11-11

1、题目一

    有 n个人围成一圈,顺序排号。从第一个人开始报数(从 1 到 3 报数),凡报到 3 的人退出圈子, 问最后留下的是原来第几号的那位。

    其实这是一个约瑟夫环问题,这个问题最本质其实就是循环链表的问题,围成一个圈之后,报数即是循环链表的删除操作。

    通常在没有想到很好的方法的时候,我们即会以最常用的思维去解决问题,所以最普通的方法如下:

1.1、解法一

import java.util.Calendar;
import java.util.Scanner;
​
public class YsfTest {
​
    public static void main(String[] args) {
        
        while(true){
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入游戏进行的人数:");
            int n = sc.nextInt();
            long start = Calendar.getInstance().getTimeInMillis();
            System.out.println(way1(n,3));
            long end = Calendar.getInstance().getTimeInMillis();
            System.out.println(end-start);
        }
    }
    
    public static String way1(int n,int m){
        boolean[] arr = new boolean[n];
        for (int i = 0; i < arr.length; i++) {
            arr[i]= true;
        }
        int leftCount = n; //从左到右剩下的人数
        int countNum = 0; //计数器
        int index = 0; //报数人数
        int ret = 0; //最后剩下的人的位置
        if(leftCount==1){//如果一开始就只有一个人
            ret = 1;
        }else{
            while(leftCount>1){//如果左到右一直都大于1个人就需要报数
                if(arr[index]){
                    countNum++;
                    if(countNum==m){
                        countNum = 0;//计数器归0
                        arr[index] = false;
                        leftCount--;
                    }
                }
                index++; //结算循环次数
                if(index==n){ //数组长度不会变,到了最后一个人要重置重写开始报
                    index = 0;
                }
            }
        }
        for (int i = 0; i < arr.length; i++) {
            if(arr[i]==true){
                ret = i+1;
            }
        }
        return Integer.toString(ret);
    }
    
}

    这个思路就是创建一个数组,数组中报到对应数的设置为false,即被淘汰的,当只剩下最后一个人,也就是只有一个true了。这种解法的效率如何呢?

1.2、解法二

前面说到,这本质就是一个链表,所以呢,我们用链表的方式去实现。

import java.util.Calendar;
import java.util.Scanner;
​
public class YsfTest2 {
    public static void main(String[] args) {
        while(true){
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入游戏进行的人数:");
            int n = sc.nextInt();
            long start = Calendar.getInstance().getTimeInMillis();
            CreatLink link =new CreatLink();
            link.creatLink(n);
            System.out.println(link.playGame(3));
            link = null;
            long end = Calendar.getInstance().getTimeInMillis();
            System.out.println("耗时:"+(end-start));
        }
    }
}
​
​
class CreatLink{
    Node head;
    class Node{
        int data;
        Node next;
        public Node(int data){
            this.data=data;
        }
    }
    //创建循环链表
    public void creatLink(int sum){
        Node temp=new Node(1);
        head=temp;
        for (int i = 2; i <=sum; i++) {
                temp.next=new Node(i);
                temp=temp.next;
        }
        temp.next=head;
    }
    
    //开始游戏
    public String playGame(int num){
        Node temp=head; //从第一个人开始
        //出列的节点
        Node removeNode;
        //循环链表中当只剩一个节点时,该节点的引用指向自己
        while(temp.next!=temp){
            for (int i = 2; i <num; i++) {
                temp=temp.next;
            }
            removeNode=temp.next;
            temp.next=temp.next.next;
            temp=removeNode.next;
        }
        return Integer.toString(temp.data);
    }
}

    这种解法,注意可能创建对象过多,会出现:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space。我们可以设置一下Default VM参数。最终看到的运行效率如下:

1.3、解法三

    对于上面两种解法,无论是数组、还是链表,都要频繁的去查询、删除,这样效率其实是很低的。所以,聪明的人就会找规律。

    假设:我们有10个人,[0,1,2,3,4,5,6,7,8,9],数到3的人被踢出去

    第一次:踢掉了2,我们记作f(10,3),那么后面继续的话从3开始,还有[3,4,5,6,7,8,9,10%10,11%10]

    第二次:踢掉了5,此时还有9个数我们记作f(9,3),同理还有[6,7,8,9,0,1,3,4]

    第三次:踢掉了8,此时还有8个数我们记作f(8,3),同理还有[9,0,1,3,4,6,7]

    如果在踢第二次的时候,我们将其重新编号:

    [3,4,5,6,7,8,9,10%10,11%10]

    [0,1,2,3,4,5,6,7,8],我们发现,其实差值就是等于我们数的数字。

    [0+3,1+3,2+3,3+3,4+3,5+3,6+3,(7+3)%10,(8+3)%]

    因此,继续推理可以得出:

    f(10,3) = (f(9,3)+3)%10

    f(9,3)=(f(8,3)+3)%9

    f(8,3)=(f(7,3)+3)%8

    ...

    f(2,3) = (f(1,3)+3)%2

    当执行到倒数第二次的时候,就只剩下一个人了。

public class YsfTest3 {
    public static void main(String[] args) {
        while(true){
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入游戏进行的人数:");
            int n = sc.nextInt();
            long start = Calendar.getInstance().getTimeInMillis();
            System.out.println(way3(n,3));
            long end = Calendar.getInstance().getTimeInMillis();
            System.out.println("耗时:"+(end-start));
        }
    }
    
    public static String way3(int n,int m){
        int ret = 0;
        for (int i = 2; i < n; i++) {
            ret = (ret+m)%i;
        }
        return Integer.toString(ret+1);
    }
}

    我们再来看下这种解法的效率,数量越大和前面的两种方式差距也越大,效率设置达到之前的10倍。

2、题目二

    我们经常会碰到计算一个某个字符串在原字符串中出现的次数问题。这种问题提供以下几个思路参考。

public class StringCountNum {
    
    public static void main(String[] args){
        String s = "acbaaabadadasjajcaaaakkkadakadajaka";
        String test_s = "aa";
        System.out.println(way1(s,test_s));
        System.out.println(way2(s,test_s));
        System.out.println(way3(s,test_s));
    }
    
    public static String way1(String s1,String s2){
        int count = 0; 
        Pattern p = Pattern.compile(s2); 
        Matcher m = p.matcher(s1); 
        while (m.find()) { 
           count++; 
        } 
        return count+"";
    }
    
    public static String way2(String s1,String s2){
        int count = 0;
        while(s1.indexOf(s2)>=0) {
            s1 = s1.substring(s1.indexOf(s2)+s2.length());
            count++;
        }
        return count+"";
    }
    
    public static String way3(String s1,String s2){
        int count2 = 0;
        count2 = (s1.length()-s1.replace(s2, "").length())/s2.length();
        return count2+"";
    }
    
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

经典算法题收集一 的相关文章

随机推荐

  • 线性回归算法(二)-- 最优解与损失函数

    介绍 要理解最优解和损失函数 我们需要先弄明白什么是误差 以简单线性回归为例 如下图所示 青色数据样本为真实值 y y y 直线上同一 x x x位置的红色样本点为预测值
  • qt操作第三方软件

    QT控制第三方软件方法 背景需求 实现思路 获取句柄方法 QT通过获取的信息操作 例子 控件ID为0或者控件ID和操作句柄相同怎么办 得到窗体x y height width 模拟键盘鼠标操作 附录 键值对照表 背景需求 通过前辈们写的软体
  • Shell--基础--06--传递参数

    Shell 基础 06 传递参数 1 介绍 我们可以在执行 Shell 脚本时 向脚本传递参数 1 1 脚本内获取参数的格式 格式为 n n 代表一个数字 0 执行的文件名 1 为执行脚本的第一个参数 2 为执行脚本的第二个参数 以此类推
  • AI时代,重新理解阿里云

    如果说 在数字化时代 阿里云给外界的标签是基于算力 数据等要素的基建角色 那么 在如今的智能化时代 基于自身强大的云计算能力和长期以往的AI技术积累 它的这种底座底色显然再一次被夯实 彰显 作者 皮爷 出品 产业家 宜昌城东大道 左侧是中国
  • DirectX编程:利用 DirectSound 录音

    DirectX编程 利用 DirectSound 录音 转载 http www cnblogs com stg609 archive 2008 10 24 1318931 html 花了一阵子 把DirectX安装后自带的帮助文件中的那部分
  • ES时间分组统计查询

    创建索引 PUT test 索引结构 PUT test mapping properties insertTime type date id type text fields keyword type keyword ignore abov
  • halcon之Blob分析实战

    Blob分析 Blob Analysis 在计算机视觉中的Blob是指图像中的具有相似颜色 纹理等特征所组成的一块连通区域 Blob分析 Blob Analysis 是对图像中相同像素的连通域进行分析 该连通域称为Blob 其过程其实就是将
  • 【数据结构】栈

    文章目录 1 栈的概念及结构 2 栈的实现 2 1栈的实现思路 2 2概念理解题 2 3栈的结构体定义 2 4函数接口 功能 2 5头文件Stack h 2 6函数实现Stack c 2 7测试函数Test c 2 8有效的括号 利用栈实现
  • Oracl之动态Sql编写总结

    一 概述 在通常的sql操作中 sql语句基本上都是固定的 如 SELECT t empno t ename FROM scott emp t WHERE t deptno 20 但有的时候 从应用的需要或程序的编写出发 都可能需要用到动态
  • 【抽样技术】CH2 简单随机抽样补充——比率估计与回归估计

    目录 一 概述 1 问题的提出 2 比率估计与回归估计的作用和使用条件 3 辅助变量的特点 4 相关符号 二 比率估计量 编辑 编辑 1 问题的提出 2 定义 3 比估计与简单估计的比较 4 比率估计的思想 5 比率估计量及其性质 1 引理
  • 给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。

    import java util Arrays 问题 顺时针螺旋输出数组 题目特征 保持一种模式前进 遇到一定条件转换另一种模式前进 思路 用一个二维数组来不同取值来控制前进 设置变动的边界为改变的条件 public class Test1
  • [1160]ModuleNotFoundError: No module named setuptools_rust

    报错信息 Traceback most recent call last File line 1 in File tmp pip build my9sai1o cryptography setup py line 14 in from se
  • 复现iis 文件解析漏洞

    1 开启IIS服务 开始 管理工具 Internet信息服务 IIS 管理器 2 点击Internet信息服务 IIS 管理器 查看自己web网站中的文件和IIS服务的开始 停止 暂停按钮 查看IIS服务是否开启 若未开启请开启 HLY 本
  • 如何查看某个端口被占用

    查看8080端口是否被占用可以使用如下命令 Windows netstat ano find 8080 Linux netstat ano grep 8080 netstat命令详解 Netstat用于显示与IP TCP UDP和ICMP协
  • IPFS: NAT traversal(NAT穿越)

    IPFS是一个p2p网络 那么一定绕不开的一个问题就是NAT穿越 之前的文章里面也提到过IPFS网络连通性使用的ICE NAT穿越框架 本文简单介绍一下什么是NAT 为什么有NAT技术 NAT主要用来缓解全球的IPv4地址不够用的情况 IP
  • springboot整合rabbitMq(未完成)

    1 下载安装如下软件 erlang语言和rabbitmq服务 2 配置环境变量 erl安装目录 bin rabbit安装目录 sbin 3 安装插件 打开cmd窗口 进入sbin的cmd窗口 输入rabbitmq plugins enabl
  • python3 学习笔记(三):函数与模块

    python3 学习笔记 python 优雅 明确 简单 函数与模块 1 函数 组织好的 可重复使用的 用来实现某一功能的代码段 1 定义 def function x pass 其中 def为关键字 function 为函数名 x为形参
  • C语言基础知识--weak关键字

    目录 一 C语言弱函数定义 weak关键字 1 weak关键字简介 2 weak关键字使用示例 二 总结 一 C语言弱函数定义 weak关键字 1 weak关键字简介 使用 attribute weak 修饰函数 告诉编译器此函数为同名函数
  • 修改服务器404页面,Apache服务器404页面设置具体步骤

    在网站运营过程中 由于某些原因有时候要对网页进行删除 也就意味以后用户或者用户访问删除页面都是访问不了的 如果用户和搜索引擎访问错误页面返回值不是404的话 那么就很不友好 大大增加跳出率 404页面就是引导用户从错误的页面访问到正确的页面
  • 经典算法题收集一

    1 题目一 有 n个人围成一圈 顺序排号 从第一个人开始报数 从 1 到 3 报数 凡报到 3 的人退出圈子 问最后留下的是原来第几号的那位 其实这是一个约瑟夫环问题 这个问题最本质其实就是循环链表的问题 围成一个圈之后 报数即是循环链表的