输入一个数字n 如果n为偶数则除以2,若为奇数则加1或者减1,直到n为1,求最少次数 写出一个函数
题目:
输入一个数字n 如果n为偶数则除以2,若为奇数则加1或者减1,直到n为1,求最少次数 写出一个函数
首先,这道题肯定可以用动态规划来解,
n为整数时,n的解为 n/2 的解加1
n为奇数时,n的解为 (n+1)/2 和 (n-1)/2 的解中较小的解加2
通过这个思路,我们可以自底向上依次计算出n的解,代码如下
public static int getNum(int n) {
if(n<1) {
return 0;
}
int[] res = new int[n+1];
res[0] = 0;
res[1] = 0;
for(int i=2;i<=n;i++) {
if((i&1) == 0) {
res[i] = res[i/2] + 1;
}else {
res[i] = Math.min(res[(i+1)/2], res[(i-1)/2]) + 2;
}
}
return res[n];
}
通过上面的思路可以得到问题的解,但是由于是自底向上依次计算n的解,所以有很多不必要的计算,时间效率和空间效率都不高。
比如,当计算n=100时,如果已经知道n=50的解,那么就可以得出n=100的解,所以n=51到n=99都是没有必要计算的。
如果仍然通过自底向上计算,那么想要忽略51到99这一区间的数字的计算是比较麻烦的,如果是自顶向下计算则容易做到,通过n可以确定只要计算 n/2,(n-1)/2 , (n+1)/2,这三个数就 行,利用递归来做代码如下
public static int getNum2(long n) {
if(n<=1) {
return 0;
} if((n&1) == 0) {
return getNum2(n/2) + 1;
}else {
long a = (n-1) / 2;
long b = (n+1) / 2; return Math.min(getNum2(a), getNum2(b)) + 2;
}
}
递归来做这道题简单明了。
递归也有递归的坏处,首先递归最可能问题就是递归深度的问题,很可能造成栈溢出。虽然对这道题来说,几乎不会出现这个问题,但是在用递归做其他问题的时候一定要考虑到这一点。
至于为什么这道题不会造成栈溢出,自己想吧
所有的递归算法都可以转化成非递归算法,这道题也一样,同样的,递归时还有一个小问题,就是它没有复用子问题的解,对于每个子问题,不管之前是否已经计算过解,都要再重新计算一次,转化成非递归算法时可以一并解决这个问题,代码如下
public static int getNum3(long n) {
MyTask task = new MyTask(n); ForkJoinPool pool = new ForkJoinPool();
int res = pool.invoke(task);
pool.shutdown();
return res;
} static class MyTask extends RecursiveTask<Integer> {
private long number;
private static final Map<Long,Integer> map = new ConcurrentHashMap<>(); public MyTask(long number) {
super();
this.number = number;
} private synchronized void put(Long a,Integer b) {
if(map.containsKey(a)) {
System.out.println("had existed!");
}else {
map.put(a, b);
}
} private Integer get(Long a) {
System.out.println("success");
return map.get(a);
} @Override
protected Integer compute() {
if(number<=1) {
put(number, 0);
return 0;
}else if(map.containsKey(number)) {
return get(number);
} int res = 0;
if((number&1) == 0) {
MyTask task = new MyTask(number / 2); try {
res = task.fork().get() + 1;
put(number, res);
} catch (Exception e) {
e.printStackTrace();
}
return res;
}else {
MyTask task1 = new MyTask((number-1) / 2);
MyTask task2 = new MyTask((number+1) / 2);
try {
int a = task1.fork().get() + 2;
int b = task2.fork().get() + 2; res = Math.min(a,b);
put(number, res);
} catch (Exception e) {
e.printStackTrace();
}
return res;
}
} }
这个解法是将第二种解法的递归算法转化成了非递归算法,同时保存了之前已经计算过的子问题的解,并且用到了java中的fork/join框架,至于为什么要用这个框架,原因是,不用它我不知道怎么把这个递归算法转化成非递归算法,望各路大神指点指点
对于这道题来说,第二种方式最快,第三种方式其次,最后是第一种方式,而且第一种方式计算的n的最大值,也远远小于后两种。
好了,就先写到这,人生的第一篇博文就此诞生!庆祝!虽然写的我自己看了都觉得很烂,但是一步一步来嘛
输入一个数字n 如果n为偶数则除以2,若为奇数则加1或者减1,直到n为1,求最少次数 写出一个函数的更多相关文章
- input验证码框,输入非数字或非12位时,红框提示;每4位加一个空格
以下代码:input验证码框,输入非数字或非12位时,红框提示;每4位加一个空格 //input验证码框,输入非数字或非12位时,红框提示;每3位加一个空格 $(".text"). ...
- 如何写出一个让人很难发现的bug?
程序员的日常三件事:写bug.改bug.背锅.连程序员都自我调侃道,为什么每天都在加班?因为我的眼里常含bug. 那么如何写出一个让(坑)人(王)很(之)难(王)发现的bug呢? - 1 -新手开发+ ...
- 浅谈如何写出一个让(坑)人(王)很(之)难(王)发现的bug
该文章内容来自脚本之家,原文链接:https://www.jb51.net/news/598404.html 程序员的日常三件事:写bug.改bug.背锅.连程序员都自我调侃道,为什么每天都在加班?因 ...
- java————数组 简单写出一个管理系统
数组的特点 1, 数组是一块连续的空间,下标描述空间的位置. 2, 下标从0开始,最大下标为数组长度—1.(*.length-1) 3, 数组元素都是变量.(就是每个下标对应的内容).变量的类型 ...
- 请写出一个超链接,点击链接后可以向zhangsan@d-heaven.com发送电子邮件。
请写出一个超链接,点击链接后可以向zhangsan@d-heaven.com发送电子邮件. <a href=”mailto: zhangsan@d-heaven.com”>发邮件</ ...
- 2019-8-31-C#-如何写出一个不能被其他程序集继承的抽象类
title author date CreateTime categories C# 如何写出一个不能被其他程序集继承的抽象类 lindexi 2019-08-31 16:55:58 +0800 20 ...
- JS函数 编程练习 使用javascript代码写出一个函数:实现传入两个整数后弹出较大的整数。
编程练习 使用javascript代码写出一个函数:实现传入两个整数后弹出较大的整数. 任务 第一步: 编写代码完成一个函数的定义吧. 第二步: 我们来补充函数体中的控制语句,完成函数功能吧. 提示: ...
- C# 如何写出一个不能被其他程序集继承的抽象类
我需要限定某个抽象类只能在我程序集类实现,而不支持其他程序集实现,也就是我需要一个不能被继承的抽象类 在 C# 里面有抽象类和接口,这两个都是期望被继承才能被使用,而抽象类是可以做到只能在自己程序集和 ...
- 用js写出一个漂亮的单选框选中效果
一般的input框比较简单,我们可以用JavaScript配合css背景图片定位让我们模拟写出一个点击选中效果 首先需要有个图片素材,当页面加载的时候是背景图片定位到左图,当我们点击图片的时候,背景图 ...
随机推荐
- 又见Bug
文章转载自「开发者圆桌」一个关于开发者入门.进阶.踩坑的微信公众号 作为一名开发者,如何解决遇到的问题.异常或Bug,是开发者必须要面对的,尽管问题很多,情况复杂,但还是有方法和技巧可寻的. 问题无非 ...
- 【js】性能问题
执行环境和作用域 一.全局变量和局部变量 每个执行环境都有与之关联的变量对象(变量和函数存储在这里),全局执行环境是最外围的执行环境,根据ECMA实现所在的宿主不同,表示的执行环境的对象也不同.在we ...
- KoaHub.js -- 基于 Koa.js 平台的 Node.js web 快速开发框架之koahub-loader
koahub loader Installation $ npm install koahub-loader Use with koa // 1.model loader var model = ...
- 轻松理解JavaScript闭包
摘要 闭包机制是JavaScript的重点和难点,本文希望能帮助大家轻松的学习闭包 一.什么是闭包? 闭包就是可以访问另一个函数作用域中变量的函数. 下面列举出常见的闭包实现方式,以例子讲解闭包概念 ...
- JavaScript tips:数组去重
1.实现目标:数组去重 2.实现思路: (1)创建新数组. (2)遍历原数组,判断当前被遍历元素是否存在于新数组,如果存在于新数组,则判断当前被遍历元素是重复的:如果不存在于新数组,则判断当前被遍历元 ...
- 记一次企业级爬虫系统升级改造(六):基于Redis实现免费的IP代理池
前言: 首先表示抱歉,春节后一直较忙,未及时更新该系列文章. 近期,由于监控的站源越来越多,就偶有站源做了反爬机制,造成我们的SupportYun系统小爬虫服务时常被封IP,不能进行数据采集. 这时候 ...
- Django ORM模型的一点体会
作者:Vamei 出处:http://www.cnblogs.com/vamei 严禁转载. 使用Python的Django模型的话,一般都会用它自带的ORM(Object-relational ma ...
- JVM中堆内存和栈内存的区别
Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间 ...
- Layoutinlater 转
http://blog.csdn.net/guolin_blog/article/details/12921889
- 我的日志文件java logger
操作读取日志文件, 1.使用默认的日志文件,并验证默认级别 public void originalConfig() { Logger logger = Logger.getLogger(Logger ...