题源来自:剑指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); } }