题目:

  输入一个数字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. Codeblocks快捷键

    Codeblocks快捷键: Ctrl+Shift+Enter:复制控制台exe内容 Ctrl+Z:撤销 Ctrl+F:查找  Ctrl+R:查找替换功能 Ctrl+Shift+C:加注释     C ...

  2. Gradle之恋-任务2

    由于任务作为Gradle的核心功能模块,多花点精力是值得的,本文继上一篇主要涉及到:任务的执行顺序.为任务添加组和描述.跳过任务.扩展任务属性.定义默认任务. 任务顺序 如果构建(build)不能在清 ...

  3. 20170305Meetup Git、heroku drop db

    Rails中,给link_to添加上action和class样式,内部文本 <%= link_to product_path(product),{ :controller => 'cont ...

  4. 2017-03-10 T-sql 语句 高级查询

    T-SQL语句: 创建数据库: 1,点击新建查询,在弹出的页面上进行代码编写.点击可用数据库,编写前确定当前操作的页面是自己想要进行操作的界面. 2,数据库创建语句 Create datebase   ...

  5. 九度OJ题目1208:10进制 VS 2进制 (JAVA)

    题目描述: 对于一个十进制数A,将A转换为二进制数,然后按位逆序排列,再转换为十进制数B,我们乘B为A的二进制逆序数.     例如对于十进制数173,它的二进制形式为10101101,逆序排列得到1 ...

  6. 会话控制之session和cookie(20161107)

    注:除了登录页面,每个页面,包括处理页面也要加,为了提高安全性 session尽量不用,因为很占内存 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML ...

  7. Postman使用教程——调试网络接口的凶器

    postman是谷歌浏览器的一个插件,干什么用的呢?跟题目一样,就是用来调试网络接口的.在我们程序猿做程序的时候,如果做网络应用的开发,比如一些B/S.C/S,我们总会给别人一些网络接口,也会使用别人 ...

  8. java 学习 todoList

    1.并发包的使用 2.线程相关的源码,怎么结束一个线程 3.单例模式代码 4.mixin 相关的理解代码 书单: effective java java 编程思想 spring 编程指南 深入理解jv ...

  9. NodeJs中process.cwd()与__dirname的区别

    process.cwd() 是当前执行node命令时候的文件夹地址 ——工作目录,保证了文件在不同的目录下执行时,路径始终不变__dirname 是被执行的js 文件的地址 ——文件所在目录 Node ...

  10. 基于ELK的数据分析实践——满满的干货送给你

    很多人刚刚接触ELK都不知道如何使用它们来做分析,经常会碰到下面的问题: 安装完ELK不知从哪下手 拿到数据样本不知道怎么分解数据 导入到elasticsearch中奇怪为什么搜不出来 搜到结果后,不 ...