当前位置: 首页 > news >正文

算法之不定期更新(一)(2018-04-12)

前言

从三月份到现在,大大小小笔试了十几家公司(主要是因为一直solo code,没人内推),然后也能感觉到自己的进步把。从编程题只能ac一题到后来的ak。今天面腾讯的时候,面试官还一度怀疑我专门去刷了腾讯的笔试题。因此,我想开始做算法,也就是大家都知道的leetcode啦。其实真的蛮有意思的,建议前途未卜的在校大学生都可以去试一下。。算法的确有他独特的魅力。这个专栏我打算加进一块是,把我见到的有意思的算法题给拿出来,跟大家分享交流。

题目

input: n
output: 一个可以被1到n的所有整数整除的最小整数,
        由于整数过大,输出这个整数对987654321取余的结果

这里给同学提个醒。。再往下直接就是我写得解题思路了,希望大家可以先自己思考一下这个问题。



















解题思路

我这里先给出一个,我跟别人交流之后,感觉是可以继续发展的想法:

  • 先求12的最小公倍数a1, 然后求a13的最小公倍数a2,依次类推最后求出的就是一个可以被所有数整除的最小整数

但是这个方法最大的问题就在于,我们求两个数的最小公倍数的时候,用到的方法非常麻烦,具体大家可以某度质因数分解之类的方法。

然后我在做这个题的时候,其实也用到了类似质因数分解,只是其实我们可以更好的利用到因数这一个特性。

我用一个比较小的例子来说明我的思想吧:
下文题到的因数列表的意思是,恰好能够构成整数的因数的集合

有同学说我因数列表没说明白,那我在这举个例子,12 = 2 * 3 * 2,那么[2, 3, 2]就是他的因数列表

  • n = 1的时候,这个最大整数要可以被1整除就行,那么这个数就是1,因数列表是[1]
  • n = 2的时候,这个最大整数要可以被1, 2整除,那么这个数就是1 * 2 = 2,因数列表是[1, 2]
  • n = 3的时候,这个最大整数要可以被1, 2, 3整除,那么这个数就是1 * 2 * 3 = 6,因数列表是[1, 2, 3]

看到这里其实还看不出什么,但是接下来就是重头戏了

  • n = 4的时候,这个最大整数要可以被1, 2, 3, 4整除,这时候我们发现,n = 3的时候求出来的6包含了因数[2, 3],而2又恰好是4的因数,那么其实可以发现,这个新的最小整数只要在旧的最小整数6的基础上增添一个新的因数,让4也可以在最小整数的因数列表里面找到所有的因数,那么也就是,我们在原本的因数列表里加入一个新的因数4 / 2 = 2(4 / 2 中的 2 来自 6 的因数列表里的 2),也就是新的因数列表是[1, 2, 3, 2],此时的最小整数是1 * 2 * 3 * 2 = 12

看到这里,我相信大家已经可以明白我的思路了,解决问题的方法就是,求输入为n的最小整数,也就是要在输入为n-1的最小整数的因数列表里面找出n的因数,然后把最后没有找出来的因数给加入到因数列表里面。

而寻找因数的过程可以归结如下:

  1. list // 因数列表, index // 因数列表下标索引,用于循环
  2. k = n / list[index], 如果 k 是个整数,说明list[index]n的一个因数,那么n = k,也就是说,找到了一个因数之后,我们下次要找因数的n就没有那么大了,毕竟已经有一个因数了。
  3. 如果k不是一个整数,说明list[index]不是n的因数,不做任何处理
  4. index++

好了,现在我们可以求出输入为n的时候的因数列表了。

一个新的问题来了,计算机没办法表示出这个因数列表的乘积,我们要怎么求出它对987654321的余数呢。答案就是 ((a % c) * (b % c)) % c = (a * b) % c。在这个题里,也就是,我们不断用result乘因数列表里的数的时候,我们就result = result % 987654321,可以在保持结果不变的情况下减少result的值,乘完因数列表里所有的数后的result就是结果

代码

我一个切图仔。。还是js写得舒服一点,用其他语言实现的话,其实理解了我的解题思路应该不难。(by the way)动态数组是真的好用嘻嘻嘻。

 function solution (n) {
     const list = [] // 因数列表
     let result = 1 // 结果
     for (let i = 1; i <= n; i++) { // 依次在不同的n值时的list添加新的因数
         let newFactor = i // 这个新的因数初始为i

          // 在list中寻找i的因数,并且减小newFactor
         for(let j = list.length; j >= 0; j--) {
             if (newFactor === 1) {
                // 此时的i可以被list中若干个数相乘得到
                 break
             }
             let item = list[j]
             if (newFactor % item === 0) { // 如果这个数可以被list[j]整除
                 newFactor /= item // 缩小newFactor
             }
         }
         if (newFactor !== 1) { // 如果这个因数还有剩余部分
             list.push(newFactor) // 把剩余部分添加进list
 
             // 并且把因数乘入result
             result = (result * newFactor) % 987654321
         }
     }
    
    return result
 }

附上测试图把:

图片描述

那么这个算法题就到这了,如果大家又什么好的想法或者我的有什么问题,欢迎在讨论区和我交流(虽然我知道你们都不想看这又臭又长的)




更好的解法

在评论区里,@JMC_给出了效率更高的解法,本来想补上他的思路的,发现我的文笔有限,说不清楚,大家可以看他的代码JMC_的解法C++版

JMC_的原话是:

刚测试了一下你的代码,发现你这个时间复杂度是O(n*n)。其实算1-n的最小公倍数的话,只要算1-n中的质数的贡献就可以了,每个质数p的贡献就是p的最大幂(小于等于n ),然后将所有的贡献累乘起来就是答案了,这样时间复杂度就会降成O(n)。

我用javascript重写了一下,大家可以用node运行一下,然后自己模仿程序运行的过程应该就可以理解了。

function work (n) {
    const isprime = [] // 判断一个数是否是质数
    const prime = [] // 质数列表
    let result = 1
    for (let i = 2; i <= n; i++) {// 一开始我们认为每一个数都可能是自身的幂
        isprime[i] = i
    }
    for (let i = 2; i <= n; i++) { //线性筛
        if (isprime[i] == i) { //i为质数
            prime.push(i)
            result = (result * i) % 987654321
        }
        for (let j = 0; i * prime[j] <= n; j++) { // 遍历质数列表
            if (isprime[i] === prime[j]) { // i * prime[j]为质数的幂
                isprime[i*prime[j]] = prime[j]
                result = (result * prime[j]) % 987654321
            }
            else isprime[i * prime[j]] = 0
            if (i % prime[j] == 0) break
        }
        console.log(i)
        console.log('isprime')
        console.log(isprime)
        console.log('prime')
        console.log(prime)
        console.log('\n\n\n')
    }
    return result
}

相关文章:

  • Java一行代码控制shape 优雅的解决 Drawable Shape 文件繁多问题
  • innerWidth outerWidth
  • 共享单车运营方不能“只管生不管养”
  • 多项底层技术发力,我国物联网大规模商用迎来窗口期
  • 概念大热的区块链,有这些值得关注的特性
  • Storm笔记整理(四):Storm核心概念与验证——并行度与流式分组
  • 公费出书流程
  • centos7 yum安装配置redis 并设置密码
  • 美元汇率 题解
  • MySql查询最近一个月,一周,一天
  • IDC:今年VR+AR市场规模将达到139亿美元
  • CentOS7 安装JDK
  • OSINT系列:威胁信息挖掘ThreatMiner
  • 数据结构化与保存
  • Java 异常基础详解
  • 分享一款快速APP功能测试工具
  • 2017届校招提前批面试回顾
  • ES6 ...操作符
  • Less 日常用法
  • Lsb图片隐写
  • MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇
  • NSTimer学习笔记
  • PHP 使用 Swoole - TaskWorker 实现异步操作 Mysql
  • React Native移动开发实战-3-实现页面间的数据传递
  • Three.js 再探 - 写一个跳一跳极简版游戏
  • 闭包,sync使用细节
  • 程序员该如何有效的找工作?
  • 浮动相关
  • 关于 Cirru Editor 存储格式
  • 如何编写一个可升级的智能合约
  • 使用docker-compose进行多节点部署
  • 由插件封装引出的一丢丢思考
  • 原生js练习题---第五课
  • 怎么将电脑中的声音录制成WAV格式
  • ​​​​​​​Installing ROS on the Raspberry Pi
  • ### Error querying database. Cause: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException
  • #define、const、typedef的差别
  • #我与Java虚拟机的故事#连载09:面试大厂逃不过的JVM
  • (04)odoo视图操作
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (C语言版)链表(三)——实现双向链表创建、删除、插入、释放内存等简单操作...
  • (Oracle)SQL优化技巧(一):分页查询
  • (PHP)设置修改 Apache 文件根目录 (Document Root)(转帖)
  • (更新)A股上市公司华证ESG评级得分稳健性校验ESG得分年均值中位数(2009-2023年.12)
  • (过滤器)Filter和(监听器)listener
  • (一) storm的集群安装与配置
  • (原創) 系統分析和系統設計有什麼差別? (OO)
  • (转)socket Aio demo
  • .java 9 找不到符号_java找不到符号
  • .net CHARTING图表控件下载地址
  • .Net FrameWork总结
  • .net 开发怎么实现前后端分离_前后端分离:分离式开发和一体式发布
  • .net 微服务 服务保护 自动重试 Polly
  • .NET/C# 编译期间能确定的相同字符串,在运行期间是相同的实例
  • .net连接MySQL的方法