9-15剑指offer(斐波那契、替换空格)

博客探讨了两种优化斐波那契数列的实现方法,包括递归和动态规划,以及如何降低字符串替换的时间复杂度。递归方法虽然简洁但效率低,动态规划通过数组避免了重复计算。此外,通过减少indexOf调用来改进字符串替换,将时间复杂度降低到O(n)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题源来自:剑指OFFER

剑指Offer_在线编程+题解_牛客题霸_牛客网

斐波那契数列:

        实现方法1:递归

递归的方法最为简洁,但是重复计算过多,并且容易StackOverflow。

package 剑指Offer_斐波那契and替换空格;

/**
 * @program:多线程和IO
 * @descripton:3种实现斐波那契数列  注意在static中不能访问非static的变量
 * @author:ZhengCheng
 * @create:2021/9/15-16:11
 **/
public class Fibb {
    public static void main(String[] args) {
        long l1 = System.currentTimeMillis();
        int i1 = Fib01(45);
        long l2 = System.currentTimeMillis();
        int i2 = Fib02(55);
        long l3 = System.currentTimeMillis();
        int i3 = Fib03(55);
        long l4 = System.currentTimeMillis();
        System.out.println("result1:"+i1+"时间花费"+(l2-l1));
        System.out.println("result2:"+i2+"时间花费"+(l3-l2));
        System.out.println("result3:"+i3+"时间花费"+(l4-l3));
    }
    // 从n=0开始
    //实现1  递归
    public static int Fib01( int n ){
        if (n == 0 || n== 1){
            return n;
        }
        return Fib01(n-1)+Fib01(n-2);
    }
    //实现2  数组实现  动态规划
    public static int Fib02( int n ){
        if (n == 0 || n== 1){
            return n;
        }
        int arr[] = new int[n+1];
        arr[0] = 0;
        arr[1] = 1;
        for (int i = 2; i < arr.length; i++) {
            arr[i] = arr[i-1] + arr[i-2];
        }
        return arr[n];
    }
    //实现3, 对动态规划进行规划,因为其使用了数组,占用空间大了。使得空间复杂度为O(1)
    // 先找出其状态转移方程
    //  sum -> f(n)    a -> f(n-1)    b -> f(n-2)
    public static int Fib03( int n ){
        if (n == 0 || n== 1){
            return n;
        }
        int cnt = 1;
        int  b = 0;
        int  a = 1;
        int sum = 0;
        while (cnt < n){
            sum = a + b;
            //换位置
            b = a;
            a = sum;
            cnt++;
        }
        return sum;
    }
}

替换空格:

        将字符串的空格替换成%20 例如We are happy  输入中并不会存在其他字符。

分析:首先,查看字符串的源码,发现其实现方式是一个final的char[] 。所以字符串对象是引用的数组对象,其值是无法改变的。当我们对字符串进行操作时,我们不难发现,对字符串的操作都会生成一个新的字符串,其地址值是会发生改变的。

故,我们首先想到的暴力解法。其次考虑如何优化,由字符串是需要复制的,故在复制后,进行对字符串的改动。

package 剑指Offer_斐波那契and替换空格;

/**
 * @program:多线程和IO
 * @descripton:替换空格
 * @author:ZhengCheng
 * @create:2021/9/15-16:36
 **/
public class ReplaceKG {
    public static void main(String[] args) {
        String a ="We are happy";
    }
    //暴力解法,直接使用replace API
    public static String reKG01(String str){
        return str.replace("","%20");
        
        /*
        public String replace(CharSequence target, CharSequence replacement) {
        String tgtStr = target.toString();              得到target即要
        String replStr = replacement.toString();        得到repl即我们填充的子串
        int j = indexOf(tgtStr);                        返回被替换的子串在字符串中首次出现的位置
        if (j < 0) {                                    未发现的话,说明没有需要替换的
            return this;
        }
        int tgtLen = tgtStr.length();                   得到target长度
        int tgtLen1 = Math.max(tgtLen, 1);              与1比较长度
        int thisLen = length();                         未修改字符串的长度

        int newLenHint = thisLen - tgtLen + replStr.length();   计算修改后字符串的长度
        if (newLenHint < 0) {
            throw new OutOfMemoryError();
        }
        StringBuilder sb = new StringBuilder(newLenHint);       使用Stringbuild创建字符串
        int i = 0;
        do {
            sb.append(this, i, j).append(replStr);              先将不需要替换的复制,然后加入替换的
            i = j + tgtLen;                                     指针后移
        } while (j < thisLen && (j = indexOf(tgtStr, j + tgtLen1)) > 0);
        return sb.append(this, i, thisLen).toString();
        }
         */
    }
}

 阅读源码后,我们清晰的得到,默认的replace方法,是暴力的。并且源码可知,其时间复杂度为O(n2)。因为首先需要遍历一次,获得长度,其次还需要indexof函数。index的底层使用了coder???   总之,indexOf在使用时还是会遍历,导致时间复杂度的提高成O(n2)。

下面的代码从大体的逻辑上和使用StringBuilder的没太大区别,优化了indexOf过多的遍历次数,使得时间复杂度降低为O(n);空间复杂度O(n)

package 剑指Offer_斐波那契and替换空格;

/**
 * @program:多线程和IO
 * @descripton:替换空格
 * @author:ZhengCheng
 * @create:2021/9/15-16:36
 **/
public class ReplaceKG {
    public static void main(String[] args) {
        String a ="We are happy";
       // System.out.println(reKG01(a));
        System.out.println(reKG02(a));

    }
  
    //降低时间复杂度,不适用indexof的方法。
    public static String reKG02(String str){
        if (str.equals("")){
            return str;
        }
        char[] chars = str.toCharArray();
        //得到空位个数
        int cnt = 0;
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == ' '){
                cnt++;
            }
        }
        //创建新的字符串数组
        char[] chars1 = new char[cnt*2+chars.length];
        //指定两个指针 填充新字符串
        int i =chars.length-1 ;
        int j =chars1.length-1 ;
        while (true){
            if (i<0||j<0){
                break;
            }
            if (chars[i] != ' '){
                chars1[j--] = chars[i--];

            }else {
                chars1[j--] = '0';
                chars1[j--] = '2';
                chars1[j--] = '%';
                i--;
            }
        }
        /*for (int i1 = 0; i1 < chars1.length; i1++) {
            System.out.print(chars1[i1]);
        }*/
        return new String(chars1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值