Java解决买汽水、斐波那契数列、组合排序问题

Java解决买汽水、斐波那契数列、组合排序问题?

1.经典汽水问题

题目一:一瓶汽水两块钱,两个瓶身可以换一瓶汽水,三个瓶盖也可以换一瓶汽水。在不考虑赊账的前提下,20块钱能买多少瓶汽水?
解题思路:这是我在学习Java中遇到的第一个递归问题。先解释一下递归,递归就是在满足条件的情况下,在方法中再次调用本方法,从而形成循环,因此递归一定是存在结束条件的,否则将形成一个死循环。先举个例子来说明下递归的用法。

/**
 * 不用循环计算1到50的和
 */
public static void main(String[] args) {
	 System.out.println(sum(5));
}
public static int sum(int num){
	 // 如果入参大于1
     if (num >= 1) {
     	// 则继续调用该方法
        return num + sum(--num);
      }
      // 递归结束
     return 0;
}

分析一下汽水问题。买汽水的时,一瓶汽水会有一个瓶身和一个瓶盖,且瓶身和瓶盖还可以继续换汽水,所以该方法的入参设计应该有三个参数,瓶身、瓶盖、总瓶数。递归的条件则有两个,一是瓶身数量大于等于2时,二是瓶盖数量大于等于3时。

/**
 *
 * @param a 瓶身
 * @param b 瓶盖
 * @param sum 总瓶数
 * @return 总瓶数
 */
public static int buy(int a, int b, int sum){
		// 当瓶身数量大于2时
        if (a >= 2) {
        	// 瓶盖的数量等于原瓶盖数量加瓶身可换的汽水数,所以是a/2
            b = b + a/2;
            // 总数量等于原数量加瓶身可换的汽水数
            sum = sum + a/2;
            // 瓶身的数量等于原瓶身可换的汽水数量加剩余的瓶身数,所以和a%2
            // 比如,三个瓶身可以换1瓶汽水,还剩1个瓶身,所以计算结果为 3/2 + 3%2 = 2
            a = a/2 + a%2;
          	// 递归,继续兑换汽水
            return buy(a, b, sum);
        }
        if (b >= 3) {
        	// 瓶盖就同理可得了
            a = a + b/3;
            sum = sum + b/3;
            b = b/3 + b%3;
            return buy(a, b, sum);
        }
        // 递归结束,返回总瓶数
        return sum;
}

执行结果如下

public static void main(String[] args) {
        // 20块钱能买几瓶汽水
        // 20块钱等于10个瓶身,10个瓶盖,10瓶汽水
        System.out.println(buy(10, 10, 10));
        // 输出结果为53
 }

2.斐波那契数列求和问题

题目二:斐波那契数列是指,从第三个数开始,均为该数的前两个数之和。如1 1 2 3 5 8 … 这就是一个斐波那契数列。问,长度为10的斐波那契数列的和是多少?
解题思路:从数学的角度出发,其实斐波那契数列就是当X大于3时,F(X) = F(X - 1) + F(X - 2)。那么要想计算斐波那契数列的和,不妨先从获取指定位数的值开始。

// 计算斐波那契数列的第N个数
public static long calc(int n){
     if (n < 0) {
         return 0;
     }
     // 第0个数为0,第一个数为1
     if (n == 0 || n == 1) {
        return n;
     } else {
     	// 当入参满足条件时,开始计算数列中指定的数
     	// 可以看出,该处的递归就是上述的F(X) = F(X - 1) + F(X - 2)函数
        return calc(n-1) + calc(n-2);
     }
}

试一下输出的结果是否正确

 public static void main(String[] args) {
        System.out.println(calc(3)); // 输出结果为2
        System.out.println(calc(6)); // 输出结果为8
}

接下来再一个打印方法,验证一下计算的数列是否正确

 // 打印相应长度的斐波那契队列
public static void print(int n){
     for (int i = 1; i <= n; i++) {
     	// 从第一个数开始
        System.out.print(calc(i));
        	// 为了打印好看,在非最后一个数的后面都打印一个逗号,便于阅读
           if (i != n) {
              System.out.print(",");
           } else {
              System.out.println();
           }
       }
}

打印结果如下

public static void main(String[] args) {
        print(6);
        // 1,1,2,3,5,8
        // 第三位为2,第六位为8,说明上述获取指定位数的值计算是正确
}

能精确的计算出斐波那契数列指定位数的值,那么数列的求和当然是信手拈来。

// 求和相应长度斐波那契队列
public static long sum(int n){
     if (n < 0) {
         return 0;
     }
     if (n == 1 || n == 0) {
         return n;
     }
     long num = 0;
     for (int i = 1; i <= n; i++) {
     	 // 通过调用上面的计算指定位置值的方法完成求和计算
     	 // 此处不是递归,因为递归是方法A在满足情况的条件调用方法A
     	 // 此处是方法B调用方法A
         num = num + calc(i);
    }
    return num;
}

打印一下数列元素和求和结果进行验证

public static void main(String[] args) {
        print(6);
        System.out.println("sum = " + sum(6));
        // 打印结果为
        // 1,1,2,3,5,8
		// sum = 20
}

3.排列组合问题

题目三:用1,2,2,3,4,5这六个数字,打印出所有不同的排列,要求4不能在第三位,3与5不能相连,如512234,412345。
解题思路:高考常见的排列组合问题。A几几C几几一下就能计算出来的问题,用程序该如何解决呢?直接上代码,在代码中附上步骤。

 /**
   * 用1,2,2,3,4,5这六个数字,打印出所有不同的排列,
   * 要求4不能在第三位,3与5不能相连
   * 如512234,412345
   */
public static void main(String[] args) {
      String[] arr = {"1", "2", "2", "3", "4", "5"};
      // 每个数字只能使用一次,因此需要一个开关进行控制
      boolean[] check = new boolean[6];
      Random random = new Random();
      // 每次取出来的数
      String str = "";
      // 拼接后的随机数
      String num = "";
      // 满足条件的数集合,防止将重复的数放入集合,因此选取Set类型
      Set<String> set = new HashSet<>();
      // 1,2,2,3,4,5组合成的所有数集合
      Set<String> total = new HashSet<>();
      // 下标
      int index;
      // 开始造数
      while (true) {
          for (int i = 0; i < arr.length; i++) {
              while (true) {
                  index = random.nextInt(arr.length);
                  str = arr[index];
                   // 每次取出的数不能重复 
                  if (!check[index]) {
                      break;
                  }
              }
                check[index] = true;
                // 字符串的拼接用StringBuffer的append方法性能会更好,之前写的时候疏忽了
                // 将随机选出的数拼接起来
                num += str;
            } 
            // 统计组合排序的次数
            total.add(num);
            // 4不能在第三位,3与5不能相连
            if (num.indexOf("35") == -1 && num.indexOf("53") == -1 && num.indexOf("4") != 2) {
            	// 把满足条件的数放入集合中
                set.add(num);
            }
            // 开关数组重置
            check = new boolean[6];
            // 数重置
            num = "";
            // A66/A21 = 360  6个数直接排序,消除双算
            // 当1,2,2,3,4,5组合成的所有数集合大小等于360时
            // 说明所有可能的排序都已经生成,并且所有满足条件的数都可以放入到set集合中
            if (total.size() == 360) {
            	// 结束循环
                break;
            }
	}	
   		// lambda表达式打印出所有满足条件的组合数
        set.forEach((s) -> System.out.println(s));
        // 满足条件的个数为198
        System.out.println(set.size());
 }
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值