题目:

  输入一个数字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,求最少次数 写出一个函数的更多相关文章

  1. input验证码框,输入非数字或非12位时,红框提示;每4位加一个空格

    以下代码:input验证码框,输入非数字或非12位时,红框提示;每4位加一个空格 //input验证码框,输入非数字或非12位时,红框提示;每3位加一个空格 $(".text"). ...

  2. 如何写出一个让人很难发现的bug?

    程序员的日常三件事:写bug.改bug.背锅.连程序员都自我调侃道,为什么每天都在加班?因为我的眼里常含bug. 那么如何写出一个让(坑)人(王)很(之)难(王)发现的bug呢? - 1 -新手开发+ ...

  3. 浅谈如何写出一个让(坑)人(王)很(之)难(王)发现的bug

    该文章内容来自脚本之家,原文链接:https://www.jb51.net/news/598404.html 程序员的日常三件事:写bug.改bug.背锅.连程序员都自我调侃道,为什么每天都在加班?因 ...

  4. java————数组 简单写出一个管理系统

    数组的特点 1,  数组是一块连续的空间,下标描述空间的位置. 2,  下标从0开始,最大下标为数组长度—1.(*.length-1) 3,  数组元素都是变量.(就是每个下标对应的内容).变量的类型 ...

  5. 请写出一个超链接,点击链接后可以向zhangsan@d-heaven.com发送电子邮件。

    请写出一个超链接,点击链接后可以向zhangsan@d-heaven.com发送电子邮件. <a href=”mailto: zhangsan@d-heaven.com”>发邮件</ ...

  6. 2019-8-31-C#-如何写出一个不能被其他程序集继承的抽象类

    title author date CreateTime categories C# 如何写出一个不能被其他程序集继承的抽象类 lindexi 2019-08-31 16:55:58 +0800 20 ...

  7. JS函数 编程练习 使用javascript代码写出一个函数:实现传入两个整数后弹出较大的整数。

    编程练习 使用javascript代码写出一个函数:实现传入两个整数后弹出较大的整数. 任务 第一步: 编写代码完成一个函数的定义吧. 第二步: 我们来补充函数体中的控制语句,完成函数功能吧. 提示: ...

  8. C# 如何写出一个不能被其他程序集继承的抽象类

    我需要限定某个抽象类只能在我程序集类实现,而不支持其他程序集实现,也就是我需要一个不能被继承的抽象类 在 C# 里面有抽象类和接口,这两个都是期望被继承才能被使用,而抽象类是可以做到只能在自己程序集和 ...

  9. 用js写出一个漂亮的单选框选中效果

    一般的input框比较简单,我们可以用JavaScript配合css背景图片定位让我们模拟写出一个点击选中效果 首先需要有个图片素材,当页面加载的时候是背景图片定位到左图,当我们点击图片的时候,背景图 ...

随机推荐

  1. ajax 实现三级联动

    ajax 实现三级联动,相当于写了一个小插件,用的时候直接拿过来用就可以了,这里我用了数据库中的chinastates表, 数据库内容很多,三级联动里的地区名称都在里面,采用的是代号副代号的方式 比如 ...

  2. 1588: [HNOI2002]营业额统计

    1588: [HNOI2002]营业额统计 Time Limit: 5 Sec  Memory Limit: 162 MBSubmit: 9203  Solved: 3097[Submit][Stat ...

  3. swift -- as / 扩展

    一.使用 可选链式 调用代替强制展开 //当声明一个属性时,将属性类型设置为可选类型: 好处: 当可选类型的属性被赋予初始值时,系统调用初始值;当可选类型属性没有赋予初始值时,系统只会调用失败;如果属 ...

  4. 用Jquery做一个时间日期选择器

    今天我们就用Jquery做一个时间日期选择器,当打开网页时,文本框里面显示的是当前的日期,点击文本框可以出现年.月.日的下拉菜单,并且可以选择,会根据年份的选择判断是否是闰年,从而改变二月的天数,闰年 ...

  5. SQL一次查出相关类容避免长时间占用表(上)

    /* server: db: EDI */ -- 以下案例多次查询同一张表,仅有组合条件Name+Direction不同 --可以使用一次查出相关类容避免长时间占用表 USE EDI GO DECLA ...

  6. Python实现的异步代理爬虫及代理池

    使用python asyncio实现了一个异步代理池,根据规则爬取代理网站上的免费代理,在验证其有效后存入redis中,定期扩展代理的数量并检验池中代理的有效性,移除失效的代理.同时用aiohttp实 ...

  7. 表达式计算 java 后缀表达式

    题目: 问题描述 输入一个只包含加减乖除和括号的合法表达式,求表达式的值.其中除表示整除. 输入格式 输入一行,包含一个表达式. 输出格式 输出这个表达式的值. 样例输入 1-2+3*(4-5) 样例 ...

  8. Java中的常量治理

    版权声明:本文为博主原创文章,转载请注明出处,欢迎使劲喷 虽然推崇在java中使用枚举(可查看<Java中的枚举的治理>)来对数据字典及常量进行控制,但是有些时候,我们还是会觉得常量控制更 ...

  9. cuda编程学习1——hello world!

    将c程序最简单的hello world用cuda编写在GPU上执行,以下为代码: #include<iostream>using namespace std;__global__ void ...

  10. javascript中构造StringBuffer实例

    function StringBuffer(){      this.strings = new Array;  }    StringBuffer.prototype.append=function ...