输入一个数字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背景图片定位让我们模拟写出一个点击选中效果 首先需要有个图片素材,当页面加载的时候是背景图片定位到左图,当我们点击图片的时候,背景图 ...
随机推荐
- ajax 实现三级联动
ajax 实现三级联动,相当于写了一个小插件,用的时候直接拿过来用就可以了,这里我用了数据库中的chinastates表, 数据库内容很多,三级联动里的地区名称都在里面,采用的是代号副代号的方式 比如 ...
- 1588: [HNOI2002]营业额统计
1588: [HNOI2002]营业额统计 Time Limit: 5 Sec Memory Limit: 162 MBSubmit: 9203 Solved: 3097[Submit][Stat ...
- swift -- as / 扩展
一.使用 可选链式 调用代替强制展开 //当声明一个属性时,将属性类型设置为可选类型: 好处: 当可选类型的属性被赋予初始值时,系统调用初始值;当可选类型属性没有赋予初始值时,系统只会调用失败;如果属 ...
- 用Jquery做一个时间日期选择器
今天我们就用Jquery做一个时间日期选择器,当打开网页时,文本框里面显示的是当前的日期,点击文本框可以出现年.月.日的下拉菜单,并且可以选择,会根据年份的选择判断是否是闰年,从而改变二月的天数,闰年 ...
- SQL一次查出相关类容避免长时间占用表(上)
/* server: db: EDI */ -- 以下案例多次查询同一张表,仅有组合条件Name+Direction不同 --可以使用一次查出相关类容避免长时间占用表 USE EDI GO DECLA ...
- Python实现的异步代理爬虫及代理池
使用python asyncio实现了一个异步代理池,根据规则爬取代理网站上的免费代理,在验证其有效后存入redis中,定期扩展代理的数量并检验池中代理的有效性,移除失效的代理.同时用aiohttp实 ...
- 表达式计算 java 后缀表达式
题目: 问题描述 输入一个只包含加减乖除和括号的合法表达式,求表达式的值.其中除表示整除. 输入格式 输入一行,包含一个表达式. 输出格式 输出这个表达式的值. 样例输入 1-2+3*(4-5) 样例 ...
- Java中的常量治理
版权声明:本文为博主原创文章,转载请注明出处,欢迎使劲喷 虽然推崇在java中使用枚举(可查看<Java中的枚举的治理>)来对数据字典及常量进行控制,但是有些时候,我们还是会觉得常量控制更 ...
- cuda编程学习1——hello world!
将c程序最简单的hello world用cuda编写在GPU上执行,以下为代码: #include<iostream>using namespace std;__global__ void ...
- javascript中构造StringBuffer实例
function StringBuffer(){ this.strings = new Array; } StringBuffer.prototype.append=function ...