对于此问题,可以先手动写出前几个月的兔子数量,来观察其总数增长规律。前10个月的的总数为:1、1、 2、3、5 、8 、13、21、34、55,从这些数字来看,发现其规律就是从第3个数字开始,每个数字的值等于前面紧邻的两个数字的和,所以这其实就是一个斐波那契数列。
既然看出了这题目的本质是斐波那契数列,那编程实现就简单了。最常规的做法是递归,当月份为1或2时,总数为1,其他月份等于前面两个月数量相加。示例代码如下:
public static int f(int month){
if(month==1 || month==2)
return 1;
else
return f(month-1)+f(month-2);
}
该问题除了可以用递归形式实现,还可以用循环方式实现。用循环实现的,我们只要用两个变量来表示所求的月份的前两个月的数量,然后不断迭代更新这两个月份的值,再相加这两个月份的值,也能求出该月份的兔子数量。示例代码如下:
public static int num(int month){
int temp = 0;
int month1num = 1;
int month2num = 1;
for(int i =3;i < month;i++){
temp = month2num;
month2num = month2num +month1num;
month1num = temp;
}
return month2num;
}
要解决该问题,先了解什么是素数。素数一般指质数,质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。
既然是求素数,也就是说求满足如下要求的一些数:这些数在除以1和本身之外的数字时,余数不为0即可。最常见的思路是双层for循环,挨个除从2到小于该数字-1,用一个boolean型变量标识该数字是否是素数,是的话则输出。示例代码如下:
private static void primeNumber(){
for(int i=101;i<200;i++){
boolean isResult=true;
for(int j=2;j<i;j++){
if(i%j == 0){
/*代表该数字i不是素数*/
isResult=false;
}
}
if(isResult){
/*经过内层for循环的筛选后的,都是素数*/
System.out.print(i+" ");
}
}
}
关于在内层循环判断素数的逻辑,其实也可以不用boolean变量来实现。该方法没上面的那个方法那么容易想到,当除数从2到被除数-1全试完后,其实也就是说明这个数是个素数。此时,内层循环的控制变量j经历了j++,其值也被除数i是相等的!也就是说,将是否是素数的判断逻辑从内层for循环转移到了外层for循环。示例代码如下:
private static void primeNumber(){
int j=0;
for(int i=101;i<200;i++){
for(j=2;j<i;j++){
if(i%j == 0){
/*代表该数字i不是素数*/
break;
}
}
if(j==i){
/*当j和i相等时,这个数就是素数*/
System.out.print(i+" ");
}
}
}
上面的实现方式,能输出正确的答案,但却存在着许多不必要的计算。这个不是只代码方面的漏洞,而是数学方面的知识,即除数的选择范围,从2-被除数开方即可。示例代码如下:
private static void primeNumber(){
int j=0;
int k=0;
for(int i=101;i<200;i++){
k=(int)Math.sqrt((double)i+1);
for(j=2;j<=k;j++){
if(i%j == 0){
/*代表该数字i不是素数*/
break;
}
}
if(j==(k+1)){
/*当j和i相等时,这个数就是素数*/
System.out.print(i+" ");
}
}
}
从题目可以看出,水仙花数是三位数,所以考虑的范围从101-999即可。之所以从101开始,是因为三位数的起始数字100明显不是水仙花数。该功能的实现比较简单,就是求出百、十、个位数上的数字,然后再进行相关运算,和原数字比较即可。示例代码如下:
private static void narcissus(){
for(int i =101;i<1000;i++){
/*百位数字*/
int hundredNum=i/100;
/*十位数字*/
int tenNum=i/10%10;
/*个位数字*/
int oneNum=i%10;
if(i == hundredNum*hundredNum*hundredNum+tenNum*tenNum*tenNum+oneNum*oneNum*oneNum){
System.out.println(i+" ");
}
}
}
该问题中,被除数固定,除数从2到自身(到自身是因为要考虑质数的存在),然后逐个除以每个除数,当余数不为0时,除数自增,这就是大致的解题思路。示例代码如下:
private static void decompose(int num){
for(int i=2;i<=num;){
if(i==num){
/*当num和k相等的时候,代表已经分解到了被除数自身,输出n的值,结束循环*/
System.out.println(num);
break;
}else if(num%i==0){
System.out.print(i+"*");
/*n除以i的商,作为新的正整数num,继续分解*/
num = num/i;
}else{
/*如果num不能被i整除,则用i+1作为i的值*/
i++;
}
}
}
同时,很多能用循环解决的问题,用递归也能解决,本问题也不例外,只是将被除数/i的时候,调用自身方法即可。示例代码如下:
private static void decompose(int num){
for(int i=2;i<=num;i++){
if(i==num){
/*当num和k相等的时候,代表已经分解到了被除数自身,输出n的值,结束循环*/
System.out.println(num);
break;
}else if(num%i==0){
/*如果num != i,但n能被i整除,则应打印出i的值*/
System.out.print(i+"*");
/*n除以i的商,作为新的正整数num,继续分解*/
decompose(num/i);
break;
}
}
}
Java中的条件运算符,也就是三元运算符,语法形式为: 布尔表达式 ? 表达式1 :表达式2,如果布尔表达式的值为 true ,则返回表达式1 的值,否则返回 表达式2 的值。从这个例子就可以看出,三元运算符其实可以看做if语句的一种简写,理解了这个,这个题目的实现就简单了。示例代码如下:
private static void getGradeLevel(int grade){
System.out.println("该成绩对应的等级为:"+(grade>=90?'A':(grade>=60?'B':'C')));
}
该问题的求解思路是先求最大公约数,假设数字m和n的最大公约数是a,则最小公倍数可以通过m*m/a来求出。对于最大公约数的求解,常用的辗转相除法。具体思路为:用两个数中的较大数除以较小的数,求出一个余数,然后将余数赋值给较小的数,将原来较小的数,赋值给较大的数,知道较小的数变成0为止,此时较大的数就是最大公约数。示例代码如下:
private static int commonDivisor(int bigNum,int smallNum){
/*假设m为较大的数,n为较小的数,此处不再进行参数校验*/
while(smallNum!=0){
int temp=smallNum;
/*将此轮相除的余数作为下一轮的较小值*/
smallNum=bigNum%smallNum;
/*此轮的较小值作为下一轮的较大值*/
bigNum=temp;
}
return bigNum;
}
该问题的思路是将一个字符串拆分,然后判断拆分后的字符是属于哪个类别,在判断哪个类别时,有几种常用的方式:用ASCII码比较、字符比较和使用正则表达式进行匹配。本质上前两种方式是一样的,所以可简单认为有两种方式实现该功能。
第一种是使用ASCII码值进行比较,ASCII码本质上来说是一种数字表示字符的办法,如下:
该实现方式,直接与对应的ASCII码值区间比较,如判断某个字符是否属于数字,直接与数字0和数字9比较就行。示例代码如下:
private static void judgeStr(String string){
int number = 0,letter = 0,space = 0,other = 0;
char[] chars=string.toCharArray();
for (char c:chars){
/*1:直接字符比较*/
//if ('0'<=c&&c<='9'){
// number++;
//}else if (('a'<=c&&c<='z')||('A'<=c&&c<'Z')){
// letter++;
//}else if (' '==c){
// space++;
//}else {
// other++;
//}
/*2:使用ASCII码值进行比较*/
if (48<=c&&c<=57){
number++;
}else if ((97<=c&&c<=122)||(65<=c&&c<90)){
letter++;
}else if (32==c){
space++;
}else {
other++;
}
}
System.out.print("字符个数:"+letter+" 空格个数:"+space+" 数字个数:"+number+" 其他个数:"+other);
}
第二种方式是使用正则表达式来匹配,这种方式在正式算法中用的也较多,因为在判断某个字符串是否合法等方面,使用正则表达式比直接字符比较方便的多。正则表达式使用时,需要定义一个匹配规则,如接下来的"[a-zA-Z]",表示的意思是从小写字母a到和小写字母z的集合与从大写字母A到和大写字母Z的集合的并集,即所有字母,然后再用一个字符串str去匹配这个规则,如果能匹配上,则返回true,否则返回false。示例代码如下:
private static void judgeStr(String string){
int number = 0,letter = 0,space = 0,other = 0;
/*字母*/
String expression1 = "[a-zA-Z]";
/*数字*/
String expression2 = "[0-9]";
/*空格*/
String expression3 = "\\s";
String[] strings=string.split("");
for(String s:strings){
if (s.matches(expression1)){
letter++;
}else if (s.matches(expression2)){
number++;
}else if (s.matches(expression3)){
space++;
}else {
other++;
}
}
System.out.println("字符个数:"+letter+" 空格个数:"+space+" 数字个数:"+number+" 其他个数:"+other);
}
此问题的关键在于第n个数怎么计算,计算出第n个数后,直接与前面的数累加就行。第n个数的求解也不难,就是前一个数字*10,再加上自身,然后累加1到n,即可求出答案。示例代码如下:
/*exp:每个位数上的数字,即例子中的a;num代表求多少个数字的和*/
private static int sum(int exp,int num){
int sum = exp;
int temp = exp;
for(int i=1;i<num;i++){
temp = temp*10;
/*第i趟,temp就是该趟中第i+1个数字的值*/
temp = temp+exp;
/*此时的总和等于前面的值加上第i+1个数字的值*/
sum = sum+temp;
}
return sum;
}
此问题的求解也比较容易理解,就是通过相除求余,然后将当余数等于0时,所有的除数相加,与原来的被除数比较即可。示例代码如下:
private static void completeNum(){
for(int i=2;i<1000;i++){
int sum=0;
for(int j=1;j<i;j++){
if(i%j == 0){
sum=sum+j;
}
}
if(sum==i)
System.out.print(sum+" ");
}
}
对于该问题的求解,可以采用以下思路:将每次下落–>弹起当做一个阶段,这样考虑的话,第10次落地时经过的总距离就是前九个阶段的距离和+第十次下落的距离。第十次反弹的距离等于第十次下落距离的一半。所以在实现该功能时,至少需要两个变量:总距离和,每次下落/弹起的距离。示例代码如下:
private static void fall(int num){
/*下落的总距离*/
int sum=0;
/*每次下落的高度*/
int high=100;
for(int i=1;i<=num;i++){
sum=sum+high;
high=high/2;
}
System.out.println("第"+num+"次下落时的总距离:"+sum);
System.out.println("第"+num+"次下落后反弹的高度:"+high);
}
该问题主要考察for循环的使用,其解决思路是对百、十、个位,分别生成1-4的数字,然后在组成数字时判断一下,去除不同位数上相等数字的情况。示例代码如下:
private static int generateNum(){
int count=0;
for(int i=1;i<5;i++){
for(int j=1;j<5;j++){
for(int k=1;k<5;k++){
/*判断百、十、个位上的数字均不相等*/
if(i!=j && j!=k && k!=i){
count++;
System.out.println(i*100+j*10+k);
}
}
}
}
return count;
}
该问题主要考察多重if语句的使用,其解决思路是对输入的利润进行判断,将利润划分到不同的区间,这样不同的区间会计算出不同的奖金,将这些不同区间的奖金加起来,就是最后的奖金总额。示例代码如下:
private static double profit(double num){
double result=0.0;
if(num<=10){
result=num*0.1;
}else if(num<=20){
result=10*0.1+(num-10)*0.75;
}else if(num<=40){
result=10*0.1+10*0.75+(num-20)*0.05;
}else if(num<=60){
result=10*0.1+10*0.75+20*0.05+(num-40)*0.03;
}else if(num<100){
result=10*0.1+10*0.75+20*0.05+20*0.03+(num-60)*0.015;
}else{
result=10*0.1+10*0.75+20*0.05+20*0.03+40*0.015+(num-100)*0.01;
}
return result;
}
要解此问题,需要了解什么是完全平方数,完全平方数指的是一个数可以由某个数字的平方计算得到。解该问题需要考虑两个方面的问题:
1>怎么保证这个数字能够完全开方?在Math中有和方法sqrt是求某个数的开方的,当某个数的开方%1结果为0时,其实就代表了这个数字是个整数。
2>结果的取值范围,该题并没有指定结果一定要是正整数,所以有可能不是正整数,直观上来看,我们可以从-100开始,进行遍历计算。
示例代码如下:
private static void printNum(){
for(int i=-100;i<10000;i++){
if((Math.sqrt(i+100)%1 == 0)
&& Math.sqrt(i+100+168)%1 == 0){
System.out.println(i+" ");
}
}
}
这道题目其实也是在考查多重条件下的编程能力,不过此题中的多重条件月份是整数,此时就可以使用switch…case…来替换if语句。具体到此题,有两种解法:
1>将每月的之前的月份的所有日期进行累加,这样最后再加上当月的天数,就能求出最终答案。示例代码如下:
private static int dayNum(int year,int month,int day){
int result=0;
switch(month){
case 1:
result=0;
break;
case 2:
result=31;
break;
case 3:
result=59;
break;
case 4:
result=90;
break;
case 5:
result=120;
break;
case 6:
result=151;
break;
case 7:
result=181;
break;
case 8:
result=212;
break;
case 9:
result=243;
break;
case 10:
result=273;
break;
case 11:
result=304;
break;
case 12:
result=334;
break;
}
/*如果是闰年,则2月是29天,需要在原来默认的28天基础上+1*/
if((year%4==0 && year%100!=0) || year%400==0){
if(month>=3){
result++;
}
}
/*再加上当月的天数*/
result=result+day;
return result;
}
2>上一种写法能实现功能,但是却不够好,因为需要计算出月份累加后的天数,这种较为复杂的计算尽量扔给计算机进行实现就行,因此,就有了第二种实现方式。就是我们给出每个月的天数,让程序自动累加。示例代码如下:
private static int dayNum(int year,int month,int day){
int result=0;
int monthDay=0;
for(int i=1;i<=month;i++){
switch(month){
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
monthDay=31;
break;
case 2:
monthDay=28;
break;
case 4:
case 6:
case 9:
case 11:
monthDay=30;
break;
}
result=result+monthDay;
}
/*如果是闰年,则2月是29天*/
if((year%4==0 && year%100!=0) || year%400==0){
if(month>=3){
result++;
}
}
/*再加上当月的天数*/
result=result+day;
return result;
}
此处考察的是排序逻辑(暂不考虑线性排序算法),最容易想到的就是需要比较两趟的挨个排序,也就是冒泡排序。第一趟比较时将最大的值放在最后一位,第二趟比较时将最小值放在第一位,排序就完成了。因为这三个数组不是在一个序列(数组或链表中),所以就不用常见的双层for循环来完成该题目,直接用几个if语句来实现就行,如果元素过多的话,就需要考虑双层for循环了。示例代码如下:
private static void sort(int x,int y,int z){
int temp=0;
if(x>y){
temp=x;
x=y;
y=temp;
}
if(y>z){
temp=y;
y=z;
z=temp;
}
/*程序运行到此处,最大值已经处于z位置*/
if(x>y){
temp=x;
x=y;
y=temp;
}
System.out.println("排列后的三个数是:"+x+","+y+","+z);
}
该题考的是双层for循环的使用,也就是当一个题目中有两个相关的变量,且需要进行自增、自减等遍历操作时,往往用双层for循环来解决问题。在此题中,外层for循环时前一个乘数,内层for循环是第二个乘数。示例代码如下:
for(int i=1;i<=9;i++){
for(int j=1;j<=i;j++){
System.out.print(i+"*"+j+"="+i*j+" ");
}
System.out.println();
}
}
该题目考的是同样变化条件下的处理,该类问题一般有递归和非递归两种处理方式, 该题目比较简单,就用非递归的方式实现,从题目可以看出,循环体总共执行了9次,也就是需要创建一个能执行9次的for循环。示例代码如下:
private static int countNum(){
int num=1;
for(int i=10;i>1;i--){
num=(num+1)*2;
}
return num;
}
该题考的是如何在for循环中,正确地使用条件判断。此题的判断条件有如下几个,在for循环中进行规避即可:
1>a不和z比。
2>当c不和x、z比,隐含的条件是:c只能和y比,所以a、b都不能和y比。
3>这个条件是隐含最深的条件,需要对每个安排过赛程的人,进行重复性判断,不让一个人和多个人比。
示例代码如下:
private static void printList(){
char[] teamA={'a','b','c'};
char[] teamB={'x','y','z'};
char tempA='d';
char tempB='d';
for(int i=0;i<teamA.length;i++){
for(int j=0;j<teamB.length;j++){
/*题目要求:a不和z比*/
if(teamA[i]=='a' && teamB[j]=='x'){
continue;
/*题目要求:c不和x、z比*/
}else if((teamA[i]=='c' && teamB[j]=='x') || (teamA[i]=='c' && teamB[j]=='z')){
continue;
/*题目要求:当c不和x、z比时,只能和y比,所以a、b都不能和y比*/
}else if((teamA[i]=='a' && teamB[j]=='y') || (teamA[i]=='b' && teamB[j]=='y')){
continue;
}else{
/*此处是为了防止出现一个人重复和多人比的情况*/
if(tempA!=teamA[i] || tempB==teamB[j]){
System.out.println(teamA[i]+" VS "+teamB[j]);
tempA=teamA[i];
tempB=teamB[j];
}
}
}
}
}
该题目依然在考双层for循环的使用,在使用双层for循环时,一般外层循环变量控制次数/趟数,内层循环控制具体的业务逻辑。本题也是一样的,将7层图形分成4和3两部分,所以用了两个外层for循环,在内层for循环的处理上,有不同的方式。
1>在前4层,每层的图形分为三个部分,*前面的空格、和后面的空格。在第一层,*前面的空格有三个,和次数的关系是4-i,为1个,和次数的关系是2n-1,*后面的空格,直接随循环输出即可,所以前4层示例代码:
/*打印出前4行*/
for(i=1;i<=4;i++){
/*打印出每排*前面的空格*/
for(int k=1; k<=4-i;k++)
System.out.print(" ");
/*打印出每排中间的*/
for(j=1;j<=2*i-1;j++)
System.out.print("*");
/*打印出每排*后面的空格*/
System.out.println("");
}
后3层的处理也是类似,分别找出每层中三部分与次数的关系,不再赘述,示例代码如下:
for(i=4;i>=1;i--){
for(int k=1; k<=5-i;k++)
System.out.print(" ");
for(j=1;j<=2*i-3;j++)
System.out.print("*");
System.out.println("");
}
2>变换循环的起始数字,也能达到输出该图形的效果,只要保证每层三个部分内容的正常打印就行。示例代码如下:
/*高和宽必须是相等的奇数*/
int H = 7, W = 7;
for(int i=0; i<(H+1)/2; i++){
for(int j=0; j<W/2-i; j++)
System.out.print(" ");
for(int k=1; k<(i+1)*2; k++)
System.out.print('*');
System.out.println();
}
for(int i=1; i<=H/2; i++) {
for(int j=1; j<=i; j++)
System.out.print(" ");
for(int k=1; k<=W-2*i; k++)
System.out.print('*');
System.out.println();
}
该问题和问题10有些相似,都是在每次循环中先求出一个数,在求出总和或总积,做这类题时,单个的数好球,要注意的是起始变量和循环次数的控制(即边界)。从该数列可以看出,每个数后一个数的分母是前一个数的分母和分子相加,由此就可以求出前二十个数,进而求出他们的和。示例代码如下:
private static double sum(int num){
double sum=0.0;
int num1=1;
int num2=2;
int tempNum=0;
for(int i=1;i<=20;i++){
/*这里要注意,num2/num1后要转换成double类型,要不默认是int型,造成计算结果变小*/
sum=sum+(double)num2/num1;
tempNum=num2;
num2=num2+num1;
num1=tempNum;
}
return sum;
}
该题和之前的有些题相似,都是在每次循环时,求出一个数,然后求他们的和或积。示例代码如下:
private static int sum(int num){
int sum=0;
int temp=1;
for(int i=1;i<=20;i++){
temp=temp*i;
sum=sum+temp;
}
return sum;
}
这道题和前一道题类似,不过要求用递归完成。使用递归也不难,因为在上道题的解法中有个在循环中反复使用乘法和加法的操作,把这些在for循环中重复的操作拿出来,放到一个方法中,每次调用一下就是递归。示例代码如下:
private static int multi(int num){
int result=1;
if(num==1){
return result;
}else{
result=num*multi(num-1);
}
return result;
}
这道题和之前的猴子吃桃问题如出一辙,需要使用循环或递归的方式求解。在该题中,最后的10就是循环的初始条件,接下来只要找出循环的次数或停止递归的条件(边界条件)即可。循环方式示例代码如下:
private static int age(int num){
int result=10;
for(int i=1;i<num;i++){
result=result+2;
}
return result;
}
递归方式实现如下:
private static int age(int num){
int result=10;
if(num==1){
return result;
}else{
result=age(num-1)+2;
}
return result;
}
该问题有两个要求,需要先解第一问,才能做第二问。要解第一问,有不同的做法,将数字转换为字符数组,该问题第一问和第二问都会较简单的解决。示例代码如下:
private static int digitCapacity(int num){
char[] ch=String.valueOf(num).toCharArray();
return ch.length;
}
private static void print(int num){
char[] ch=String.valueOf(num).toCharArray();
for(int i=ch.length-1;i>=0;i--){
System.out.print(ch[i]);
}
}
当然,在判断数字位数功能的实现上,也可以使用简单的if…else来实现。示例代码如下:
private static int digitCapacity(int num){
int result=0;
if(num>=0 && num<=9){
result=1;
}else if(num>=10 && num<=99){
result=2;
}else if(num>=100 && num<=999){
result=3;
}else if(num>=1000 && num<=9999){
result=4;
}else if(num>=10000 && num<=99999){
result=5;
}
return result;
}
不使用char数组,纯用数字的方式,也可以实现逆序输出功能。示例代码如下:
private static void print(int num){
int length=digitCapacity(num);
System.out.println(num%((int)Math.pow(10, length)));
}
该题目类似前一道题,也有使用char数组和直接运算两种做法。使用char数组的话,直接比较对应位置上的数字即可。示例代码如下:
private static boolean isHuiwen(int num){
if((num<10000 || num>99999)){
System.out.println("输入的数字不是五位数,请重新输入");
return false;
}
char[] ch=String.valueOf(num).toCharArray();
if(ch[0]==ch[4] &&ch[1]==ch[3])
return true;
return false;
}
直接运算的方式,是求出万、千、十、个位置上对应的数字,然后比较即可。示例代码如下:
private static boolean isHuiwen(int num){
if((num<10000 || num>99999)){
System.out.println("输入的数字不是五位数,请重新输入");
return false;
}
/*个位上的数字*/
int num1=num%10;
/*十位上的数字*/
int num2=num%100/10;
/*百位上的数字*/
int num3=num%1000/100;
/*千位上的数字*/
int num4=num%10000/1000;
/*万位上的数字*/
int num5=num/10000;
if(num1==num5 && num2==num4)
return true;
return false;
}
同时,可以将这道题延伸为不限位数。此时,转换为数组的方式就比较方便了,具体的做法是:将数字转换为char数组,然后比较对应位置上的数字。示例代码如下:
private static boolean isHuiwen(int num){
if((num<10000 || num>99999)){
System.out.println("输入的数字不是五位数,请重新输入");
return false;
}
char[] ch=String.valueOf(num).toCharArray();
for(int i=0;i<ch.length;i++){
if(ch[i]!=ch[ch.length-1-i]){
return false;
}
}
return true;
}