zoukankan      html  css  js  c++  java
  • 输入一个数字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的最大值,也远远小于后两种。

      好了,就先写到这,人生的第一篇博文就此诞生!庆祝!虽然写的我自己看了都觉得很烂,但是一步一步来嘛

      

  • 相关阅读:
    实验12——java取整、猜数和猜拳
    实验11——java线程模拟卖票
    实验10—— java读取歌词文件内容动画输出
    实验09——java基于TCP实现客户端与服务端通信
    JavaSE第十四天
    javaSE第十一天
    JavaSE第十天
    JavaSE第十天
    JavaSE第九天
    JavaSE第八天
  • 原文地址:https://www.cnblogs.com/wsss/p/5465359.html
Copyright © 2011-2022 走看看