【Java基础算法】1-20题(斐波那契数列+递归+正则表达式+增强 for 循环+double精读丢失)

目录

参考自https://blog.csdn.net/m0_37741420/article/details/107294620

★(斐波那契)1.有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?(斐波那契)(递归)

在这里插入图片描述
思路:
①手动写前几个月兔子的总数,因为兔子需要成长需要第三个月才能生崽,所以计算当月兔子对数时,发现是上一个月的兔子数(基础兔子数)+上上个月的兔子对数=本月新生的兔子对数(因为上上个月的兔子正好这个月开始生,并且是一对生一对)
②单位为对。1,1,2,3,5, 8,13,21 从这些数字来看,发现其规律就是从第3个数字开始,每个数字的值等于前面紧邻的两个数字的和,所以这是一个斐波那契数列。
斐波那契数列最常规的做法是递归。
方法一:递归

//1.第一想法是用递归,要区分每个月生下来的兔子和计算每个月可生育的兔子数目
//1.2递归写法
public class Test {
    //第一想法是用递归,要区分每个月生下来的兔子和计算每个月可生育的兔子数目
    public static int rabbit(int i ){
        //每次进行一次方法都是一个月
        //排除月份输入错误的情况
        if(i<1){
            return -1;
        }
        //前三个月第一对兔子无法生育
        if(i<3){
            return 1;
        }
        return (rabbit(i-1)+rabbit(i-2));
    }

改进,将i换为month
递归能不用就不用因为

1)造成大量的资源浪费
2)可能会造成栈溢出异常

方法二:循环
用两个变量来表示所求月份的前两个月的数量,然后不断迭代更新这两个月份的值,再相加这两个月份的值,就能求出该月份的兔子数量。
在这里插入图片描述

//1.1循环写法,更优
 public static int rabbit_1(int month){
        if(month<1){
            return -1;
        }
        //前三个月第一对兔子无法生育
        int temp = 0;//中间变量
        int num1 = 1;
        int num2 = 1;
        for(int k = 3;k<= month;k++){
            temp = num2;
            num2 = temp +num1;
            num1 = temp;
        }
        return num2;
    }

调用测试部分及结果

public static void main(String[] args){

        //1.
        //1.
        System.out.println("第8个月的兔子总数为"+rabbit(8)+"对");
        System.out.println("第6个月的兔子总数为"+rabbit(6)+"对");
        System.out.println("第23个月的兔子总数为"+rabbit(23)+"对");

        System.out.println("第8个月的兔子总数为"+rabbit_1(8)+"对");
        System.out.println("第6个月的兔子总数为"+rabbit_1(6)+"对");
        System.out.println("第23个月的兔子总数为"+rabbit_1(23)+"对");
    }

在这里插入图片描述

2.判断101-200之间有多少个素数,并输出所有素数。

在这里插入图片描述

思路:
①素数又称质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。
简单理解就是,质数只可以被1和他自身整除。
②通过外层循环,来循环数字101-200。
③通过内层循环从101开始检查,让101除以2,3,4……100。同理,判断199时除以2,3,4……198。判断中途是否有整除,如果有则不是素数,则无需进行下一次循环,跳出。
工具:
开方的sqrt()方法
方法一:内层循换判断素数
设置一个boolean类型变量,初始值为真,如果中途有数整除则设置为假

//2.3内层循换判断素数。更差写法,在每个数字已经判断不是素数后依旧会进行循环
    public static void prime_1(int num1 ,int num2){
        int i = 0,j = 0;
        for( i = num1;i<=num2;i++){
            boolean sign = true;//每个数字循环开始除数时,都默认为素数。不能受上一个数字影响
            for(j = 2;j<i;j++){

                if( i%j == 0){
                    //判断是否为素数的条件在内层循环
                    sign = false;//中间有进入这一层的判断,说明有整除的情况,设置不是素数
                }
            }
            if(sign == true){//筛选后的都是素数,打印
                System.out.print(" "+i+",");
            }
        }
        System.out.println();
    }

方法二:外层循环判断素数
当除数从2开始逐渐加到被除数-1的情况下判断,若中间都没有整除导致的跳出,除数会一直加到最后一次与被除数相同才因为不满足循环体判断条件跳出,而不是内部的判断。跳出后在外层循环

 //2.101-200的素数
    public static void prime(int num1,int num2){//查找素数的范围
        int i = 0,j = 0;

        for( i = num1;i<= num2;i++){
            for( j = 2; j<i; j++ ){
                if(i%j == 0){
                    break;
                }
            }
            if( j == i){
                //说明没有中途跳出
                System.out.print(" "+i+",");
            }
        }
    }

方法三:优化方法二,将其除数的取值范围从[2,被除数-1]换为[2,被除数开方]

思路:因为如果一个数不是素数是合数, 那么一定可以由两个自然数相乘得到, 其中一个大于或等于它的平方根,一个小于或等于它的平方根,并且成对出现。
参考的作者原写法 int k = (int)Math.sqrt((double)i+1);难理解的点,关于开方时,i为什么+1

 public static void prime_2(int num1,int num2){//查找素数的范围
        int i = 0,j = 0;

        for( i = num1;i<= num2;i++){
             int k = (int)Math.sqrt(i);
            //System.out.print(" "+k);

            for( j = 2; j<=k; j++ ){
                if(i%j == 0){
                    break;
                }
            }
            //判断是否为素数的条件在外层循环
            if( j == k+1){
                //说明没有中途跳出
                System.out.print(" "+i+",");
            }
        }
        System.out.println();

    }

调试结果

		prime(101,200);
        prime_1(101,200);
        prime_2(101,200);	

3.打印出所有的“水仙花数”,所谓“水仙花数”是指一个三位数,其各位数字立方和等于该数字本身。例如:153是一个水仙花数,因为153 = 13+53+33

思路:
①循环遍历所有三位数
循环体内
②通过取余,除十取余,来取出三位数的个位,十位,百位。
③进行立方加计算判断
工具:%,/

//3.打印所有水仙花数
    public static void narcissus(){
        int bits = 0,ten = 0,hun = 0;
        for(int i = 100;i<=999;i++){
            bits = i %10;
            ten = (i/10)%10;
            hun = i/100;
            if(bits*bits*bits + ten*ten*ten + hun*hun*hun == i){
                System.out.print(i+" ");
            }
        }
    }


narcissus();

在这里插入图片描述

★(递归)4.将一个正整数分解质因数。例如:输入90,打印出90= 2* 3 * 3 *5。

思路:
1>①保证是质因数的原理是,从最小的质数2开始除该正整数,若成功整除,输出除数记录。再将商当做新的被除数分解,依旧从最小的2重新开始。
②注意除数不是只除一次,除到有余数为止,除数才自增(例如90 = 223*5,2需要参与两次)
2>①用递归,每次用循环的数成功整除以后,都证明新找到一个因数。输出因数
②然后将除后的商作为新的形参调用递归,再进行分解因数,依次类推。
方法一:循环

//4.分解质因数
    
    //4.1循环写法
    public static void decompose(int num){
        System.out.print(num+"=");

        for(int temp = 2;temp<= num;){//为了满足在同一个temp下多分解几次的情况,自增条件留白
            if( num%temp == 0){
                if(num/temp == 1){
                    //走到最后一个质因数的情况,去掉末尾的*
                    System.out.print(temp);
                    break;
                }
                else{
                    //当前除数一直可以整除被除数,不自增。只一直使用当前除数分解被除数
                    System.out.print(temp+"*");
                    num = num/temp;//为了提取已经找到的因子,分解大项。这一步可以改写为递归

                }
            }
            else{
                temp++;//当前的除数temp已经不能整除被除数,自增
            }
        }

    }

方法二循环内递归

//4.2递归写法

    /**
     * 加深理解
     * @param num
     */
    public static void decompose_1(int num){

        for(int temp = 2; temp<=num; temp++){
            if(num%temp == 0){
                if( num/temp == 1){
                    //末尾情况,结束方法
                    System.out.print(temp);
                    return;
                }else{
                    //整除成功,打印记录
                    System.out.print(temp+"*");
                    decompose_1(num/temp);//递归时,分解新的num,从除数2开始
                    break;//进入下一层递归返回后无需再进行循环。
                }
            }
            //一直找不到可以整除的除数,除数自增。num是质数的情况,temp自增到和num相同进入结束判断,停止
        }


    }

  		decompose(126);
        decompose(90);
        decompose(97);
        decompose_1(90);
        decompose_1(89);

在这里插入图片描述

5.利用条件运算符的嵌套来完成此题:学习成绩 >= 90分的同学用A表示,60-89分之间的用B表示,60分以下的用C表示。

 public static void grade(int num){
        if(num < 90){
            if(num<60){
                System.out.println("该同学的成绩为C");
            }
            else{
                System.out.println("该同学的成绩为B");
            }
        }
        else{
            System.out.println("该同学的成绩为A");
        }
    }

		grade(90);
        grade(89);
        grade(61);
        grade(60);
        grade(57);

在这里插入图片描述

6.输入两个正整数m和n,求其最大公约数和最小公倍数。

思路:
最大公约数,指两个或多个整数共有约数中最大的一个。
②这里使用辗转相除法, 又名欧几里德算法(Euclidean algorithm),是求最大公约数的一种方法。
它的具体做法是:用较大数除以较小数,再用出现的余数(第一余数)去除除数,再用出现的余数(第二余数)去除第一余数,如此反复,直到最后余数是0为止。当前除数就为两数的最大公约数
最小公倍数其中除0以外最小的一个公倍数就叫做这几个整数的最小公倍数。最小公倍数=两数乘积/两数的最大公约数由于两个数的乘积等于这两个数的最大公约数与最小公倍数的积。即(a,b)×[a,b]=a×b。

public static void common(int m,int n){
//求最大公约数
        int num1 = 0,num2 = 0,temp = 0;

        //将较大数存到num1中,作为被除数
        if (m == n){
            //两个相同数字的最大公约数为他本身
            System.out.println(m+"和"+n+"的最大公约数为:"+m);
            System.out.println(m+"和"+n+"的最小公倍数为:"+n);
            return;//不跳出后面会进循环
        }
        else if(m>n){
            num1 = m;
            num2 = n;
        }
        else{
            num1 = n;
            num2 = m;
        }

//逻辑主体
        while(num1%num2 != 0){
            temp = num1%num2;
            num1 = num2;
            num2 = temp;
        }
        System.out.println(m+"和"+n+"的最大公约数为:"+num2);

        //最小公倍数
        //由于两个数的乘积等于这两个数的最大公约数与最小公倍数的积。即(a,b)×[a,b]=a×b。
        // 所以,求两个数的最小公倍数,就可以先求出它们的最大公约数,然后用上述公式求出它们的最小公倍数。
        num1 = m*n;
        //此时num1为两数乘积,num2为两数的最大公约数
        System.out.println(m+"和"+n+"的最小公倍数为:"+num1/num2);
    }

★(正则表达式,增强for,字符串转数组)7.输入一行字符,分别统计出其中英文字母、空格、数字和其他字符的个数。

思路:
①将字符串拆分,依次判断字符的类型。
②用ASCII码比较
②字符比较和
②使用正则表达式进行匹配。
工具:
toCharArray()方法
使用方法:String.toCharArray 方法 ,作用:将字符串转换为字符数组。
java增强for循环
for (char c:chars)就是定义一个遍历字符c,让它分别等于字符串数组chars里面的各个字符,然后执行下面的语句,当c被赋值为chars里面所有字符各一次后,就会退出这个循环.
正则表达式教程
split()方法使用
matcher()方法使用

matches() 方法用于检测字符串是否匹配给定的正则表达式。
public boolean matches(String regex)
regex – 匹配字符串的正则表达式。

方法一:字符比较

 public static void judgestr(String str){
        //方法一:直接字符比较
        int number = 0,letter = 0,space = 0,other = 0;

        char[] arr = str.toCharArray();
        for(char c:arr){
            if('0'<=c&& c<= '9'){
                number++;
            }
            else if(('a'<= c && c <='z' )|| ('A' <= c && c <= 'Z')){
                letter++;
            }
            else if (c == ' '){
                space++;
            }else{
                other++;
            }
        }
        System.out.println("该行字符中,英文字母个数为:"+letter+",数字个数为:"+number+",空格个数为:"+space+",其他字符个数为:"+other);
    }

方法二:ASCII码比较
在这里插入图片描述

 //方法二,ASCII码比较
    public static void judgestr_1(String str){
        //小写英文97-122,大写英文65-90,数字0-9,为48-57。空格32,
        int number = 0,letter = 0,space = 0,other = 0;

        char[] arr = str.toCharArray();
        for(char c:arr){
            if(48<=c&& c<= 57){
                number++;

            }
            else if((97<= c && c <=122 )|| (65 <= c && c <= 90)){
                letter++;
            }
            else if (c == 32){
                space++;
            }else{
                other++;
            }

        }
        System.out.println("该行字符中,英文字母个数为:"+letter+",数字个数为:"+number+",空格个数为:"+space+",其他字符个数为:"+other);


    }

方法三:正则表达式

用正则表达式来匹配。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

“[a-zA-Z]”,表示的意思是从小写字母a到和小写字母z的集合与从大写字母A和大写字母Z的集合的并集,即所有字母,然后再用一个字符串str去匹配这个规则,如果能匹配上,则返回true否则返回false。示例代码如下:

 //7.3正则表达式比较
    public static void judgestr_2(String str) {
        int number = 0,letter = 0,space = 0,other = 0;
        String[] string= str.split("");
        for(String s : string){
            if(s.matches("[0-9]")) {
                number++;
            } else if(s.matches("[a-zA-Z]")){
                letter++;
            }else if(s.matches("\\s")){//"[ ]"也可以
                space++;
            } else{
                other++;

            }
        }
        System.out.println("该行字符中,英文字母个数为:"+letter+",数字个数为:"+number+",空格个数为:"+space+",其他字符个数为:"+other);

    }

	    judgestr("45342 f42ceqv 4rf;9-2(%3/.,");
        judgestr_1("45342 f42ceqv 4rf;9-2(%3/.,");
        judgestr_2("45342 f42ceqv 4rf;9-2(%3/.,");

在这里插入图片描述

总结:

  • split() 方法与toCharArray区别
    split是根据你需要按照的分隔符来分割的根据匹配给定的正则表达式来拆分字符串;
  • toCharArray是将String对象的每一个下标位的对象保存在char[]中

①String a = “avc,d,e,s,f”; String []b = a.split(“,”);这个就是根据逗号将数据分开,遍历输出得到的b的对象为"avc",“d”,“e”,“s”,“f”。
②String a = “avc,d,e,s,f”; char[] b = a.toCharArray(); 这个b保存的信息就是[‘a’,‘v’,‘c’,‘,’,‘d’,‘,’,‘e’,‘,’,‘s’,‘,’,‘f’]。

重点理解

char[] chars=str.toCharArray();
for(char c:chars)
String[] string=str.split(“”);
for(String s : string)
if(s.matches(“\s”))


8.求s = a+aa+aaa+aaaa+aa……a的值,其中a是一个数字。例如2+22+222+2222+22222(此时共有5个数相加),几个数相加由键盘控制。

思路:
①使用循环计算(循环几个数相加),第一次为第一项的个位数字
②每次循环都用前项*10后加个位项,直到达到要求的项数

 public static long calculation(int a,int n){

        long num = a;//记录每次加的222之类
        long s = num;
        for(int i = 2; i <= n; i++){
            num = num*10 + a;
            s += num;
        }
        return s;
    }

 System.out.println(calculation(2,5));//2+22+222+2222+22222

在这里插入图片描述
在这里插入图片描述

9.一个数如果恰好等于它的因子之和,这个数就称为“完数”。例如6 = 1+2+3.编程找出1000以内的所有完数。

思路:
①完数中因子的定义,假如整数n除以m,结果是无余数的整数,那么我们称m就是n的因子。整数n的因子数为包含它自身的所有因子个数。它的真因子就是除了自身以外的约数。②只要通过除后余数为0,记录下所有因子的和,然后与自身比较即可。

例如?困惑,因子只有一组吗,如12 = 2*6,(2,6) 12= 2 * 2 * 3.(2,2,3)哪组才是因子。
答,都不是,12的真因子为1,2,3,4,6

public static void perfect(int n1,int n2){
        //n1,n2为范围
        //思路,先找出当前数字的真因子,然后计算和与其比较
        int num1 = n1,num2 = n2;

		//较大的数存在num2
        int sum = 0;//sum用来记录因子的和
        if(n1> n2){
            num1 = n2;
            num2 = n1;//范围[num1,num2]
        }

        //外循环循环要找完数的数字范围
        for(int i =num1;i <= num2;i++){
            //内循环排查是否为完数
            //拆分因子
            sum = 0;
            for(int j = 1 ; j < i ;j++){
                // j<i因为只找“真”因子,排除数字自身
                //回忆4题,分解质因数。为什么没有把自增放在循环体内部。
                //因为,此时是寻找真因子,相同数只需要找到一次。
                // 12= 2*2*3.(是分解质因数)但是12的真因子为1,2,3,4,6

                if(i%j == 0){
                    //余数为0的情况,说明是因子
                    sum += j;//每次遇到可以整除的情况就是真因子,就加起来记录
                }
            }
            //在跳出内循环后判断是否是完数
            if(i == sum){
                System.out.println(i+" ");
            }

        }
    }

 perfect(1,1000);

在这里插入图片描述

10.一球从100米高度自由落下,每次落地后反跳回原高度的一半;再落下,求它在第10次落地时,共经过多少米?第 10次反弹多高?

思路:
①记录从第一次开始的每一次落地时的反弹高度,每次除二。
②注意,计算经过路径时有反弹高度和落下高度。只有第一次落地只有落下高度,没有之前的反弹高度

 //因为不知道要下落几次才能完全落地,默认下落弹起高度都为Int型整数。
    public static void fall(int high,int frequency){
         //形参为初始高度和落地次数
        int h = high;//反弹高度
        int sum = h;//经过的路线总长度
        //第一次落下的特殊情况
        h /= 2;
        int i = 0;
        
        for( i = 2;i<= frequency;i++){
            sum += h*2;//第i次落地后经过的距离,有反弹高度和落下高度
            h /= 2;//第i次落地后反弹高度

        }
        System.out.println("第"+(i-1)+"次落地时,共经过"+sum+"米,第"+(i-1)+"次反弹"+h+"米");
    }

		fall(100,3);
        fall(100,10);

在这里插入图片描述

11.有1,2,3,4四个数字,能组成多少个互不相同且一个数字中无重复数字的三位?并把它们都输出。

思路:
①暴力三层循环,每层依次对个位,百位,十位,循环列举1,2,3,4四个数字。
②判断最后组成的数字,如果三位无重复数字就输出

/返回值为符合条件数字的个数,暴力循环
    public static int generateNum(){
        int bits= 0,ten = 0, hun = 0;
        int sum = 0;
        int count = 0;
        for(int i = 1;i <= 4; i++){//个位遍历1,2,3,4
            sum = 0;

            for(int j = 1; j <= 4;j++){

                for(int k = 1;k <= 4; k++){
                    sum = i+j*10+k*100;
                    if( i != j && i != k && j != k){
                        count++;
                       System.out.print(sum+"  ");

                    }
                }
            }


        }

        return count;
    }

System.out.println("由1,2,3,4组成的无重复数字的三位数有"+generateNum()+"个");

在这里插入图片描述

★(double精度出现问题)12.企业发放的奖金根据利润提成。利润(I)低于或等于10万元时,奖金可提10%;利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可可提成7.5%;20万到40万之间时,高于20万元的部分,可提成5%;40万到60万之间时高于40万元的部分,可提成3%;60万到100万之间时,高于60万元的部分,可提成1.5%,高于100万元时,超过100万元的部分按1%提成,从键盘输入当月利润,求应发放奖金总数?

  public static double bonus(double profit){
        double reward = 0;
        if(profit <= 10){
            reward = profit*0.1;
        }else if(profit < 20){
            reward = 10*0.1 + (profit-10)*0.075;
        }else if(profit < 40){
            reward = 10*0.1 + 10*0.075+(profit-20)*0.05;
        }else if(profit < 60){
            reward = 10*0.1 + 10*0.075 + 20*0.05 + (profit-40)*0.03;
        }else if(profit < 100){
            reward = 10*0.1 + 10*0.075 + 20*0.05 + 20*0.03 + (profit-60)*0.015;
        }else{
            reward = 10*0.1 + 10*0.075 + 20*0.05 + 20*0.03 + 40*0.015 + (profit-100)*0.01;
        }
        return reward;

    }

		System.out.println("利润为6.4w时,奖金为"+bonus(6.4)+"w");
        System.out.println("利润为7.4w时,奖金为"+bonus(7.4)+"w");
        System.out.println("利润为35.4w时,奖金为"+bonus(35.4)+"w");
        System.out.println("利润为12.1w时,奖金为"+bonus(12.1)+"w");
        System.out.println("利润为24.2w时,奖金为"+bonus(24.2)+"w");
        System.out.println("利润为36.3w时,奖金为"+bonus(36.3)+"w");
        System.out.println("利润为49.7w时,奖金为"+bonus(49.7)+"w");
        System.out.println("利润为67.5w时,奖金为"+bonus(67.5)+"w");
        System.out.println("利润为88.6w时,奖金为"+bonus(88.6)+"w");
        System.out.println("利润为116.8w时,奖金为"+bonus(116.8)+"w");

在这里插入图片描述
重点理解:
double精读问题产生的异常结果
问题:小数点为0.4时
在这里插入图片描述
在这里插入图片描述

★(发现%n为换行)13、一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?

思路:
①完全平方数,若一个数能表示成某个整数的平方的形式,则称这个数为完全平方数。完全平方数是非负数,而一个完全平方数的项有两个。
②在Math中有方法sqrt是求某个数的开方的,当某个数的开方%1结果为0时,就代表了这个数为完全平方数。
③该数的取值范围,该题并没有指定该数一定要是正整数,所以有可能不是正整数。因为加100就为完全平方数,而完全平方数非负。所以该数从-100开始,进行遍历计算。

工具:
Math.sqrt()方法

 public static void square(){
        //
        int num = 0;
        for( num = -100; num<10000; num++) {
            if (Math.sqrt(100 + num)%1 == 0) {
                //开方后余数为0,则是完全平方数
                if(Math.sqrt(100+num+168)%1 == 0){
                    System.out.println(" "+num);
                }
            }
        }

    }

 square();

在这里插入图片描述

总结:
格式控制符%.4f意思是保留精度三位,指小数点后三位
%n在printf中作为参数使用,为换行符
%n是一种格式字符串,只能用到printf的参数里;\n也是回车符,只不过这个是转义字符,可以用于一切字符串。‘%n’ 行分隔符 结果为特定于平台的行分隔符。

//格式控制符、开方函数及%n演示
		double x = 11.635;
        double y = 2.76;

        System.out.printf("e 的值为 %.4f%n", Math.E);//%n是回车换行符
        System.out.printf("sqrt(%.3f) 为 %.3f%n", x, Math.sqrt(x));

14、输入某年某月某日,判断这一天是这一年的第几天?

思路:
①闰年和平年中天数的区别
一三五七八十腊(12月)为大月31天,其中2月在闰年为29天,在平年为28天
闰年共有366天(1-12月分别为31天,29天,31天,30天,31天,30天,31天,31天,30天,31天,30天,31天)。
平年共有365天(1-12月分别为31天,28天,31天,30天,31天,30天,31天,31天,30天,31天,30天,31天)。
②普通闰年:公历年份是4的倍数,且不是100的倍数的,为闰年(如2004年、2020年等就是闰年)。
世纪闰年:公历年份是整百数的,必须是400的倍数才是闰年(如1900年不是闰年,2000年是闰年)。
③计算先判断月份,计算当前月之前的天数总和。然后加上当天月的日期,暂时默认2月为28天
③(可优化switch写法)
④再判断是否是闰年,如果是就天数加一。把2月的28天补到29天即可
工具:
switch case语句

** 方法一 :**每个月分类计算当前月之前的月经过的天数

public static void monthDay(int year,int month,int day){
        int y = year,m = month,d = day;
        int result = 0;//记录天数
        switch(m){
            case 1:
                result = 0;
                break;
            case 2:
                result = 31;
                break;
            case 3:
                result = 31+28;
                break;
            case 4:
                result = 31+28+31;
                break;
            case 5:
                result = 31+28+31+30;
                break;
            case 6:
                result = 31+28+31+30+31;
                break;
            case 7:
                result = 31+28+31+30+31+30;
                break;
            case 8:
                result = 31+28+31+30+31+30+31;
                break;
            case 9:
                result = 31+28+31+30+31+30+31+31;
                break;
            case 10:
                result = 31+28+31+30+31+30+31+31+30;
                break;
            case 11:
                result = 31+28+31+30+31+30+31+31+30+31;
                break;
            case 12:
                result = 31+28+31+30+31+30+31+31+30+31+30;
                break;
                default :
                    System.out.println("输入月份错误请重新输入");

        }

        //判断闰年
        // 普通闰年:公历年份是4的倍数,且不是100的倍数的,为闰年(如2004年、2020年等就是闰年)。
        //世纪闰年:公历年份是整百数的,必须是400的倍数才是闰年(如1900年不是闰年,2000年是闰年)。
        if(((y%4 == 0) && (y%100 != 0) )||((y%100 == 0) && (y%400 == 0))){
            if(m >2){
                result++;//把2月从28加到29;
            }
        }

        result += d;
        System.out.println(y+"年"+m+"月"+d+"日是这一年的第"+result+"天");



    }

** 方法二:**优化方法一,优化switch写法。将大月,小月,2月。分成三类,用循环自动累加天数即可。

 public static void monthDay_1(int year,int month,int day) {
        int y = year,m = month,d = day;
        int result = 0;//记录天数
        for( int i = 1; i <= m; i++ ){
            switch(i){

                case 1:
                    result += 0;
                    break;

                //若月份是3,说明上一个月满日子为28
                case 3:
                    result += 28;
                    break;


                //上一个月是小月的月份
                case 5:
                case 7:
                case 10:
                case 12:
                    result += 30;
                    break;


                //上一个月是大月的月份
                case 2:
                case 4:
                case 6:
                case 8:
                case 9:
                case 11:
                    result += 31;
                    break;

                default :
                    System.out.println("输入月份错误请重新输入");
                    break;

            }
        }

        //判断闰年
        // 普通闰年:公历年份是4的倍数,且不是100的倍数的,为闰年(如2004年、2020年等就是闰年)。
        //世纪闰年:公历年份是整百数的,必须是400的倍数才是闰年(如1900年不是闰年,2000年是闰年)。
        if(((y%4 == 0) && (y%100 != 0) )||((y%100 == 0) && (y%400 == 0))){
            if(m > 2){
                result++;//把2月从28加到29;
            }
        }

        result += d;
        System.out.println(y+"年"+m+"月"+d+"日是这一年的第"+result+"天");

    }

15、输入三个整数x,y,z,请把这三个数由小到大输出。

思路:
①排序逻辑,暂不考虑排序算法。
②先两两比较,xy,yz将最大的挪到第三位,不符合就调换位置。
③再比较一次xy中较大挪到第二位即可。(其实就是冒泡排序思想)

public static void sort(int x,int y ,int z){
        int xx = x,yy = y ,zz = z;
        int temp = 0;

        if(xx > yy){
            //两数调换
            temp = xx;
            xx = yy;
            yy = temp;

        }
        if( yy > zz){
            temp = yy;
            yy = zz;
            zz = temp;
        }
        //以3,2,1为例,此时x,y,z中的结果为2,1,3
        if (xx > yy){
            temp = xx;
            xx = yy;
            yy = temp;

        }
        System.out.println(x+","+y+","+z+"这三个数由小到大的顺序为"+xx+","+yy+","+zz);


    }

		sort(1,2,3);
        sort(3,2,1);
        sort(3,1,2);
        sort(2,3,1);

在这里插入图片描述

16、输出9*9口诀。

思路:嵌套循环,外层循环大数,内层循环小于等于大数的小数作为乘法的第一项。内层循环结束后换行,进行下一个大数的计算。
因为计算结果有个位有十位对的不太齐把原来的System.out.print(" "+j+"*"+i+"="+i*j); 改为 System.out.printf(" "+j+"*"+i+"=%2d",i*j);

public static void formula(){
        for(int i = 1; i<10; i++){
            for(int j = 1; j <= i; j++){
                 System.out.printf("  "+j+"*"+i+"=%2d",i*j);//对齐
            }
            System.out.println();
        }
    }

formula();

对比对齐优化前后效果
在这里插入图片描述

在这里插入图片描述

17、猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个 第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时,见只剩下一个桃子了。求第一天共摘了多少。

思路:
①正着算,就是突然想到一个超级暴力的做法。因为不知道第一天的数字,假设为num,之后的每天开始吃的桃子数量就是(num/2)-1。
②用排除法,假设第一天摘得桃子数量在1到1000之间,然后用循环挨个试这些数字哪个符合第10天剩1个。
①思路二,反着算,从第十天往后倒,第10天剩余1个,第9天开始吃时就是(1+1)*2个,依次类推。
**方法一:**循环1000次,计算不出来,死循环。
**方法二:**倒推


    public static void peach_1(int day,int surplus){
        int pnum = surplus;//第十天剩一个桃子
        //一直计算到
        for(int i = day-1; i > 0; i--){
            //第9天吃完后剩余1个
            pnum = (pnum+1)*2;//计算第9天开始吃时候的数量

        }
        System.out.println("当这种吃法第"+day+"天一开始就发现只剩"+surplus+"时,计算出第一天开始吃时候的数量为"+pnum);



    }

	    peach_1(10,1);
        peach_1(10,6);
        peach_1(4,6);

在这里插入图片描述

★18、两个乒乓球队进行比赛,各出三人。甲队为a,b,c三人,乙队为x,y,z三人。已抽签决定比赛名单。有人向队员打听比赛的名单。a说他不和x比,c说他不和x,z比,请编程序找出三队赛手的名单。

思路:原作者写法我发现,只符合,队员顺序为[a,b,c],[x,y,z]的情况,只是通过队员排列顺序的原因,在b错误的匹配z之前恰好用b匹配了x,排除了这种情况。如果队员顺序为[b,a,c]
,[z,y,x]则结果错误。
而且防止已经配对的队员又重复比较,设置了一组值每次成功匹配时记录两个队员。下次匹配时不允许于匹配成功的组其中一个相等,另一个不等的情况。
好像只能排除上一组,上上一组应该不行
见下图和代码
在这里插入图片描述
原作者代码如下

 private static void printList(){
 
        char[] teamA={'b','a','c'};
        char[] teamB={'z','y','x'};
        //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];
                    }
                }
            }
        }


    }

思路:
题目条件有,a不和x,c不和x,c不和z
想讨论一下,顺序随意定义的普遍情况
①可以假设两个队伍的队员为数组元素,建立两个数组。
②将数组中的队员两两比较,但是要考虑如果先比较的是甲队的b这样,没有约束条件的队员。乙队所有成员都符合与他比赛,只能通过后续甲队的a,c确定比赛对手后再判断给b匹配剩余的乙队成员。
所以如果第一次匹配b或a时无法确定对手,可以选择用先记录的方式,记录满足当前队员b或a的匹配条件,但又不确定的乙队对手都有哪些,相当于待定对手。
④考虑一个问题,b和a的待定对手不同且有重叠。要怎么记录?可以也使用数组,每个队员都建立一个自己的数组用来存放自己的待定对手。

如甲队[b,a,c]与乙队[z,y,x]比较时,可以设置三个初始元素都为0的数组分别存放b,a,c的待定对手。a可以与y,z匹配,将y,z所在的相同下标位置将元素制1,a的待定对手数组为atemp=[1,1,0],btemp = [1,1,1],ctemp = [0,1,0]数组按列代表了与z,y,x匹配的情况。 类似二维数组,行由上到下为a,b,c(这个上下顺序随意,记得哪行是哪个字母的就行)列为z,y,x(一定要按乙队本来设置的顺序,因为每列的元素要对应)

可以看出,待定对手数组中,只有数组btemp的位置2(与x在乙队数组的位置相同)为1。得出b匹配x。而ctemp中只有位置1(与y相同)为1,得出c匹配y。将已经匹配成功的行列清0。只剩下atemp[0]为1,即a与z匹配。

⑤实现代码的话需要找到第一个突破口,如某行或某列只有一个1,匹配成功后,将对应行列清0,再次寻找。计算量太大,优化一下
只用一个数组记录匹配情况来规划匹配顺序,保证每次匹配都可以确定对手代替atemp,btemp和ctemp。遍历a,b,c时,对应位置有匹配就+1。得到数组temp=[2,3,1]代表了z,y,x分别可以与2个,3个,1个队员成功匹配。遍历找到最小的1,即为当前能直接确定对手的情况,对x(位置2为1)进行匹配
⑥匹配成功一次后,对数组temp中每一个未匹配元素和自身减一,因为排除一个备选对象。再次寻找值为1的位置,进行匹配。
⑦直到匹配成功3次为止

 private static void printList(){
        //甲乙队伍
        char[] jia = { 'b','c','a'};
        char[] yi = {'y','x','z'};//队员顺序可以随意定义
        int[] temp = {0,0,0};//记录yi数组对应位置y,x,z有多少个待定对手
        int[] flag = {0,0,0};//记录jia数组已经匹配成功的队员,至1则不再参与匹配

        //循环完成数组temp,记录yi数组对应位置y,x,z有多少个待定对手
        //方便寻找顺利的判断顺序
        for(int i = 0; i<jia.length; i++){
            for(int j = 0; j< yi.length; j++){
                if( ((jia[i] == 'a') && (yi[j] == 'x'))
                        || ((jia[i] == 'c') && (yi[j] == 'x'))
                        || ((jia[i] == 'c') && (yi[j] == 'z'))){

                    //不可以匹配的条件

                } else{
                    temp[j]++;

                }
            }
        }


        //打印算出来的temp函数
        for(int j = 0; j< yi.length; j++){
            System.out.print(temp[j]+" , ");
        }
        System.out.println();


        //开始正式匹配运算
        //遍历temp找1的情况,根据数组用最方便的顺序匹配比较
        for(int r = 0; r<temp.length; r++){
            //外循环三次,因为会成功匹配三对
            //需要每次都从temp头寻找为1的位置

            //遍历数组temp寻找1的位置
            int l = 0;
            for(int i = 0; i<temp.length; i++){
                if(r>0){
                    //说明已经匹配成功一次了
                    //将所有未匹配的减一,包括匹配成功的置为0
                    if(temp[i] != 0){
                        temp[i]--;
                    }
                }

                if( temp[i] == 1){
                    l = i;//找到了1所在位置i,则此轮循环可以确定对手的成员是yi[l]
                }
            }

            //遍历jia寻找与yi[l]匹配的对手
            for(int j = 0; j< yi.length; j++){
                if( ((jia[j] == 'a') && (yi[l] == 'x'))
                        || ((jia[j] == 'c') && (yi[l] == 'x'))
                        || ((jia[j] == 'c') && (yi[l] == 'z'))){

                    //不可以匹配的条件


                } else if(flag[j] == 0){//没有成功匹配过的jia[j]
                    System.out.println(yi[l]+" VS "+jia[j]);//匹配成功,输出
                    //找到jia数组对应的j位置与之匹配后跳出循环,不再给l进行找j
                    flag[j] = 1;
                    break;

                }
            }


        }
    }

  printList();

在这里插入图片描述

★(增加字符串长度)19、打印出如下图案菱形。

在这里插入图片描述
思路:
1>①按行老实打印

2>①分为上下两部分输出字符串或数组打印,上部分每行的空格随行数递减,字符*随行数每次递增两个
②观察图形发现,最长的第4行比第1行的字符左半边前多出3个字符。以此类推第1行字符串比第n行(最长的中间那一行) 多出n-1个空格。则第1行需要打印n-1个空格,第2行前打n-2个空格。第i行前打印n-i个空格(循环n次)
③打印到中间位置,最大行结束后。空格与字符*开始递减。下半部分不包括最长的那一行,所以总行数为n-1。下半部分的第一行比第n行少两个*,多一个空格,第二行多两个空格,以此类推(循环n-1次)

3>①分为上下两部分按数量输出字符和空格。需要找出每行空格,字符与行的关系
②上半部分打印时(共n行)
行数范围1,2……,i,……n (i为当前行数) 每行空格数为n-i,每行*数为(i*2)-1
③下半部分打印时(共n-1行)有两种循环写法
行数范围n+1,n+2,……,i,……,2n-1【从n+1行开始的总共n-1行推断行数最大值为n+(n-1) 即形参给定的num菱形总行数)】(i为当前行数) 每行空格数为i-n,每行*数为(2*n-1)-(2*(i-n))(第n行的*数量-每行递增少两个*)。
第二种循环写法改变取值范围行数范围1,2,……,i,……,n-1每行空格数为i,每行*数为2*(n-i)-1。因为上半部分第i行的*数为2*i-1
方法二:分为上下两部分输出字符串或数组打印

public static void diamond(int num){
        //偶数行会按照奇数行打印
        String dia ="*";
        int n = 0;//确定最长的一行行数
        int length = 0;//规定下半部分数组的打印长度

        //确定最长行的行数
        if(num%2 == 0){
            n = num/2;
        }else{
            n = num/2+1;
        }

        //打印上半部分
        for (int i = 1; i<= n; i++){//代表行数,所以从1开始
            for(int j = 0; j<n-i; j++){
                System.out.print(" ");
            }
            System.out.println(dia);
            dia = dia+"**";
        }

        //打印下半部分
        char[] c = dia.toCharArray();
        length = c.length-2;//用控制length的大小来实现每行打印的*越来越少
        for (int i = 1; i < n; i++){//打印下半部分的n-1行

            //打印i行的空格,由1到i个
            for(int j = 0; j<i; j++){
                System.out.print(" ");
            }

            //打印i行的*
            length = length-2;
            for(int j = 0; j<length; j++){
                System.out.print(c[j]);
            }
            //打印完本行后换行
            System.out.println();


        }

    }

方法三:分为上下两部分按数量输出字符和空格

 //19.2
    public static void diamond_1(int num) {
        int n = 0;
        //确定最长行的行数
        if(num%2 == 0){
            n = num/2;
        }else{
            n = num/2+1;
        }

        //打印上半部分
        for(int i = 1; i<=n; i++){
            for(int j = 0; j<n-i; j++){//打印n-i个空格
                System.out.print(" ");
            }
            for(int k = 0; k<(i*2)-1; k++){//打印2i-1个*
                System.out.print("*");
            }
            //打印完一行后换行
            System.out.println();

        }
        //打印下半部分写法一
//        for(int j = n+1; j <= 2*n-1; j++){
//            for(int i = 0; i<j-n; i++){//打印j-n个空格
//                System.out.print(" ");
//            }
//            for(int k = 0; k<4*n-2*j-1; k++){//打印2*n-j个*
//                System.out.print("*");
//            }
//            //打印完一行后换行
//            System.out.println();
//
//        }

        //下半部分写法二
        for(int j = 1; j<= n-1; j++){
            for(int i = 0; i<j; i++ ){
                System.out.print(" ");
            }
            for(int k = 0; k<2*(n-j)-1; k++){
                System.out.print("*");
            }
            System.out.println();
        }

    }

        diamond(7);
        diamond_1(7);
        diamond(6);
        diamond_1(6);
        diamond(9);
        diamond_1(9);

在这里插入图片描述
在这里插入图片描述

20、有一分数序列:2/1,3/2,5/3,8/5,13/8,21/13…求出这个数列的前20项之和。

思路:观察发现后项的分子都为前项分母。后项的分母为前项分子与分母的和
使用循环用前项来计算每一项的值,并同时加起来。

/**
     * 参数为项数
     * @param num
     */
    public static void fraction(int num){
        //
        int n = num;
        int mol = 1;//存分子
        int den = 2;//存分母
        double sum = 0.0;
        int temp = 0;


        for(int i = 1; i<= n; i++){
            //这里要注意,num2/num1后要转换成double类型,要不默认是int型,造成计算结果变小
            
            sum += (double)mol/den;
            //当前i项的分子分母
            temp = mol;
            mol = den;
            den = temp+den;


        }
        System.out.println("这个序列的前"+n+"项总和为"+sum);


    }

		fraction(2);
        fraction(4);
        fraction(20);

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值