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 25−1
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);
}
}
输出结果: