高质量代码
摘自《剑指Offer》,读者根据自己笔试在OJ平台答题中遇到的常见问题进行总结,分为一下3部分
1、规范性
2、完整性
3、鲁棒性
代码完整性
从3个方面确保代码完整性
1. 功能测试
是否能正确处理得到结果,考虑常见细节 double 不能用等于判断
2. 合法的输入
面对异常输入 ,如何告诉代码异常
3. 边界测试
例如:
最大正数,最大负数,0等
循环或递归的边界条件,base case是否考虑正确
解决办法
3种错误处理方式
1.函数用返回值来告知调用者是否出错
这种方式最大的问题是使用不便,因为函数不能直接把计算结果通过返回值赋值给其他变量,同时也不能把这个函数计算的结果直接作为参数传递给其他函数。(不知道为什么这么说?有朋友解释一下吗?)
2.当错误发生时设置 一个全局变量
这种方法比第一种方法使用起来更加方便,因为调用者可以直接把返回值复制给其他变量或者作为参数传递给其他函数。但是这个方法有一个问题:调用者很容易忘记检查全局变量,因此在调用出错的时候忘记进行相应的错误处理,从而留下安全隐患。
3.异常
我们可以根据不同的出错原因定义不同的异常类型。因此,函数的调用者根据异常的类型就能知道出错的原因,从而做出相应的处理。在抛出异常的时候,程序的执行会打乱正常的顺序,对程序的性能有很大的影响。
案例题目描述
求数的n次方
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0
package NowCoderOffer;
//快速幂
public class Power {
public double Power(double base, int exponent) throws Exception {
if(base<=0.000001 && exponent <0){
throw new Exception();
}
//定义了 0^0 为1
if (exponent == 0){
return 1.0;
}
if (exponent <0 ){
base = 1/base; //注意
exponent *= -1;
}
double ans = powerN(base,exponent);
return ans;
}
public double powerN(double base , int e){
// double res = 1.0;
// for (int i =0; i<e ;i++ ){
// res *=base;
// }
// return res;
// }
if(e == 0){
return 1.0;
}
if (e == 1){
return base;
}
double help = powerN(base,e>>1);
help *=help;
if ((e&1) ==1){
help *=base;
}
return help;
}
}
//错误方法
class Solution {
public double Power(double base, int exponent) {
//没有考虑 base == 0 n<0 的情况
if(base<=0.000001){
return 0.0;
}
if (exponent == 0){
return 1.0;
}
if (exponent <0 ){
base = 1/base;
exponent *= -1;
}
double ans = powerN(base,exponent);
return ans;
}
public double powerN(double base , int e){
double res = 1.0;
for (int i =0; i<e ;i++ ){
res *=base;
}
return res;
}
}
打印1到n位的最大数
输入是整数n,且没有限定n的取值范围,使用数组来表示一个大数
package NowCoderOffer;
import sun.security.util.Length;
public class Print1toMaxOfDigits {
public static void print1woMaxOfDigits(int n){
if (n<=0){return;}
char[] numbers = new char[n];
for (int i=0 ; i<10 ;i++){ //个位开始 后续递归的显示后面的值
numbers[0] = (char) (i + '0');
print1woMaxOfDigitsrecursive(numbers,0);
}
}
public static void print1woMaxOfDigitsrecursive(char[] numbers , int n){
if (n == numbers.length-1){
printNumber(numbers);
return;
}
for (int i = 0; i<10 ;i++ ){
//递归别用n++,否则要恢复
numbers[n+1] = (char)(i +'0');
print1woMaxOfDigitsrecursive(numbers,n+1);
}
}
//去除0操作!
public static void printNumber(char[] numbers){
boolean isBegin0 =true;
for (int i = 0 ; i<numbers.length ;i++){
if (isBegin0 && numbers[i]!='0'){
isBegin0 = false;
}
if (!isBegin0){
System.out.print(numbers[i]);
}
}
System.out.println();
}
public static void main(String[] args) {
// char i = '0';
// System.out.println( (char) (i+1)); //test
print1woMaxOfDigits(2);
}
}
字符串匹配
请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配
package Offer;
//我的思维误区
//没有考虑到递归性 等价的小规模问题,就应该意识到是递归
//分类时的情况整合!
public class stringMatch {
public static boolean match (String str, String pattern) {
// write code here
if(str == null || pattern == null){
return false;
}
return matchCore(str,pattern,0,0);
}
//递归
public static boolean matchCore(String str, String pattern,int iS, int iP){
//base case
if(iS == str.length() && iP == pattern.length()){
return true;
}
//注意不是任意一个到达底端就可以
if(iP == pattern.length() && iS!=str.length()){
return false;
}
//如果下一个字符为*
if(iP+1 <pattern.length() && pattern.charAt(iP+1) == '*'){
if (iS == str.length()){
return matchCore(str , pattern , iS,iP+2);
}
//匹配上
if( (pattern.charAt(iP) == '.'&& str.length()>iS)|| ( str.length()>iS &&str.charAt(iS) == pattern.charAt(iP))){
//或者向下走 , 原地停留, 忽略
return matchCore(str,pattern,iS+1,iP+2) || matchCore(str,pattern,iS+1,iP)||matchCore(str,pattern,iS,iP+2);
}else {
//不匹配上
return matchCore(str,pattern,iS,iP+2);
}
}else{
//匹配上
if((pattern.charAt(iP) == '.' && str.length()>iS)|| ( str.length()>iS &&str.charAt(iS) == pattern.charAt(iP))){
return matchCore(str,pattern,iS+1,iP+1);
}else {
//不匹配
return false;
}
}
}
public static void main(String[] args) {
System.out.println(match("aaa", "aaa."));
}
}
1、没有考虑到递归性
2、设计的分类没有那么简略
3、测试案例想的比较好
string: aaa
pattern:
1、a*a
2、*.*
3、aaa.
4、aaaa*
测试用例是经验之谈,需要考虑极端情况!
注意:很有可能是出现异常了(如空指针,超出范围之类),此时网页不会报错!,考虑的不是逻辑,是边界!
每日总结
今天学习到了, 面试时需要和面试官交流,
1、到底时空间优先,还是时间优先,
2、允不允许改变原始数据,允不允许添加额外的空间,
3、最后揣测期望的时间、空间复杂度大概时多少(最后优化时再说)
4、通过简例子来展现思路
不增加额外空间且能减少移动步骤的合并,见P55,从尾到头的移动数据