1、递归和循环
斐波那契数列
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项是1)。
示例1
输入:4
返回值:3
使用递归非常消耗时间和空间,还可能引起栈溢出,故使用循环更优。
public class Solution { public int Fibonacci(int n) { if(n<0||n>39) return -1; if(n==0||n==1) return n; int a=0,b=1; int sum=0; for(int i=2;i<=n;i++) { sum=a+b; a=b; b=sum; } return sum; } }
测试用例
- 功能测试(输入3,5,10等);
- 临界值测试(输入0,1,2);
- 性能测试(输入较大的数字,如40,50,100等)。
2、查找和排序
旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
示例1
输入:[3,4,5,1,2]
返回值:1
public class Solution { public int minNumberInRotateArray(int [] array) { int l=0,h=array.length-1; int m=0; while(l<=h) { if(h-l==1) return array[h]; m=(l+h)/2; //数组中首位中数字相等时,无法确定中间元素是属于前面还是后面的递增子数组 //只能使用顺序查找 if(array[h]==array[m]&&array[l]==array[m]) { lhmEqual(l,h,array); } if(array[h]>=array[m]) h=m; else if(array[l]<=array[m]) l=m; } return 0; } public int lhmEqual(int l,int h,int[] array) { int r=array[l]; for(int i=l+1;i<=h;i++) { if(array[i]<r) r=array[i]; } return r; } }
测试用例
- 功能测试(输入的数组为非递减的旋转数组,有重复的数字或者没有重复的数字);
- 边界测试(输入的数组为非递减数组,只包含一个数字的数组);
- 特殊用例测试(输入为空)。
3、回溯法
矩阵中的路径
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如
[a s a]
[b f d]
[c c e]
[e s e]
矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
public class Solution { public boolean hasPath(char[] matrix, int rows, int cols, char[] str) { boolean[] visited=new boolean[rows*cols]; int pathCount=0; for(int i=0;i<rows;++i) { for(int j=0;j<cols;++j) { if(hasPath(matrix,rows,i,cols,j,str,pathCount,visited)) return true; } } return false; } public boolean hasPath(char[] matrix,int rows,int i,int cols,int j,char[] str,int pathCount,boolean[] visited) { int index=i*cols+j; if (i < 0 || i >= rows || j < 0 || j >= cols || matrix[index] != str[pathCount] || visited[index]) return false; if(pathCount==str.length-1) return true; boolean f=false; if(i>=0&&j>=0&&i<rows&&j<cols&&matrix[index]==str[pathCount]&&!visited[index]) { pathCount++; visited[index]=true; f=hasPath(matrix,rows,i+1,cols,j,str,pathCount,visited) ||hasPath(matrix,rows,i-1,cols,j,str,pathCount,visited) ||hasPath(matrix,rows,i,cols,j+1,str,pathCount,visited) ||hasPath(matrix,rows,i,cols,j-1,str,pathCount,visited); if(!f) { pathCount--; visited[index]=false; } } return f; } }
测试用例
- 功能测试(在多行多列的矩阵中存在或者不存在路径);
- 边界值测试(矩阵只有一行或者一列;矩阵和路径中所有字符都是相同的);
- 特殊输入测试(输入为空)。
机器人的运动范围
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
public class Solution { public int movingCount(int threshold, int rows, int cols) { int count=0; boolean[] visited=new boolean[rows*cols]; for(int i=0;i<cols*rows;i++) visited[i]=false; count=numCount(threshold,rows,cols,0,0,visited); return count; } //统计数量 public int numCount(int threshold,int rows,int cols,int i,int j,boolean[] visited) { int count=0; int index=i*cols+j; if(i>=0&&j>=0&&i<rows&&j<cols&&!visited[index]&&check(i,j)<=threshold) { visited[index]=true; count=1+numCount(threshold,rows,cols,i+1,j,visited) +numCount(threshold,rows,cols,i-1,j,visited) +numCount(threshold,rows,cols,i,j+1,visited) +numCount(threshold,rows,cols,i,j-1,visited); } return count; } //判断是否有资格进入方格 public int check(int i,int j) { int sum=0; while(i>0) { sum+=i%10; i/=10; } while(j>0) { sum+=j%10; j/=10; } return sum; } }
测试用例
- 功能测试(方格为多行多列;k为正数);
- 边界值测试(方格只有一行或者一列;k为0);
- 特殊输入测试(k为负数)。
4、动态规划与贪婪算法
剪绳子
给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1,m<=n),每段绳子的长度记为k[1],...,k[m]。请问k[1]x...xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
输入描述:输入一个数n,意义见题面。(2 <= n <= 60)
返回值描述:输出答案。
示例1
输入:8
返回值:18
//动态规划 public class Solution { public int cutRope(int target) { int[] r=new int[target+1]; r[2]=2; r[3]=3;//疑惑,不是说m>1吗 for(int i=4;i<=target;i++) { int max=0; for(int j=2;j<=i/2;j++) { r[i]=r[j]*r[i-j]; if(r[i]>max) max=r[i]; } r[i]=max; } return r[target]; } } //贪婪算法 import java.lang.*; public class Solution { public int cutRope(int target) { int numOf3=0; int reOf3=0; numOf3=target/3; reOf3=target%3; int s=1; if(reOf3==0) s=(int)Math.pow(3,numOf3); else if(reOf3==1) s=(int)Math.pow(3,numOf3-1)*4; else s=(int)Math.pow(3,numOf3)*2; return s; } }
测试用例
- 功能测试(绳子长度>=5);
- 边界值测试(绳子长度为0,1,2,3,4)。
5、位运算
二进制中哦个1的个数
输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示。
示例1
输入:10
返回值:2
分析:在计算机系统中,数值一律用补码来表示和存储。正数的原码、反码、补码都是其本身。也就是说,根本就不需要考虑数的符号问题。
//解法1 public class Solution { public int NumberOf1(int n) { int sum=0; while(n!=0){ sum+=n&1;//逐个判断低位是否为1; n=n>>>1;//无符号右移,例如从11101变成1110 } return sum; } } //解法2 //把一个整数减去1,再和原整数做与运算,会把该整数最右边的1变成0。 //那么一个整数的二进制中有多少个1,就可以进行多少次这样的操作。 public class Solution { public int NumberOf1(int n) { int sum=0; while(n!=0){ sum++; n=n&(n-1); } return sum; } }
测试用例
- 正数(包括边界值1、0x7FFFFFFF);
- 负数(包括边界值0x80000000、0xFFFFFFFF);
- 0。