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());
}