《Java黑皮书基础篇第10版》 第18章【习题】

Java语言程序设计 习题第十八章

18.2章节习题

18.1 什么是递归方法?什么是无限递归?

递归方法可以拆解为。在Java中,大多数方法的执行都需要调用栈,来跟踪方法的调用和返回。在的过程中,递归方法调用自身,把新的调用添加到调用栈的顶部(进栈);当遇到终止情况时,开始执行,把顶部的调用栈返回(出栈)

如果一个递归方法没有终止情况,就会发生无限递归

18.2 程序清单18-1中,对于factorial(6)而言,涉及多少次的factorial方法调用?

14次

18.3 给出下面程序的输出,指出基础情况以及递归调用。

// 输出15
public class Test {
    
    
  public static void main(String[] args) {
    
    
    System.out.println(
      "Sum is " + xMethod(5));
  }

  public static int xMethod(int n) {
    
    
    if (n == 1)
      return 1;
    else
      return n + xMethod(n - 1);
  }
}
// 输出7654321
public class Test {
    
    
  public static void main(String[] args) {
    
    
    xMethod(1234567);
  }

  public static void xMethod(int n) {
    
    
    if (n > 0) {
    
    
      System.out.print(n % 10);
      xMethod(n / 10);
    }
  }
}   

18.4 编写一个递归的数学定义来计算 2 n 2^n 2n,其中n为正整数

f(n) = 2 if n = 1
f(n) = 2 * 2^(n-1) for (n > 1)

18.5 编写一个递归的数学定义来计算 x n x^n xn,其中n为正整数,x为实数

f(n) = x if n = 1
f(n) = x * x^(n-1) for (n > 1)

18.6 编写一个递归的数学定义来计算1+2+3+···+n,其中 n 为正整数

f(n) = 1 if n = 1
f(n) = f(n-1) + n for (n > 1)

18.3章节习题

18.7 给出以下两个程序的输出

// 5 4 3 2 1
public class Test {
    
    
  public static void main(String[] args) {
    
    
    xMethod(5);
  }

  public static void xMethod(int n) {
    
    
    if (n > 0) {
    
    
      System.out.print(n + " ");
      xMethod(n - 1);
    }
  }
}   
// 1 2 3 4 5
public class Test {
    
    
  public static void main(String[] args) {
    
    
    xMethod(5);
  }

  public static void xMethod(int n) {
    
    
    if (n > 0) {
    
    
      xMethod(n - 1);
      System.out.print(n + " ");
    }
  }
}   

18.8 下面方法中的错误是什么?

// n永远不会等于0
public class Test {
    
    
  public static void main(String[] args) {
    
    
    xMethod(1234567);
  }

  public static void xMethod(double n) {
    
    
    if (n != 0) {
    
    
      System.out.print(n);
      xMethod(n / 10);
    }
  }
}
// 无限递归创建新对象test
public class Test {
    
     
  public static void main(String[] args) {
    
    
    Test test = new Test();
    System.out.println(test.toString());
  }

  public Test() {
    
    
    Test test = new Test();
  }
} 

18.9 程序清单18-2中,对fib(6)进行了多少次fib方法的调用?

25次

18.4章节习题

18.10 描述递归方法的特点

有一个基础情况用来终止的动作,而进行的动作

将大的问题简化为小的问题,但是问题的本质都是一样的

18.11对于程序清单18-3中的 isPalindrome 方法,什么是基础情况?当调用 isPalindrome(“abdxcxdba”) 时,该方法被调用多少次?

字符串长度小于1或者首尾字符不相同

被调用5次

18.12 使用程序清单18-3中定义的方法,给出isPalindrome(“abcba”)的调用栈。

首先看aa,然后是bb,最后是c

18.5章节习题

18.13 使用程序清单18-4中定义的方法,给出 isPalindrome(“abcba”) 的调用栈。

首先看aa,然后是bb,最后是c

18.14 使用程序清单18-5中定义的方法,给出 selectionSort(new double[]{2,3,5,1}) 的调用栈。

1,2,3,4

18.15 什么是递归辅助方法?

一个有更多参数的重载方法

18.6章节习题

18.16 getSize 方法的基础情况是什么?

d本身就是一个文件

18.17 程序是如何得到一个给定目录下所有的文件和目录的?

储存到File[]

18.18 如果一个目录具有三个子目录,每个子目录具有四个文件,getSize 方法将调用多少次?

20次

18.19 如果目录为空的话(即,不包含任何文件),程序可以工作吗?

可以

18.20 如果第 20 行替换成以下代码,程序可以工作吗?

for (int i = 0; i < files.length; i++) 

不行,目录可能是空的

18.21 如果第 20 21 行替换成以下代码的话,程序可以工作吗?

for (File file: files) 
  size += getSize(file); // Recursive call

不行,files可能是null

18.7章节习题

18.22 程序清单18-8 中调用 moveDisks(5, ‘A’, ‘B’, ‘C’) 的话,将会调用 moveDisks 方法多少次

2 5 − 1 2^5 -1 251

18.8章节习题

18.23 如何得到两个点的中点?

p1.midpoint(p2)

18.24 displayTriangles方法的基础情况是什么?

order == 0

18.25 对于0阶,1阶,2阶,以及n阶的思瑞平斯基三角形,将分别调用多少次 diaplayTriangles 方法?

1, 4, 10, 1 + 3 n 1 + 3^n 1+3n

18.26 如果输入一个负数阶值,将发生什么?如何修正代码中的这个问题?

会发生无限循环

添加if (order < 0)

18.27 重写代码第71-77 行,绘制三条连接点的线段来绘制三角形,取代原来使用绘制多边形的方法来绘制的方法。

// Draw a triangle to connect three points
Line line1 = new Line(p1.getX(), p1.getY(), p2.getX(), p2.getY());
Line line2 = new Line(p2.getX(), p2.getY(), p3.getX(), p3.getY());
Line line3 = new Line(p3.getX(), p3.getY(), p1.getX(), p1.getY());

this.getChildren().addAll(line1, line2, line3);

18.9章节习题

18.28 下面的语句中哪些是正确的:

·任何递归方法都可以转换为非递归方法

·执行递归方法比执行非递归方法要占用更多的时间和内存

·递归方法总是比非递归方法简单一些

·递归方法中总是有一个选择语句检查是否达到基础情况

对对错对

18.29 引起栈溢出异常的原因是什么?

栈空间用完了

18.10章节习题

18.30 指出本章中的尾递归方法。

18.4, 18.5, 18.6

18.31 使用尾递归重写程序清单18-2中的fib方法。

/** Return the Fibonacci number for the specified index */
public static long fib(long index) {
    
    
  return fib(index, 1, 0);
}

/** Auxiliary tail-recursive method for fib */
private static int fib(long index, int next, int result) {
    
    
  if (index == 0) 
    return result;
  else
    return fib(index - 1, next + result, next);
}

编程练习题

*18.1 (计算阶乘)

使用10.9节介绍的 Biglnteger 类,求得大数字的阶乘(例如,100!)。使用递归实现 factorial 方法。编写一个程序,提示用户输人一个整数,然后显示它的阶乘

package com.example.demo;

import java.math.BigInteger;
import java.util.Scanner;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        boolean loopOn = true;

        Scanner scanner = new Scanner(System.in);
        System.out.print("Input an integer: ");
        BigInteger integer = scanner.nextBigInteger();

        while (loopOn) {
    
    
            if (integer.compareTo(BigInteger.ZERO) <= 0) {
    
    
                System.out.print("Cannot be 0 or negative value, try again! Input an integer: ");
                integer = scanner.nextBigInteger();
            } else {
    
    
                loopOn = false;
            }
        }
        System.out.println("The factorial of " + integer + " is " + factorial(integer));
    }

    public static BigInteger factorial(BigInteger integer) {
    
    
        if (integer.compareTo(BigInteger.ONE) == 0) {
    
    
            return BigInteger.ONE;
        } else {
    
    
            return integer.multiply(factorial(integer.subtract(BigInteger.ONE)));
        }
    }
}

输出结果:

Input an integer: 100
The factorial of 100 is 933262154439xxxxx
*18.2 (斐波那契數)

使用迭代改写程序淸单18-2中的fib方法

package com.example.demo;

import java.util.Scanner;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        Scanner scanner = new Scanner(System.in);
        System.out.print("Input an index: ");
        int integer = scanner.nextInt();

        int f0 = 0;
        int f1 = 1;
        int currentFib = 0;
        for (int i = 0; i < integer - 2; i++) {
    
    
            currentFib = f0 + f1;
            f0 = f1;
            f1 = currentFib;
        }
        System.out.println("The fib of " + integer + " is " + currentFib);
    }
}

输出结果:

Input an index: 11
The fib of 11 is 55
*18.3 (使用递归求最大公约数)

求最大公约数的gcd(m, n)方法也可以如下递归地定义:

• 如果m%n为0,那么gcd(m, n)的值为n

• 否则,gcd(m, n)就是gcd(n, m%n)

编写一个递归的方法来求最大公约数。编写一个测试程序,提示用户输入两个整数,显示它们的最大公约数

package com.example.demo;

import java.util.Scanner;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        Scanner scanner = new Scanner(System.in);
        System.out.print("Input two numbers: ");
        int m = scanner.nextInt();
        int n = scanner.nextInt();

        System.out.println("The fib of " + m + " and " + n + " is " + gcd(m, n));
    }

    public static int gcd(int m, int n) {
    
    
        if (m % n == 0)
            return n;
        else
            return gcd(n, m%n);
    }
}

输出结果:

Input two numbers: 4 6
The fib of 4 and 6 is 2
18.4 (对数列求和)

编写一个递归方法来计算下面的级数:

m ( i ) = 1 + 1 2 + 1 3 + ⋅ ⋅ ⋅ + 1 i m(i) = 1 + \frac{1}{2} + \frac{1}{3} + ··· + \frac{1}{i} m(i)=1+21+31+⋅⋅⋅+i1

编写一个测试程序,为i = 1, 2, …, 10显示m(i)

package com.example.demo;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        for (double i = 1; i <= 10; i++) {
    
    
            System.out.printf("When i is %.0f, the sum of the fraction is %.2f\n", i, sum(i));
        }
    }

    public static double sum(double m) {
    
    
        if (m == 0)
            return 0;
        else
            return 1 / m + sum(m - 1);
    }
}

输出结果:

When i is 1, the sum of the fraction is 1.00
When i is 2, the sum of the fraction is 1.50
When i is 3, the sum of the fraction is 1.83
When i is 4, the sum of the fraction is 2.08
When i is 5, the sum of the fraction is 2.28
When i is 6, the sum of the fraction is 2.45
When i is 7, the sum of the fraction is 2.59
When i is 8, the sum of the fraction is 2.72
When i is 9, the sum of the fraction is 2.83
When i is 10, the sum of the fraction is 2.93
18.5 (对数列求和)

编写一个递归的方法来计算下面的级数:

m ( i ) = 1 3 + 2 5 + 3 7 + 4 9 + 5 11 + 6 13 + ⋅ ⋅ ⋅ + i 2 i + 1 m(i) = \frac{1}{3} + \frac{2}{5} + \frac{3}{7} + \frac{4}{9} + \frac{5}{11} + \frac{6}{13} + ··· + \frac{i}{2i + 1} m(i)=31+52+73+94+115+136+⋅⋅⋅+2i+1i

编写一个测试程序,为i = 1, 2, …, 10显示m(i)

package com.example.demo;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        for (double i = 1; i <= 10; i++) {
    
    
            System.out.printf("When i is %.0f, the sum of the fraction is %.2f\n", i, sum(i));
        }
    }

    public static double sum(double m) {
    
    
        if (m == 0)
            return 0;
        else
            return m / (2 * m + 1) + sum(m - 1);
    }
}

输出结果:

When i is 1, the sum of the fraction is 0.33
When i is 2, the sum of the fraction is 0.73
When i is 3, the sum of the fraction is 1.16
When i is 4, the sum of the fraction is 1.61
When i is 5, the sum of the fraction is 2.06
When i is 6, the sum of the fraction is 2.52
When i is 7, the sum of the fraction is 2.99
When i is 8, the sum of the fraction is 3.46
When i is 9, the sum of the fraction is 3.93
When i is 10, the sum of the fraction is 4.41
*18.6 (对数列求和)

编写一个递归的方法来计算下面的级数:

m ( i ) = 1 2 + 2 3 + ⋅ ⋅ ⋅ + i i + 1 m(i) = \frac{1}{2} + \frac{2}{3} + ··· + \frac{i}{i + 1} m(i)=21+32+⋅⋅⋅+i+1i

编写一个测试程序,为i = 1, 2, …, 10显示m(i)

package com.example.demo;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        for (double i = 1; i <= 10; i++) {
    
    
            System.out.printf("When i is %.0f, the sum of the fraction is %.2f\n", i, sum(i));
        }
    }

    public static double sum(double m) {
    
    
        if (m == 0)
            return 0;
        else
            return m / (m + 1) + sum(m - 1);
    }
}

输出结果:

When i is 1, the sum of the fraction is 0.50
When i is 2, the sum of the fraction is 1.17
When i is 3, the sum of the fraction is 1.92
When i is 4, the sum of the fraction is 2.72
When i is 5, the sum of the fraction is 3.55
When i is 6, the sum of the fraction is 4.41
When i is 7, the sum of the fraction is 5.28
When i is 8, the sum of the fraction is 6.17
When i is 9, the sum of the fraction is 7.07
When i is 10, the sum of the fraction is 7.98
*18.7 (斐波那契数列)

修改程序淸单18-2,使程序可以找出调用fib方法的次数

package com.example.demo;

import java.util.Scanner;

public class Test {
    
    
    public static int times = 0;

    public static void main(String[] args) {
    
    
        // Create a Scanner
        Scanner input = new Scanner(System.in);
        System.out.print("Enter an index for a Fibonacci number: ");
        int index = input.nextInt();

        // Find and display the Fibonacci number
        System.out.println("The Fibonacci number at index "
                + index + " is " + fib(index) + " in " + times + " times");
    }

    public static long fib(long index) {
    
    
        times++;
        if (index == 0) // Base case
            return 0;
        else if (index == 1) // Base case
            return 1;
        else  // Reduction and recursive calls
            return fib(index - 1) + fib(index - 2);
    }
}

输出结果:

Enter an index for a Fibonacci number: 4
The Fibonacci number at index 4 is 3 in 9 times
*18.8 (以逆序输出一个整数中的數字)

编写一个递归方法,使用下面的方法头在控制台上以逆序显示一个 int 型的值:

public static void reverseDisplay(int value)

例如,reverseDisplay(12345)显示的是54321。编写一个测试程序,提示用户输入一个整数 . 然后显示它的逆序数字。

package com.example.demo;

import java.util.Scanner;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        Scanner input = new Scanner(System.in);
        System.out.print("Enter an integer: ");
        int number = input.nextInt();

        System.out.print("After reverse, the number is ");
        reverseDisplay(number);
    }

    public static void reverseDisplay(int number) {
    
    
        // 如果数字只有1位数,就直接输出
        if (number / 10 < 1) {
    
    
            System.out.print(number);
        } else {
    
    
            // 取余运算得到个位的数字
            int digit = number % 10;
            // 输出当前个位的数字
            System.out.print(digit);
            // 将数字去除最低位,缩小10倍
            reverseDisplay(number /= 10);
        }
    }
}

输出结果:

Enter an integer: 123456
After reverse, the number is 654321
*18.9 (以逆序输出一个字符串中的字符)

编写一个递归方法,使用下面的方法头在控制台上以逆序显示一个字符串:

public static void reverseDisplay(String value)

例如,reverseDisplay(“abcd”)显示的是dcba。编写一个测试程序,提示用户输入一个字符串,然后显示它的逆序字符串

package com.example.demo;

import java.util.Scanner;

public class Test {
    
    
    // 定义静态全局变量(也就是类的字段)buffer储存颠倒以后的字符串,定义为静态是因为reverseDisplay()是静态方法,只能接受静态变量;定义为全局变量是因为,如果把buffer定义在方法中,每次递归就会导致buffer被清空
    static StringBuffer buffer = new StringBuffer();

    public static void main(String[] args) {
    
    
        Scanner input = new Scanner(System.in);
        System.out.print("Enter an string: ");
        String string = input.next();

        System.out.print("After reverse, the string is ");
        reverseDisplay(string);
    }

    public static void reverseDisplay(String value) {
    
    
        // 如果字符串长度为1,就append以后输出
        if (value.length() == 1) {
    
    
            buffer.append(value);
            System.out.print(buffer);
        } else {
    
    
            // 否则,就append最后一个字符,然后从字符串中删除最后一个字符
            buffer.append(value.substring(value.length() - 1));
            reverseDisplay(value.substring(0, value.length() - 1));
        }
    }
}

输出结果:

Enter an string: abcd
After reverse, the string is dcba
*18.10 (字符串中某个指定字符出现的次教)

编写一个递归方法,使用下面的方法头给出一个指定字符在字符串中出现的次数。

public static int count(String str,char a)

例如,count(“Welcome”, ‘e’)会返回 2。编写一个测试程序,提示用户输入一个字符串和一个字符,显示该字符在字符串中出现的次数。

package com.example.demo;

import java.util.Scanner;

public class Test {
    
    
    static int times = 0;

    public static void main(String[] args) {
    
    
        Scanner input = new Scanner(System.in);
        System.out.print("Enter a string and a character: ");
        String string = input.next();
        char uChar = input.next().charAt(0);
        System.out.print("Frequency: " + count(string, uChar));
    }

    public static int count(String str, char a) {
    
    
        // 如果字符串长度为1,就检查这个字符串是不是匹配到指定字符
        if (str.length() == 1) {
    
    
            if (str.charAt(0) == a) {
    
    
                times++;
            }
            return times;
        } else {
    
    
            // 否则,就检查这个字符串的最后一个字符是不是匹配到指定字符,然后把字符串的最后一个字符删除
            if (str.charAt(str.length() - 1) == a) {
    
    
                times++;
            }
            count(str.substring(0, str.length() - 1), a);
            return times;
        }
    }
}

输出结果:

Enter a string and a character: welcome e
Frequency: 2
*18.11 (使用递归求一个整数各位数之和)

编写一个递归方法,使用下面的方法头计算一个整数中各位数之和:

public static int sumDigits(long n)

例如,sumDigits(234)返回的是 2+3+4=9。编写一个测试程序,提示用户输入一个整数, 然后显示各位数字之和。

package com.example.demo;

import java.util.Scanner;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        Scanner input = new Scanner(System.in);
        System.out.print("Enter an integer: ");
        int integer = input.nextInt();
        System.out.print("Sum: " + sumDigits(integer));
    }

    // 方法的本质是求整数的最后一位,并把最后一位逐渐累加
    public static int sumDigits(int n) {
    
    
        int sum = 0;

        // 如果只有1位数,就直接加上去
        if (n < 10) {
    
    
            sum = sum + n;
            return sum;
        } else {
    
    
            // 否则,求出最后一位
            int last1Digit = n % 10;
            // 然后加上去
            sum = sum + last1Digit + sumDigits(n / 10);
            return sum;
        }
    }
}

输出结果:

Enter an integer: 123
Sum: 6
**18.12 (以逆序打印字符串中的字符)

使用辅助方法改写编程练习题18.9,将子串的high下标传递给这个方法。辅助方法头为 :

public static void reverseDisplay(String value, int high)
package com.example.demo;

import java.util.Scanner;

public class Test {
    
    
    // 定义静态全局变量(也就是类的字段)buffer储存颠倒以后的字符串,定义为静态是因为reverseDisplay()是静态方法,只能接受静态变量;定义为全局变量是因为,如果把buffer定义在方法中,每次递归就会导致buffer被清空
    static StringBuffer buffer = new StringBuffer();

    public static void main(String[] args) {
    
    
        Scanner input = new Scanner(System.in);
        System.out.print("Enter an string: ");
        String string = input.next();

        System.out.print("After reverse, the string is ");
        reverseDisplay(string);
    }

    public static void reverseDisplay(String value) {
    
    
        reverseDisplay(value, value.length() - 1);
    }

    public static void reverseDisplay(String value, int high) {
    
    
        // 如果字符串长度为1,就append以后输出
        if (value.length() == 1) {
    
    
            buffer.append(value);
            System.out.print(buffer);
        } else {
    
    
            // 否则,就append最后一个字符,然后从字符串中删除最后一个字符
            buffer.append(value.substring(high));
            reverseDisplay(value.substring(0, high), high - 1);
        }
    }
}

输出结果:

Enter an string: hello
After reverse, the string is olleh
*18.13 (找出數组中的最大数)

编写一个递归方法,返回一个数组中的最大整数。编写一个测试程序, 提示用户输人一个包含 8 个整数的列表,然后显示最大的元素。

package com.example.demo;

import java.util.Scanner;

public class Test {
    
    
    static int currIdx = 0;

    public static void main(String[] args) {
    
    
        Scanner input = new Scanner(System.in);
        System.out.print("Enter an integer array: ");
        String[] array = input.nextLine().split("\\s+");
        int[] numbers = new int[array.length];
        for (int i = 0; i < array.length; i++) {
    
    
            numbers[i] = Integer.parseInt(array[i]);
        }

        System.out.print("The biggest number in your array is " + max(Integer.MIN_VALUE, numbers));
    }

    static int max(int large, int[] integers) {
    
    
        if (currIdx == integers.length) {
    
    
            return large;
        }
        large = Math.max(large, integers[currIdx++]);
        return max(large, integers);
    }
}

输出结果:

Enter an integer array: 55 66 8 90
The biggest number in your array is 90
*18.14 (求字符串中大写字母的个教)

编写一个递归方法,返回一个字符串中大写字母的个数。编写一个测试程序,提示用户输人一个字符串,然后显示该字符串中大写字母的数目

本题只有一个终止情况,而没有常规执行代码块

package com.example.demo;

import java.util.Scanner;

public class Test {
    
    
    static int times = 0;
    static int index = 0;

    public static void main(String[] args) {
    
    
        Scanner input = new Scanner(System.in);
        System.out.print("Enter an string: ");
        String string = input.nextLine();
        byte[] byteArray = new byte[string.length()];
        for (int i = 0; i < string.length(); i++) {
    
    
            byteArray[i] = (byte) string.charAt(i);
        }

        System.out.print("The times that uppercase letters show: ");
        times(byteArray);
        System.out.println(times);
    }

    static void times(byte[] byteArray) {
    
    
        if (index < byteArray.length) {
    
    
            if (byteArray[index] >= 'A' && byteArray[index] <= 'Z') {
    
    
                times++;
            }
            index++;
            times(byteArray);
        }
    }
}

输出结果:

Enter an string: srgHRSD
The times that uppercase letters show: 4
*18.15 (字符串中某个指定字符出现的次数)

使用辅助方法改写编程练习题18.10,将子串的high下标传递给这个方法。辅助方法头为:

public static int count(String str,char a,int high)
package com.example.demo;

import java.util.Scanner;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        Scanner in = new Scanner(System.in);
        System.out.print("Enter a String and a character to count it's occurrences:");
        String str = in.next();
        char ch = in.next().charAt(0);

        System.out.println("Character " + ch + " occurs " + count(str, ch) + " times in " + str);
        in.close();
    }

    public static int count(String str, char a, int high) {
    
    
        if (high > 0) {
    
    
            return str.charAt(high) == a ? (1 + count(str, a, high - 1)) : (count(str, a, high - 1));
        } else {
    
    
            return 0;
        }
    }

    public static int count(String str, char a) {
    
    
        return count(str, a, str.length() - 1);
    }
}

输出结果:

Enter a String and a character to count it's occurrences:welcome e
Character e occurs 2 times in welcome
*18.16 (求数组中大写字母的个数)

编写一个递归的方法,返回一个字符数组中大写字母的个数。需要定义下面两个方法。第二个方法是一个递归辅助方法。

public static int count(char[] chars)
public static int count(char[] chars, int high)

编写一个测试程序,提示用户在一行中输入一个字符列表,然后显示该列表中大写字母的 个数。

package com.example.demo;

import java.util.Scanner;

public class Test {
    
    
    static int times = 0;
    static int index = 0;

    public static void main(String[] args) {
    
    
        Scanner input = new Scanner(System.in);
        System.out.print("Enter an string: ");
        String string = input.nextLine();
        byte[] byteArray = new byte[string.length()];
        for (int i = 0; i < string.length(); i++) {
    
    
            byteArray[i] = (byte) string.charAt(i);
        }

        System.out.print("The times that uppercase letters show: ");
        times(byteArray);
        System.out.println(times);
    }

    static void times(byte[] byteArray) {
    
    
        times(byteArray, index);
    }

    public static void times(byte[] byteArray, int high) {
    
    
        if (high < byteArray.length) {
    
    
            if (byteArray[high] >= 'A' && byteArray[high] <= 'Z') {
    
    
                times++;
            }
            high++;
            times(byteArray, high);
        }
    }
}

输出结果:

Enter an string: segfegKBIGBKIUGVIUhnoihHOUH
The times that uppercase letters show: 16
*18.17 (数组中某个指定字符出现的次数)

编写一个递归的方法,求出数组中一个指定字符出现的次数。需要定义下面两个方法,第二个方法是一个递归的辅助方法。

public static int count(char[] chars, char ch)
public static int count(char[] chars, char ch, int high)

编写一个测试程序,提示用户在一行中输入一个字符列表以及一个字符,然后显示该字符在列表中出现的次数。

package com.example.demo;

import java.util.Scanner;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        Scanner in = new Scanner(System.in);
        System.out.print("Enter a list of characters in one line: ");
        String line = in.nextLine();
        char[] chars = line.toCharArray();

        System.out.println("Enter a single character: ");
        char ch = in.next().charAt(0);
        System.out.println("The character " + ch + " occurs " + count(chars, ch) + " times.");
        in.close();
    }

    public static int count(char[] chars, char ch) {
    
    
        return count(chars, ch, chars.length - 1);
    }

    public static int count(char[] chars, char ch, int high) {
    
    
        if (high > 0) {
    
    
            return chars[high] == ch ? (1 + count(chars, ch, high - 1)) : (count(chars, ch, high - 1));
        } else {
    
    
            return 0;
        }
    }
}

输出结果:

Enter a list of characters in one line: sdvagsvsvdaaa
Enter a single character: 
a
The character a occurs 4 times.
*18.18 (汉诺塔)

修改程序清单18-8,使程序可以计算将n个盘子从塔 A 移到塔 B 所需的移动次数

package com.example.demo;

import java.util.Scanner;

public class Test {
    
    
    static int times = 0;

    public static void main(String[] args) {
    
    
        // Create a Scanner
        Scanner input = new Scanner(System.in);
        System.out.print("Enter number of disks: ");
        int n = input.nextInt();

        // Find the solution recursively
        System.out.println("The moves are:");
        moveDisks(n, 'A', 'B', 'C');
        System.out.print("Total times are " + times);
    }

    public static void moveDisks(int n, char fromTower, char toTower, char auxTower) {
    
    
        times++;

        if (n == 1) // Stopping condition
            System.out.println("Move disk " + n + " from " +
                    fromTower + " to " + toTower);
        else {
    
    
            moveDisks(n - 1, fromTower, auxTower, toTower);
            System.out.println("Move disk " + n + " from " +
                    fromTower + " to " + toTower);
            moveDisks(n - 1, auxTower, toTower, fromTower);
        }
    }
}

输出结果:

Enter number of disks: 3
The moves are:
Move disk 1 from A to B
Move disk 2 from A to C
Move disk 1 from B to C
Move disk 3 from A to B
Move disk 1 from C to A
Move disk 2 from C to B
Move disk 1 from A to B
Total times are 7
*18.19 (思瑞平斯基三角形)

修改程序清单18-9, 开发一个程序让用户使用 “+” 和 "-” 按钮将当前阶数增1或减1,如图 18 -12a 所示。初始阶数为0。如果当前阶数为0,就忽略按钮。

package com.example.demo;

import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.stage.Stage;

public class Test extends javafx.application.Application {
    
    

    @Override
    public void start(Stage primaryStage) {
    
    
        SierpinskiTrianglePane trianglePane = new SierpinskiTrianglePane();

        Button minusButton = new Button("-");
        minusButton.setOnAction(event -> trianglePane.decreaseByOne());
        Button plusButton = new Button("+");
        plusButton.setOnAction(event -> trianglePane.increaseByOne());

        // Pane to hold plus and minus buttons
        HBox hBox = new HBox(10);
        hBox.setAlignment(Pos.CENTER);
        hBox.getChildren().setAll(minusButton, plusButton);

        BorderPane borderPane = new BorderPane();
        borderPane.setCenter(trianglePane);
        borderPane.setBottom(hBox);

        Scene scene = new Scene(borderPane, 200, 210);
        primaryStage.setTitle("SierpinskiTriangle");
        primaryStage.setScene(scene);
        primaryStage.show();
        trianglePane.paint();

        scene.widthProperty().addListener(ov -> trianglePane.paint());
        scene.heightProperty().addListener(ov -> trianglePane.paint());
    }

    /**
     * Pane for displaying triangles
     */
    static class SierpinskiTrianglePane extends Pane {
    
    
        private int order = 0;

        /**
         * Set a new order
         */
        public void setOrder(int order) {
    
    
            this.order = order;
            paint();
        }

        SierpinskiTrianglePane() {
    
    
        }

        protected void paint() {
    
    
            // Select three points in proportion to the pane size
            Point2D p1 = new Point2D(getWidth() / 2, 10);
            Point2D p2 = new Point2D(10, getHeight() - 10);
            Point2D p3 = new Point2D(getWidth() - 10, getHeight() - 10);
            this.getChildren().clear(); // Clear the pane before redisplay
            displayTriangles(order, p1, p2, p3);
        }

        private void displayTriangles(int order, Point2D p1,
                                      Point2D p2, Point2D p3) {
    
    
            if (order == 0) {
    
    

                Polygon triangle = new Polygon();
                triangle.getPoints().addAll(p1.getX(), p1.getY(), p2.getX(),
                        p2.getY(), p3.getX(), p3.getY());
                triangle.setStroke(Color.BLACK);
                triangle.setFill(Color.WHITE);
                this.getChildren().add(triangle);
            } else {
    
    

                Point2D p12 = p1.midpoint(p2);
                Point2D p23 = p2.midpoint(p3);
                Point2D p31 = p3.midpoint(p1);
                // Recursively display three triangles
                displayTriangles(order - 1, p1, p12, p31);
                displayTriangles(order - 1, p12, p2, p23);
                displayTriangles(order - 1, p31, p23, p3);
            }
        }

        public void decreaseByOne() {
    
    
            if (order > 0) {
    
    
                setOrder(order - 1);
            }
        }

        public void increaseByOne() {
    
    
            setOrder(order + 1);
        }
    }
}

输出结果:

在这里插入图片描述

*18.20 (显示多个圓)

编写一个Java程序显示多个圆,如图18-12b所示。这些圆都处于面板的中心位置。两个相邻圆之间相距10像素,面板和最大圆之间也相距10像素。

package com.example.demo;

import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

public class Test extends javafx.application.Application {
    
    
    int radius = 5;
    int increment = 10;

    @Override
    public void start(Stage primaryStage) {
    
    
        StackPane stackPane = new StackPane();
        Scene scene = new Scene(stackPane, 200, 200);

        drawCircle(stackPane);

        primaryStage.setTitle("Test");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public void drawCircle(StackPane sp) {
    
    
        Circle circle = new Circle(radius);
        // 判断pane的宽度减去圆的直径以后,是否还留有10像素边距,由于需要有左右/上下两个边距,因此10 * 2 = 20
        if (sp.getWidth() - circle.getRadius() * 2 >= 20) {
    
    
            circle.setStroke(Color.BLACK);
            circle.setFill(Color.TRANSPARENT);
            sp.getChildren().add(circle);
            radius = radius + increment;
            drawCircle(sp);
        }
    }
}

输出结果:

在这里插入图片描述

*18.21 (将十进制数转换为二进制数)

编写一个递归方法,将一个十进制数转换为一个二进制数的字符串。方法头如下:

public static String dec2Bin(int value)

编写一个测试程序,提示用户输入一个十进制数,然后显示等价的二进制数。

package com.example.demo;

import java.util.Scanner;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        Scanner in = new Scanner(System.in);
        System.out.print("Enter a decimal(base 10) number as an integer: ");
        int decValue = in.nextInt();
        System.out.println(decValue + " converted to binary is: " + dec2Bin(decValue));

        in.close();
    }

    public static String dec2Bin(int value) {
    
    
        return dec2Bin(value, "");
    }


    static String dec2Bin(int value, String binary) {
    
    
        if (value > 0) {
    
    
            return dec2Bin(value / 2, (value % 2) + binary);
        }
        return binary;
    }
}

输出结果:

Enter a decimal(base 10) number as an integer: 21
21 converted to binary is: 10101
*18.22 (将十进制數转换为十六进制数)

编写一个递归方法,将一个十进制数转换为一个十六进制数的字符串。方法头如下:

public static String dec2Hex(int value)

编写一个测试程序,提示用户输入一个十进制数,然后显示等价的十六进制数

package com.example.demo;

import java.util.Scanner;

public class Test {
    
    
    static String hexValue = "";

    public static void main(String[] args) {
    
    
        Scanner in = new Scanner(System.in);
        System.out.print("Enter a decimal number to convert to hex: ");
        int number = in.nextInt();

        System.out.println(number + " is equivalent to hexadecimal number " + dec2Hex(number));
        in.close();
    }

    public static String dec2Hex(int value) {
    
    
        if (value < 16) {
    
    
            return getAsHexNumber(value) + hexValue;
        }
        hexValue = getAsHexNumber(value % 16) + hexValue;
        return dec2Hex(value / 16);
    }

    static String getAsHexNumber(int value) {
    
    
        switch (value) {
    
    
            case 10:
                return "A";
            case 11:
                return "B";
            case 12:
                return "C";
            case 13:
                return "D";
            case 14:
                return "E";
            case 15:
                return "F";
            default:
                return String.valueOf(value);
        }
    }
}

输出结果:

Enter a decimal number to convert to hex: 436
436 is equivalent to hexadecimal number 1B4
*18.23 (将二进制数转换为十进制数)

编写一个递归方法,将一个字符串形式的二进制数转换为一个十进制数。方法头如下:

public static int bin2Dec(String binarystring)

编写一个测试程序,提示用户输入一个二进制字符串,然后显示等价的十进制数。

package com.example.demo;

import java.util.Scanner;

public class Test {
    
    
    static int pow = 0;
    static int decimal;

    public static void main(String[] args) {
    
    
        Scanner in = new Scanner(System.in);
        System.out.print("Enter the binary number to convert to decimal: ");
        String binaryNum = in.next();
        System.out.println(binaryNum + " binary number is equivalent to the decimal integer: " + bin2Dec(binaryNum));
        in.close();

    }

    public static int bin2Dec(String binaryString) {
    
    
        if (binaryString.length() == 0) {
    
    
            return decimal;
        }
        char binaryDigit = binaryString.charAt(binaryString.length() - 1);
        int binaryValue = Integer.parseInt(binaryDigit + "");
        decimal += binaryValue * ((int) Math.pow(2, pow));
        pow += 1;
        return bin2Dec(binaryString.substring(0, binaryString.length() - 1));
    }
}

输出结果:

Enter the binary number to convert to decimal: 11110001
11110001 binary number is equivalent to the decimal integer: 241
18.24 (将十六进制数转換为十进制数)

编写一个递归方法,将一个字符串形式的十六进制数转换为一个十进制数。方法头如下:

public static int hex2Dec(String hexString)

编写一个测试程序,提示用户输入一个十六进制字符串,然后显示等价的十进制数。

package com.example.demo;

import java.util.Scanner;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        Scanner input = new Scanner(System.in);
        System.out.print("Enter a hex number: ");
        String hex = input.nextLine();
        System.out.println(hex + " is decimal " + hex2Dec(hex));
    }

    public static long hex2Dec(String hexString) {
    
    
        return hex2Dec(hexString, 0, hexString.length() - 1);
    }

    public static long hex2Dec(String hexString, int low, int high) {
    
    
        if (high < low)
            return 0;
        else {
    
    
            return hex2Dec(hexString, low, high - 1) * 16
                    + hexCharToDecimal(hexString.charAt(high));
        }
    }

    public static long hexCharToDecimal(char ch) {
    
    
        if ('A' <= ch && ch <= 'F')
            return 10 + ch - 'A';
        else
            return ch - '0';
    }
}

输出结果:

Enter a hex number: B2
B2 is decimal 178
**18.25 (字符串排列)

编写一个递归方法,输出一个字符串的所有排列。例如,对于字符串abc,输出为:

abc acb bac bca cab cba
package com.example.demo;

import java.util.Scanner;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        Scanner in = new Scanner(System.in);
        System.out.print("Enter a string: ");
        String input = in.next();
        in.close();

        displayPermutation(input);
    }

    public static void displayPermutation(String s) {
    
    
        displayPermutation(" ", s);
    }

    public static void displayPermutation(String s1, String s2) {
    
    
        if (s2.length() > 0) {
    
    
            for (int i = 0; i < s2.length(); i++) {
    
    
                String shuffle1 = s1 + s2.charAt(i);
                String shuffle2 = s2.substring(0, i) + s2.substring(i + 1);
                displayPermutation(shuffle1, shuffle2);
            }
        } else {
    
    
            System.out.println(s1);
        }
    }
}

输出结果:

Enter a string: abc
 abc
 acb
 bac
 bca
 cab
 cba
**18.26 (创建一个迷宫)

编写一个程序,在迷宫中寻找一条路径,如图18-13a所示。该迷宫由一个8 x 8 的棋盘表示。路径必须满足下列条件:

路径在迷宫的左上角单元和右下角单元之间。

程序允许用户在一个单元格中放入或移走一个标志。路径由相邻的未放标志的单元格组成。如果两个单元格在水平方向或垂直方向相邻,但在对角线方向上不相邻,那么就称它们是相邻的。

路径不包含能形成一个正方形的单元格。例如,在图18-13b中的路径就不满足这个条件。( 这个条件使得面板上的路径很容易识别。)

package com.example.demo;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class Test extends Application {
    
    
    // 由8 * 8网格组成的数组
    // 在Java中,除了抽象类和接口,类都可以作为数组的元素类型
    private GameCell[][] board = new GameCell[8][8];

    private final double WIDTH = 400;
    private final double HEIGHT = 400;

    private final Button findButton = new Button("Find Path");
    private final Button clearButton = new Button("Clear Path");
    private final Label mainLabel = new Label();

    @Override
    public void start(Stage primaryStage) {
    
    
        GridPane gridPane = new GridPane();
        // 添加64个正方形到gridPane并居中,这里没有新建一个GameCell的引用变量,而是直接把new GameCell()赋值给board数组,否则就需要声明64个引用变量了
        // 不能只声明一个引用变量然后添加矩形,因为一个节点只能添加一次
        for (int i = 0; i < 8; i++) {
    
    
            for (int j = 0; j < 8; j++) {
    
    
                gridPane.add(board[i][j] = new GameCell(), j, i);
            }
        }

        gridPane.setAlignment(Pos.CENTER);

        // 创建两个操作按钮并添加到HBox
        HBox hBox = new HBox(5);
        hBox.setAlignment(Pos.CENTER);
        HBox.setMargin(findButton, new Insets(5, 5, 10, 5));
        HBox.setMargin(clearButton, new Insets(5, 5, 10, 5));
        hBox.getChildren().addAll(findButton, clearButton);

        // 用BorderPane包裹其他子Pane
        BorderPane pane = new BorderPane();
        // 设置警告label在顶部
        pane.setTop(mainLabel);
        BorderPane.setAlignment(mainLabel, Pos.CENTER);
        // 设置矩形GridPane在中部
        pane.setCenter(gridPane);
        // 设置按钮hBox在底部
        pane.setBottom(hBox);

        Scene scene = new Scene(pane, WIDTH + 15, HEIGHT + 80);
        primaryStage.setTitle(getClass().getName());
        primaryStage.setScene(scene);
        primaryStage.show();

        // 按钮触发
        findButton.setOnAction(e -> findPath());
        clearButton.setOnAction(e -> clear());
    }

    public void findPath() {
    
    
        if (findPath(0, 0)) {
    
    
            mainLabel.setText("path found");
        } else {
    
    
            mainLabel.setText("No legal path exists");
        }
    }

    public boolean findPath(int row, int col) {
    
    
        board[row][col].visit();

        if ((col == 7) && (row == 7)) {
    
    
            board[row][col].select();
            return true;
        }

        if ((row > 0) && !board[row - 1][col].isMarked() &&
                !board[row - 1][col].blocked() && !board[row - 1][col].visited()) {
    
    
            block(row, col);

            if (findPath(row - 1, col)) {
    
    
                board[row][col].select();
                return true;
            }

            unblock(row, col);
        }

        if ((row < 7) && !board[row + 1][col].isMarked() &&
                !board[row + 1][col].blocked() && !board[row + 1][col].visited()) {
    
    
            block(row, col);

            if (findPath(row + 1, col)) {
    
    
                board[row][col].select();
                return true;
            }

            unblock(row, col);
        }

        if ((col > 0) && !board[row][col - 1].isMarked() &&
                !board[row][col - 1].blocked() && !board[row][col - 1].visited()) {
    
    
            block(row, col);
            if (findPath(row, col - 1)) {
    
    
                board[row][col].select();
                return true;
            }

            unblock(row, col);
        }

        if ((col < 7) && !board[row][col + 1].isMarked() &&
                !board[row][col + 1].blocked() && !board[row][col + 1].visited()) {
    
    
            block(row, col);
            if (findPath(row, col + 1)) {
    
    
                board[row][col].select();
                return true;
            }

            unblock(row, col);
        }

        return false;
    }

    public void block(int row, int col) {
    
    
        if (row > 0) {
    
    
            board[row - 1][col].block();
        }

        if (row < 7) {
    
    
            board[row + 1][col].block();
        }

        if (col > 0) {
    
    
            board[row][col - 1].block();
        }

        if (col < 7) {
    
    
            board[row][col + 1].block();
        }
    }

    public void unblock(int row, int col) {
    
    
        if (row > 0) {
    
    
            board[row - 1][col].unblock();
        }

        if (row < 7) {
    
    
            board[row + 1][col].unblock();
        }

        if (col > 0) {
    
    
            board[row][col - 1].unblock();
        }

        if (col < 7) {
    
    
            board[row][col + 1].unblock();
        }
    }

    public void clear() {
    
    
        for (GameCell[] gameCells : board) {
    
    
            for (GameCell gameCell : gameCells) {
    
    
                gameCell.unselect();
            }
        }
    }

    // 内部类继承StackPane来储存矩形
    class GameCell extends StackPane {
    
    
        private boolean visited = false;
        private boolean blocked = false;
        private boolean selected = false;

        // 计算每一个网格的宽度和长度
        double width = WIDTH / 8;
        double height = HEIGHT / 8;
        // 声明第一个网格节点
        private Rectangle rectangle = new Rectangle(0, 0, width, height);

        // 声明第一个网格节点里面的叉号
        Line line1 = new Line(0, 0, width, height);
        Line line2 = new Line(width, 0, 0, height);

        // 构造方法
        public GameCell() {
    
    
            // 把网格节点加入到GameCell的实例对象中,并设置颜色属性,this表示引用当前对象的实例
            this.getChildren().add(rectangle);
            rectangle.setFill(Color.WHITE);
            rectangle.setStroke(Color.BLACK);

            // 如果点击了stackPane,就更改选中状态
            this.setOnMousePressed(e -> {
    
    
                selected = !selected;
                if (selected) {
    
    
                    mark();
                } else {
    
    
                    unmark();
                }
            });
        }

        // 添加叉号
        public void mark() {
    
    
            this.getChildren().addAll(line1, line2);
        }

        // 移除叉号
        public void unmark() {
    
    
            this.getChildren().remove(line1);
            this.getChildren().remove(line2);
        }

        public boolean isMarked() {
    
    
            return selected;
        }

        public void visit() {
    
    
            visited = true;
        }

        public boolean visited() {
    
    
            return visited;
        }

        public boolean blocked() {
    
    
            return blocked;
        }

        public void block() {
    
    
            blocked = true;
        }

        public void unblock() {
    
    
            blocked = false;
        }

        public void select() {
    
    
            rectangle.setFill(Color.RED);
        }

        public void unselect() {
    
    
            rectangle.setFill(Color.WHITE);
            blocked = false;
            visited = false;
        }
    }
}

输出结果:

在这里插入图片描述

**18.27 (科赫雪花分形)

本章给出了思瑞平斯基三角形分形。本练习要编写一个程序,显示另一个称为科赫雪花(Koch snowflake)的分形,这是根据一位著名的瑞典数学家的名字命名的。科赫雪花按如下方式产生:

1)从一个等边三角形开始,将其作为0阶(或0级)科赫分形,如图18-14a所示。

2 ) 三角形中的每条边分成三个相等的线段,以中间的线段作为底边向外画一个等边三角形,产生1阶科赫分形,如图18-14b所示。

3 ) 重复步骤2产生2阶科赫分形,3阶科赫分形,···,如图18-14c-d所示

package com.example.demo;

import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Line;
import javafx.stage.Stage;

import java.util.ArrayList;

public class Test extends Application {
    
    
    private final double WIDTH = 350;
    private final double HEIGHT = 350;
    private final Label mainLabel = new Label("Enter an Order");
    private int orderOfFractal = 0;
    Pane drawPane = new Pane();
    ObservableList<Node> FRACTAL = drawPane.getChildren();

    @Override
    public void start(Stage primaryStage) {
    
    
        VBox mainBox = new VBox(5);
        mainBox.setAlignment(Pos.CENTER);
        VBox.setMargin(drawPane,new Insets(15,0,0,0));
        mainBox.getChildren().add(drawPane);

        HBox hBox = new HBox(5);
        hBox.setAlignment(Pos.CENTER);

        TextField inputField = new TextField();
        inputField.setPrefWidth(100);

        hBox.getChildren().addAll(mainLabel, inputField);
        HBox.setMargin(mainLabel, new Insets(5, 5, 10, 10));
        HBox.setMargin(inputField, new Insets(5, 5, 10, 3));
        drawPane.setCenterShape(true);
        drawPane.setPrefHeight(HEIGHT - hBox.getHeight());
        mainBox.getChildren().add(hBox);

        inputField.textProperty().addListener((observable, oldValue, newValue) -> {
    
    
            FRACTAL.clear();
            if (!newValue.isEmpty()) {
    
    
                orderOfFractal = Integer.parseInt(newValue);
                drawBaseShape(orderOfFractal);
            }
        });

        Scene scene = new Scene(mainBox, WIDTH, HEIGHT);
        primaryStage.setTitle(getClass().getName());
        primaryStage.setScene(scene);
        primaryStage.setResizable(false);
        primaryStage.show();
    }

    /* Draw the starter Triangle when: order >= 0 */
    private void drawBaseShape(int order) {
    
    
        double length = HEIGHT - 100;
        Line l1 = new Line(WIDTH / 2, 0, (WIDTH / 2) + length * Math.cos(1 * (Math.PI * 2 / 6)),
                0 + length * Math.sin(1 * (Math.PI * 2 / 6)));
        Line l2 = new Line(l1.getEndX(), l1.getEndY(), l1.getEndX() - length, l1.getEndY());
        Line l3 = new Line(l2.getEndX(), l2.getEndY(), l1.getStartX(), l1.getStartY());
        FRACTAL.addAll(l1, l2, l3);
        if (order > 0) {
    
    
            draw(order);
        }
    }

    private void draw(int order) {
    
    
        if (order == 0) // Check Base Case (end recursion)
            return;

        ArrayList<Line> lines = new ArrayList<>();
        for (Node node : FRACTAL) {
    
    
            if (node instanceof Line) {
    
    
                lines.add((Line) node);
            }
        }

        for (Line line : lines) {
    
    
            triangle(line);
        }
        draw(order - 1); // Recurse
    }

    private void triangle(Line line) {
    
    
        double DIST = getLength(line) / 3;
        double dy = (line.getStartY() - line.getEndY());
        double dx = (line.getEndX() - line.getStartX());
        double th = Math.atan2(dy, dx);

        double x1 = line.getStartX() + DIST * Math.cos(th);
        double y1 = line.getStartY() - DIST * Math.sin(th);

        double x2 = line.getEndX() + DIST * Math.cos(th + Math.toRadians(180));
        double y2 = line.getEndY() - DIST * Math.sin(th + Math.toRadians(180));

        double x3 = x2 + DIST * Math.cos(th + Math.toRadians(120));
        double y3 = y2 - DIST * Math.sin(th + Math.toRadians(120));

        Line l1 = new Line(line.getStartX(), line.getStartY(), x1, y1);
        Line l2 = new Line(x2, y2, line.getEndX(), line.getEndY());
        Line l3 = new Line(l1.getEndX(), l1.getEndY(), x3, y3);
        Line l4 = new Line(l3.getEndX(), l3.getEndY(), x2, y2);

        drawPane.getChildren().remove(line);
        FRACTAL.addAll(l1, l2, l3, l4);
    }

    private double getLength(Line line) {
    
    
        // Use formula for 2D distance to get length of line
        return Math.sqrt(Math.pow(line.getEndX() - line.getStartX(), 2) + Math.pow(line.getEndY() - line.getStartY(),
                2));
    }
}

输出结果:

在这里插入图片描述

*18.28 (非递归目录大小)

不使用递归改写程序清单18-7

package com.example.demo;

import java.io.File;
import java.util.Scanner;

public class Test {
    
    

    public static void main(String[] args) {
    
    
        // Prompt the user to enter a directory or a file
        System.out.print("Enter a directory or a file: ");
        Scanner input = new Scanner(System.in);
        String directory = input.nextLine().trim();
        // Display the size
        System.out.println("The file: " + new File(directory).getName() + " contains " + getSize(new File(directory)) +
                " bytes");
    }

    public static long getSize(File file) {
    
    
        long TOTAL_BYTES = 0; // Store the total size of all files
        if (file.isDirectory()) {
    
    
            File[] files = file.listFiles(); // All files and subdirectories
            if (files != null && files.length > 0) {
    
    
                for (File f : files) {
    
    
                    if (f.isDirectory()) {
    
    
                        System.out.println("Reading Directory: " + f.getName());
                        File[] subFiles = f.listFiles();
                        if (subFiles != null && subFiles.length > 0) {
    
    
                            for (File subFile : subFiles) {
    
    
                                System.out.println("\t\tReading Sub-File: " + subFile.getName());
                                TOTAL_BYTES += subFile.length();
                            }
                        }

                    } else if (f.isFile()) {
    
    
                        System.out.println("Reading File: " + f.getName());
                        TOTAL_BYTES += f.length();

                    }

                }
            }
        } else if (file.isFile()) {
    
    
            return file.length();
        }
        return TOTAL_BYTES;
    }
}

输出结果:

Enter a directory or a file: /Users/kevinwang/IDEA/demo/src/main/java/com/example/demo/Test.java
The file: Test.java contains 1729 bytes
*18.29(某个目录下的文件数目)

编写一个程序,提示用户输入一个目录,然后显示该目录下的文件数。

package com.example.demo;

import java.io.File;
import java.util.Scanner;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        System.out.print("Enter a directory or a file: ");
        Scanner input = new Scanner(System.in);
        String directory = input.nextLine();

        System.out.println(getNum(new File(directory)) + " files");
    }

    public static long getNum(File file) {
    
    
        // 初始化文件数0
        long size = 0;

        // 如果file参数是一个路径
        if (file.isDirectory()) {
    
    
            // 列出当前路径下所有直接文件和直接文件夹的信息,不包括文件夹里面的子文件
            File[] files = file.listFiles();
            // 遍历数组,对于遍历到的元素,重新递归调用
            for (int i = 0; files != null && i < files.length; i++) {
    
    
                size += getNum(files[i]); // Recursive call
            }
        }
        // 如果file参数是一个文件
        else {
    
    
            // 文件数量+1
            size ++;
        }

        return size;
    }
}

输出结果:

Enter a directory or a file: /Users/kevinwang/IDEA/demo/src
3 files
**18.30 (找出单词)

编写一个程序,递归地找出某个目录下的所有文件中某个单词出现的次数。从命令行如下传递参数:

java Exercise18_30 dirName word
package com.example.demo;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;

public class Test {
    
    
    public static void main(String[] args) throws FileNotFoundException{
    
    
        String directory = args[0];
        String keyword = args[1];
        System.out.println(getSize(new File(directory), keyword));
    }

    public static long getSize(File file, String keyword) throws FileNotFoundException {
    
    
        // 初始化keyword出现次数0
        long size = 0;

        if (file.isDirectory()) {
    
    
            File[] files = file.listFiles(); // All files and subdirectories
            for (int i = 0; files != null && i < files.length; i++) {
    
    
                size += getSize(files[i], keyword); // Recursive call
            }
        }
        else {
    
     // Base case
            // 遍历文件每一个单词并写入words数组
            Scanner scanner = new Scanner(file);
            ArrayList<String> words = new ArrayList<>();
            while (scanner.hasNext()) {
    
    
               words.add(scanner.next());
            }

            // 遍历words数组查找关键字
            for (int i = 0; i < words.size(); i++) {
    
    
                if (words.get(i).contains(keyword)) {
    
    
                    size++;
                }
            }
        }
        return size;
    }
}

输出结果:

(base) kevinwang@KevindeMacBook-Pro demo % java /Users/kevinwang/IDEA/demo/src/main/java/com/example/demo/Test.java /Users/kevinwang/IDEA/demo/src public
5
**18.31 (替换单词)

编写一个程序,递归地用一个新单词替换某个目录下的所有文件中出现的某个单词。从命令行如下传递参数:

java Exercise18_31 dirName oldWord newWord
package com.example.demo;

import java.io.*;
import java.util.List;
import java.util.stream.Collectors;

public class Test {
    
    
    public static void main(String[] args) {
    
    
        if (args.length < 2) {
    
    
            System.err.println("Usage: java Exercise18_30 dirName word");
            System.exit(0);
        }

        String directory = args[0];
        String oldWord = args[1];
        String newWord = args[2];

        File dir = new File(directory);

        if (dir.isDirectory()) {
    
    
            File[] files = dir.listFiles();
            if (files != null && files.length > 0) {
    
    
                try {
    
    
                    replaceWords(files, oldWord, newWord, 0);
                } catch (IOException ioException) {
    
    
                    ioException.printStackTrace();
                }
            }

        } else {
    
    
            System.out.println("Please specify a Directory for 'dirName'. ");
        }
        System.out.println("The word: \"" + oldWord + "\" has been replaced with \"" + newWord + "\" for files in " +
                "directory: " + dir.getName());
    }

    static void replaceWords(File[] files, String oldWord, String newWord, int fileIndex) throws IOException {
    
    
        if (files.length == fileIndex) {
    
    
            return;
        }
        runReplace(oldWord, newWord, files[fileIndex]);
        replaceWords(files, oldWord, newWord, fileIndex + 1); // Recurse
    }

    /* Method to perform replacement logic */
    static void runReplace(String oldWord, String newWord, File file) throws IOException {
    
    
        if (file.isFile()) {
    
    
            FileReader fileReader = new FileReader(file);
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            List<String> resultList = bufferedReader.lines()
                    .map(s -> s.replace(oldWord, newWord))
                    .collect(Collectors.toList());

            bufferedReader.close();
            FileWriter fileWriter = new FileWriter(file);
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
            for (String line : resultList) {
    
    
                bufferedWriter.write(line);
                bufferedWriter.newLine();
            }
            bufferedWriter.close();
        }
    }
}

输出结果:

(base) kevinwang@KevindeMacBook-Pro demo % java /Users/kevinwang/IDEA/demo/src/main/java/com/example/demo/Test.java /Users/kevinwang/replaceTest public private
The word: "public" has been replaced with "private" for files in directory: replaceTest
***18.32 (游戏:骑士的旅途)

骑士的旅途是一个古老的谜题,它的目的是使骑从棋盘上的任意一个正方 形开始移动,经过其他的每个正方形一次,如图18-15a 所示。注意,骑士只能做L形的移动(两个空格在一个方向上而一个空格在垂直的方向上)。如图18-15b 所示,骑士可以移动到八个正方形的位置。编写一个程序,显示骑士的移动,如图 18-15C 所示。当单击一个单元格的时候,骑士被放置在该单元格中。该单元格作为骑士的起始点。单击Solve按钮显示作为解答的路径。

package com.example.demo;

import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;

import java.util.ArrayList;

public class Test extends Application {
    
    
    private static final int SIZE = 8;
    private int startX = 0;
    private int startY = 0;
    private ArrayList<Point2D> moves = null;

    public static void main(String[] args) {
    
    
        Application.launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
    
    
        BorderPane pane = new BorderPane();
        Board board = new Board();
        pane.setCenter(board);
        Button solveButton = new Button("Solve");
        pane.setBottom(solveButton);
        BorderPane.setAlignment(solveButton, Pos.CENTER);

        Scene scene = new Scene(pane, 250, 250);
        primaryStage.setTitle(getClass().getName());
        primaryStage.setResizable(false);
        primaryStage.setScene(scene);
        primaryStage.show();

        board.draw();

        solveButton.setOnAction(e -> {
    
    
            boolean[][] moves = new boolean[SIZE][SIZE];
            moves[startX][startY] = true;
            resetMoves();
            addMove(startX, startY);
            solvePuzzle(moves, 1, startX, startY);
            board.draw();
        });
    }

    private boolean solvePuzzle(boolean[][] moves, int numMoves, int x, int y) {
    
    
        int nextX = 0;
        int nextY = 0;
        int bestMoveX = 0;
        int bestMoveY = 0;
        int bestMoveX2 = 0;
        int bestMoveY2 = 0;
        int minMoveCount = SIZE;
        int moveCount = 0;

        for (int i = 2; i >= -2; i += -4) {
    
    
            for (int j = 1; j >= -1; j += -2) {
    
    
                nextX = x + i;
                nextY = y + j;
                if (nextX >= 0 && nextX <= SIZE - 1 && nextY >= 0 && nextY <= SIZE - 1
                        && !moves[nextX][nextY]) {
    
    
                    moveCount = lookAheadCount(moves, nextX, nextY);
                    if (moveCount <= minMoveCount) {
    
    
                        minMoveCount = moveCount;
                        bestMoveX2 = bestMoveX;
                        bestMoveY2 = bestMoveY;
                        bestMoveX = nextX;
                        bestMoveY = nextY;
                    }
                }

                nextX = x + j;
                nextY = y + i;
                if (nextX >= 0 && nextX <= SIZE - 1 && nextY >= 0 && nextY <= SIZE - 1
                        && !moves[nextX][nextY]) {
    
    
                    moveCount = lookAheadCount(moves, nextX, nextY);
                    if (moveCount <= minMoveCount) {
    
    
                        minMoveCount = moveCount;
                        bestMoveX2 = bestMoveX;
                        bestMoveY2 = bestMoveY;
                        bestMoveX = nextX;
                        bestMoveY = nextY;
                    }
                }
            }
        }
        moves[bestMoveX][bestMoveY] = true;
        addMove(bestMoveX, bestMoveY);
        numMoves++;
        if (numMoves == (SIZE * SIZE))
            return true;
        if (moveCount > 0 && solvePuzzle(moves, numMoves, bestMoveX, bestMoveY)) {
    
    
            return true;
        }
        moves[bestMoveX][bestMoveY] = false;
        moves[bestMoveX2][bestMoveY2] = true;
        removeLastMoveHistory();
        addMove(bestMoveX2, bestMoveY2);
        if (moveCount > 1 && solvePuzzle(moves, numMoves, bestMoveX2, bestMoveY2)) {
    
    
            return true;
        }
        moves[bestMoveX2][bestMoveY2] = false;
        removeLastMoveHistory();
        numMoves--;
        return false;
    }

    private int lookAheadCount(boolean[][] moves, int x, int y) {
    
    
        int maxCount = 0;
        for (int i = -2; i <= 2; i += 4) {
    
    
            for (int j = -1; j <= 1; j += 2) {
    
    
                int nextX = x + i;
                int nextY = y + j;
                if (nextX >= 0 && nextX <= SIZE - 1 && nextY >= 0 && nextY <= SIZE - 1
                        && !moves[nextX][nextY]) {
    
    
                    maxCount++;
                }

                nextX = x + j;
                nextY = y + i;
                if (nextX >= 0 && nextX <= SIZE - 1 && nextY >= 0 && nextY <= SIZE - 1
                        && !moves[nextX][nextY]) {
    
    
                    maxCount++;
                }
            }
        }
        return maxCount;
    }

    public void resetMoves() {
    
    
        moves = new ArrayList(63);
    }

    public void addMove(int x, int y) {
    
    
        moves.add(new Point2D(x, y));
    }

    public void removeLastMoveHistory() {
    
    
        moves.remove(moves.size() - 1);
    }

    private class Board extends Pane {
    
    
        Circle theKnight = new Circle();
        Board() {
    
    
            this.setOnMouseClicked(e -> {
    
    
                startX = (int) (e.getX() / (getWidth() / SIZE));
                startY = (int) (e.getY() / (getHeight() / SIZE));
                resetMoves();
                draw();
            });
        }

        protected void draw() {
    
    
            this.getChildren().clear();

            this.getChildren().add(theKnight);
            theKnight.setCenterX(startX * getWidth() / SIZE + 15);
            theKnight.setCenterY(startY * getHeight() / SIZE + 15);
            theKnight.setRadius(5);
            theKnight.setFill(Color.RED);

            for (int i = 1; i <= SIZE; i++) {
    
    
                this.getChildren().add(
                        new Line(0, i * getHeight() / SIZE, getWidth(), i * getHeight() / SIZE));
                this.getChildren().add(
                        new Line(i * getWidth() / SIZE, 0, i * getWidth() / SIZE, getHeight()));
            }

            if (moves != null) {
    
    
                for (int i = 1; i < moves.size(); i++) {
    
    
                    Point2D p1 = moves.get(i - 1);
                    Point2D p2 = moves.get(i);
                    this.getChildren().add(
                            new Line(p1.getX() * (getWidth() / SIZE) + getWidth() / SIZE / 2,
                                    p1.getY() * (getHeight() / SIZE) + (getHeight() / SIZE / 2),
                                    p2.getX() * (getWidth() / SIZE) + getWidth() / SIZE / 2,
                                    p2.getY() * (getHeight() / SIZE) + getHeight() / SIZE / 2));
                }
            }
        }
    }
}

输出结果:

在这里插入图片描述

***18.33 (游戏:骑士旅途的动画)

为骑士旅途的问题编写一个程序,该程序应该允许用户将骑士放到任何一个起始正方形,并单击Solve按钮,用动画展示骑士沿着路径的移动,如图18-16所示

package com.example.demo;

import javafx.animation.PathTransition;
import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;

import java.util.ArrayList;

public class Test extends Application {
    
    
    private static final int SIZE = 8;
    private int startX = 0;
    private int startY = 0;
    private ArrayList<Point2D> moves = null;

    public static void main(String[] args) {
    
    
        Application.launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
    
    
        BorderPane pane = new BorderPane();
        Board board = new Board();
        pane.setCenter(board);
        Button solveButton = new Button("Solve");
        pane.setBottom(solveButton);
        BorderPane.setAlignment(solveButton, Pos.CENTER);

        Scene scene = new Scene(pane, 250, 250);
        primaryStage.setTitle(getClass().getName());
        primaryStage.setResizable(false);
        primaryStage.setScene(scene);
        primaryStage.show();

        board.draw();

        solveButton.setOnAction(e -> {
    
    
            boolean[][] moves = new boolean[SIZE][SIZE];
            moves[startX][startY] = true;
            resetMoves();
            addMove(startX, startY);
            solvePuzzle(moves, 1, startX, startY);
            board.draw();
        });

    }

    private boolean solvePuzzle(boolean[][] moves, int numMoves, int x, int y) {
    
    
        int nextX = 0;
        int nextY = 0;
        int bestMoveX = 0;
        int bestMoveY = 0;
        int bestMoveX2 = 0;
        int bestMoveY2 = 0;
        int minMoveCount = SIZE;
        int moveCount = 0;

        for (int i = 2; i >= -2; i += -4) {
    
    
            for (int j = 1; j >= -1; j += -2) {
    
    
                nextX = x + i;
                nextY = y + j;
                if (nextX >= 0 && nextX <= SIZE - 1 && nextY >= 0 && nextY <= SIZE - 1
                        && !moves[nextX][nextY]) {
    
    
                    moveCount = lookAheadCount(moves, nextX, nextY);
                    if (moveCount <= minMoveCount) {
    
    
                        minMoveCount = moveCount;
                        bestMoveX2 = bestMoveX;
                        bestMoveY2 = bestMoveY;
                        bestMoveX = nextX;
                        bestMoveY = nextY;
                    }
                }

                nextX = x + j;
                nextY = y + i;
                if (nextX >= 0 && nextX <= SIZE - 1 && nextY >= 0 && nextY <= SIZE - 1
                        && !moves[nextX][nextY]) {
    
    
                    moveCount = lookAheadCount(moves, nextX, nextY);
                    if (moveCount <= minMoveCount) {
    
    
                        minMoveCount = moveCount;
                        bestMoveX2 = bestMoveX;
                        bestMoveY2 = bestMoveY;
                        bestMoveX = nextX;
                        bestMoveY = nextY;
                    }
                }
            }
        }
        moves[bestMoveX][bestMoveY] = true;
        addMove(bestMoveX, bestMoveY);
        numMoves++;
        if (numMoves == (SIZE * SIZE))
            return true;
        if (moveCount > 0 && solvePuzzle(moves, numMoves, bestMoveX, bestMoveY)) {
    
    
            return true;
        }
        moves[bestMoveX][bestMoveY] = false;
        moves[bestMoveX2][bestMoveY2] = true;
        removeLastMoveHistory();
        addMove(bestMoveX2, bestMoveY2);
        if (moveCount > 1 && solvePuzzle(moves, numMoves, bestMoveX2, bestMoveY2)) {
    
    
            return true;
        }
        moves[bestMoveX2][bestMoveY2] = false;
        removeLastMoveHistory();
        numMoves--;
        return false;
    }

    private int lookAheadCount(boolean[][] moves, int x, int y) {
    
    
        int maxCount = 0;
        for (int i = -2; i <= 2; i += 4) {
    
    
            for (int j = -1; j <= 1; j += 2) {
    
    
                int nextX = x + i;
                int nextY = y + j;
                if (nextX >= 0 && nextX <= SIZE - 1 && nextY >= 0 && nextY <= SIZE - 1
                        && !moves[nextX][nextY]) {
    
    
                    maxCount++;
                }

                nextX = x + j;
                nextY = y + i;
                if (nextX >= 0 && nextX <= SIZE - 1 && nextY >= 0 && nextY <= SIZE - 1
                        && !moves[nextX][nextY]) {
    
    
                    maxCount++;
                }
            }
        }
        return maxCount;
    }

    public void resetMoves() {
    
    
        moves = new ArrayList(63);
    }

    public void addMove(int x, int y) {
    
    
        moves.add(new Point2D(x, y));
    }

    public void removeLastMoveHistory() {
    
    
        moves.remove(moves.size() - 1);
    }

    private class Board extends Pane {
    
    
        Circle theKnight = new Circle(5);
        Circle movingKnight = new Circle(5);

        Board() {
    
    
            this.setOnMouseClicked(e -> {
    
    
                startX = (int) (e.getX() / (getWidth() / SIZE));
                startY = (int) (e.getY() / (getHeight() / SIZE));
                resetMoves();
                draw();
            });
        }

        protected void draw() {
    
    
            this.getChildren().clear();

            this.getChildren().add(theKnight);
            theKnight.setCenterX(startX * getWidth() / SIZE + 15);
            theKnight.setCenterY(startY * getHeight() / SIZE + 15);
            movingKnight.setCenterX(startX * getWidth() / SIZE + 15);
            movingKnight.setCenterY(startY * getHeight() / SIZE + 15);
            this.getChildren().add(movingKnight);

            for (int i = 1; i <= SIZE; i++) {
    
    
                this.getChildren().add(
                        new Line(0, i * getHeight() / SIZE, getWidth(), i * getHeight() / SIZE));
                this.getChildren().add(
                        new Line(i * getWidth() / SIZE, 0, i * getWidth() / SIZE, getHeight()));
            }

            if (moves != null) {
    
    
                for (int i = 1; i < moves.size(); i++) {
    
    
                    Point2D p1 = moves.get(i - 1);
                    Point2D p2 = moves.get(i);
                    Line line = new Line(p1.getX() * (getWidth() / SIZE) + getWidth() / SIZE / 2,
                            p1.getY() * (getHeight() / SIZE) + (getHeight() / SIZE / 2),
                            p2.getX() * (getWidth() / SIZE) + getWidth() / SIZE / 2,
                            p2.getY() * (getHeight() / SIZE) + getHeight() / SIZE / 2);
                    PathTransition animatePath = new PathTransition();
                    animatePath.setNode(movingKnight);
                    animatePath.setRate(1.0);
                    animatePath.setPath(line);
                    animatePath.playFromStart();
                }
            }
        }
    }
}

输出结果:

在这里插入图片描述

**18.34 (游戏:八皇后问题)

八皇后问题是要找到一个解决方案,将一个皇后棋子放到棋盘上的每行中,并且两个皇后棋子之间不能相互攻击。编写个程序,使用递归来解决八皇后问题,并如图18-17显示结果

package com.example.demo;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Line;
import javafx.stage.Stage;

public class Test extends Application {
    
    
    private static final int SIZE = 8;
    private int[] queens = new int[SIZE]; // Queen positions

    @Override
    public void start(Stage primaryStage) {
    
    
        search(0); // Search for a solution from row 0

        ChessBoard board = new ChessBoard();
        Scene scene = new Scene(board, 250, 250);
        primaryStage.setTitle(getClass().getName());
        primaryStage.setScene(scene);
        primaryStage.setResizable(false);
        primaryStage.show();

        board.paint();

    }

    private boolean isValid(int row, int column) {
    
    
        for (int i = 1; i <= row; i++)
            if (queens[row - i] == column // Check column
                    || queens[row - i] == column - i // Check upleft diagonal
                    || queens[row - i] == column + i) // Check upright diagonal
                return false; // There is a conflict
        return true; // No conflict
    }

    private boolean search(int row) {
    
    
        if (row == SIZE) // Stopping condition
            return true; // A solution found to place 8 queens in 8 rows

        for (int column = 0; column < SIZE; column++) {
    
    
            queens[row] = column; // Place a queen at (row, column)
            if (isValid(row, column) && search(row + 1))
                return true; // Found, thus return true to exit for loop
        }

        // No solution for a queen placed at any column of this row
        return false;
    }

    private class ChessBoard extends Pane {
    
    
        Image queenImage = new Image("File:/Users/kevinwang/true.png");

        public void paint() {
    
    
            // Clear previous drawing
            this.getChildren().clear();

            // Draw the queens
            for (int i = 0; i < SIZE; i++) {
    
    
                // Add the queen image view
                ImageView queenImageView = new ImageView(queenImage);
                this.getChildren().add(queenImageView);
                int j = queens[i]; // The position of the queen in row i
                queenImageView.setX(j * getWidth() / SIZE);
                queenImageView.setY(i * getHeight() / SIZE);
                queenImageView.setFitWidth(getWidth() / SIZE);
                queenImageView.setFitHeight(getHeight() / SIZE);
            }

            // Draw the lines
            for (int i = 1; i <= SIZE; i++) {
    
    
                this.getChildren().add(
                        new Line(0, i * getHeight() / SIZE, getWidth(), i * getHeight() / SIZE));
                this.getChildren().add(
                        new Line(i * getWidth() / SIZE, 0, i * getWidth() / SIZE, getHeight()));
            }
        }
    }
}

输出结果:

在这里插入图片描述

**18.35(H 树分形)

一个H 树分形(本章开始部分介绍过,如图18-1)如下定义:

1)从字母H开始。H的三条线长度一样,如图 18-1a 所示。

2)字母H(以它的 sans-serif 字体形式,H)有四个端点。以这四个端点为中心位置绘制一个 1 阶 H 树,如图18-1b所示。这些H的大小是包括这四个端点的 H的一半。

3)重复步骤2来创建2阶,3阶,…,n阶的H树,如图18-1c-d所示。

编写程序,绘制如图18-1所示的H树

package com.example.demo;

import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Line;
import javafx.stage.Stage;

public class Test extends Application {
    
    

    private final double WIDTH = 350;
    private final double HEIGHT = 350;
    private final Label mainLabel = new Label("Enter an Order");
    private int orderOfFractal = 0;

    Pane drawPane = new Pane();
    ObservableList<Node> FRACTAL = drawPane.getChildren();

    @Override
    public void start(Stage primaryStage) {
    
    
        VBox mainBox = new VBox(5);
        mainBox.setAlignment(Pos.CENTER);
        VBox.setMargin(drawPane, new Insets(15, 0, 0, 0));
        mainBox.getChildren().add(drawPane);

        HBox hBox = new HBox(5);
        hBox.setAlignment(Pos.CENTER);

        TextField inputField = new TextField();
        inputField.setPrefWidth(100);

        hBox.getChildren().addAll(mainLabel, inputField);
        HBox.setMargin(mainLabel, new Insets(5, 5, 10, 10));
        HBox.setMargin(inputField, new Insets(5, 5, 10, 3));
        drawPane.setCenterShape(true);
        drawPane.setPrefHeight(HEIGHT - hBox.getHeight());
        mainBox.getChildren().add(hBox);

        inputField.textProperty().addListener((observable, oldValue, newValue) -> {
    
    
            FRACTAL.clear();
            if (!newValue.isEmpty()) {
    
    
                orderOfFractal = Integer.parseInt(newValue);
                double baseHSize = HEIGHT / 2 - 50;
                double centerX = drawPane.getWidth() / 2 - baseHSize / 2; // X of point where base H is centered in Pane
                double centerY = drawPane.getHeight() / 2 - baseHSize / 2; // Y of point where base H is center in Pane
                drawHTree(orderOfFractal, centerX, centerY, baseHSize);
            }
        });

        Scene scene = new Scene(mainBox, WIDTH, HEIGHT);
        primaryStage.setTitle(getClass().getName());
        primaryStage.setScene(scene);
        primaryStage.setResizable(false);
        primaryStage.show();
    }

    private void drawH(double x, double y, double size) {
    
    
        Line leftVert = new Line(x, y, x, y + size);
        Line rightVert = new Line(x + size, y, x + size, y + size);
        Line horizontal = new Line(x, y + size / 2.0, x + size, y + size / 2.0);
        FRACTAL.addAll(leftVert, rightVert, horizontal);
    }

    private void drawHTree(int order, double x, double y, double size) {
    
    
        drawH(x, y, size);
        if (order > 0) {
    
    
            drawHTree(order - 1, x - size / 4, y - size / 4, size / 2);
            drawHTree(order - 1, x + size - size / 4, y - size / 4, size / 2);
            drawHTree(order - 1, x - size / 4, y + size - size / 4, size / 2);
            drawHTree(order - 1, x + size - size / 4, y + size - size / 4, size / 2);
        }
    }
}

输出结果:

在这里插入图片描述

18.36 (思瑞平斯基三角形)

编写一个程序,让用户输入一个阶数,然后显示填充的思瑞平斯基三角形,如图18-18所示。

package com.example.demo;

import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Polygon;
import javafx.stage.Stage;

public class Test extends Application {
    
    

    private static final double HEIGHT = 220;
    private static final double WIDTH = 200;

    @Override
    public void start(Stage primaryStage) {
    
    
        SierpinskiTrianglePane trianglePane = new SierpinskiTrianglePane();
        TextField textField = new TextField();
        textField.setAlignment(Pos.BOTTOM_RIGHT);
        textField.setPrefColumnCount(4);
        textField.setOnAction(
                e -> trianglePane.setOrder(Integer.parseInt(textField.getText())));

        HBox hBox = new HBox(10);
        hBox.getChildren().addAll(new Label("(Enter) an order: "), textField);
        hBox.setAlignment(Pos.CENTER);

        BorderPane borderPane = new BorderPane();
        borderPane.setCenter(trianglePane);
        borderPane.setBottom(hBox);

        Scene scene = new Scene(borderPane, WIDTH, HEIGHT);
        primaryStage.setTitle(getClass().getName());
        primaryStage.setScene(scene);
        primaryStage.setResizable(false);
        primaryStage.show();

    }

    public static class SierpinskiTrianglePane extends Pane {
    
    
        private int order = 0;

        public void setOrder(int order) {
    
    
            this.order = order;
            draw();
        }

        SierpinskiTrianglePane() {
    
    
        }

        protected void draw() {
    
    
            Point2D p1 = new Point2D(getWidth() / 2, 10);
            Point2D p2 = new Point2D(10, getHeight() - 10);
            Point2D p3 = new Point2D(getWidth() - 10, getHeight() - 10);

            this.getChildren().clear();

            showTriangles(order, p1, p2, p3);
        }

        private void showTriangles(int order, Point2D p1,
                                   Point2D p2, Point2D p3) {
    
    
            if (order == 0) {
    
    
                Polygon triangle = new Polygon();
                triangle.getPoints().addAll(p1.getX(), p1.getY(), p2.getX(),
                        p2.getY(), p3.getX(), p3.getY());

                this.getChildren().add(triangle);
            } else {
    
    
                Point2D p12 = p1.midpoint(p2);
                Point2D p23 = p2.midpoint(p3);
                Point2D p31 = p3.midpoint(p1);

                /* Recursively call showTriangles to draw the inner triangles */
                showTriangles(order - 1, p1, p12, p31);
                showTriangles(order - 1, p12, p2, p23);
                showTriangles(order - 1, p31, p23, p3);
            }
        }
    }
}

输出结果:

在这里插入图片描述

**18.37 (希尔伯特曲线)

希尔伯特曲线,由德国数学家希尔伯特于1891年第一个给出描述,是一种空间填充曲线,以 2 x 2, 4 x 4, 8 x 8, 16 x 16, 或者任何其他 2 的幂的大小来访问一个方格网的每个点。编写程序,以给定的阶数显示希尔伯特曲线,如图 18-19 所示。

https://blog.csdn.net/qq_43655831/article/details/112337007

**18.38 (递归树)

编写一个程序来显示一个递归树,如图18-20所示

package com.example.demo;

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.stage.Stage;

public class Test extends Application {
    
    
    private int order; // 递归树的阶级
    private int size; // 窗口大小
    private int canvasWidth; // 画布宽度
    private int canvasHeight; // 画布高度

    @Override
    public void start(Stage primaryStage) {
    
    
        size = 300;
        canvasWidth = size;
        canvasHeight = size - 50; // 减去底部 TextField 的高度

        Group root = new Group();
        Scene scene = new Scene(root, size, size);

        TextField textField = new TextField();
        Label textFieldLabel = new Label("Enter a number", textField);
        textFieldLabel.setContentDisplay(ContentDisplay.RIGHT);
        textFieldLabel.setAlignment(Pos.CENTER);

        BorderPane borderPane = new BorderPane();
        borderPane.setCenter(root);

        borderPane.setBottom(textFieldLabel);
        BorderPane.setAlignment(textFieldLabel, Pos.CENTER);

        textField.setOnAction(e -> {
    
    
            root.getChildren().clear();
            order = Integer.parseInt(textField.getText());
            drawRecursiveTree(root, order, canvasWidth / 8, canvasHeight / 4, canvasHeight * 0.24, Math.PI / 2);
        });

        scene.setRoot(borderPane);
        primaryStage.setTitle("Recursive Tree");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void drawRecursiveTree(Group group, int order, double x, double y, double length, double angle) {
    
    
        if (order < 0) {
    
    
            return;
        }

        double x2 = x + length * Math.cos(angle);
        double y2 = y - length * Math.sin(angle);

        Line line = new Line(x, y, x2, y2);
        line.setStroke(Color.BLACK);
        group.getChildren().add(line);

        drawRecursiveTree(group, order - 1, x2, y2, length * 0.7, angle - Math.PI / 6);
        drawRecursiveTree(group, order - 1, x2, y2, length * 0.7, angle + Math.PI / 6);
    }
}

输出结果:

在这里插入图片描述

**18.39(拖动树)

修改编程练习题18.38, 将树移动到鼠标所拖动到的位置

package com.example.demo;

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.stage.Stage;

public class Test extends Application {
    
    
    private int order; // 递归树的阶级
    private int size; // 窗口大小
    private int canvasWidth; // 画布宽度
    private int canvasHeight; // 画布高度

    @Override
    public void start(Stage primaryStage) {
    
    
        size = 300;
        canvasWidth = size;
        canvasHeight = size - 50; // 减去底部 TextField 的高度

        Group root = new Group();
        Scene scene = new Scene(root, size, size);

        TextField textField = new TextField();
        Label textFieldLabel = new Label("Enter a number", textField);
        textFieldLabel.setContentDisplay(ContentDisplay.RIGHT);
        textFieldLabel.setAlignment(Pos.CENTER);

        BorderPane borderPane = new BorderPane();
        borderPane.setCenter(root);

        borderPane.setBottom(textFieldLabel);
        BorderPane.setAlignment(textFieldLabel, Pos.CENTER);

        textField.setOnAction(e -> {
    
    
            root.getChildren().clear();
            order = Integer.parseInt(textField.getText());
            drawRecursiveTree(root, order, canvasWidth / 8, canvasHeight / 4, canvasHeight * 0.24, Math.PI / 2);
        });

        borderPane.setOnMouseDragged(e -> {
    
    
            borderPane.setLayoutX(e.getX());
            borderPane.setLayoutY(e.getY());
        });
        
        scene.setRoot(borderPane);
        primaryStage.setTitle("Recursive Tree");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void drawRecursiveTree(Group group, int order, double x, double y, double length, double angle) {
    
    
        if (order < 0) {
    
    
            return;
        }

        double x2 = x + length * Math.cos(angle);
        double y2 = y - length * Math.sin(angle);

        Line line = new Line(x, y, x2, y2);
        line.setStroke(Color.BLACK);
        group.getChildren().add(line);

        drawRecursiveTree(group, order - 1, x2, y2, length * 0.7, angle - Math.PI / 6);
        drawRecursiveTree(group, order - 1, x2, y2, length * 0.7, angle + Math.PI / 6);
    }
}

输出结果:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_40020256/article/details/131333685