"Java Black Book Basics, 10th Edition" Chapter 18 [Exercise]

Java Language Programming Exercises Chapter 18

18.2 Chapter Exercises

18.1 What is a recursive method? What is infinite recursion?

Recursive methods can be broken down into sums . In Java, the execution of most methods requires a call stack to track method calls and returns. During the process, the recursive method calls itself, adding the new call to the top of the call stack (push); when encountering a termination condition, it starts executing, and returns the top call stack (pop)

Infinite recursion occurs if a recursive method has no terminating condition

18.2 In program listing 18-1, for factorial(6), how many times the factorial method is called?

14 times

18.3 Give the output of the following program, indicating the base case and the recursive calls.

// 输出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 Write a recursive mathematical definition to compute 2 n 2^n2n , where n is a positive integer

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

18.5 Write a recursive mathematical definition to compute xnx^nxn , where n is a positive integer and x is a real number

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

18.6 Write a recursive mathematical definition to compute 1+2+3+···+n, where n is a positive integer

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

18.3 Chapter Exercises

18.7 Give the output of the following two programs

// 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 What is the error in the method below?

// 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 In the program listing 18-2, how many calls to the fib method are made to fib(6)?

25 times

18.4 Chapter Exercises

18.10 Describe the characteristics of recursive methods

There is a base case for terminating actions, and actions for proceeding

Simplify a big problem into a smaller one, but the essence of the problem is the same

18.11 What is the base case for the isPalindrome method in Listing 18-3? When isPalindrome("abdxcxdba") is called, how many times is this method called?

The length of the string is less than 1 or the first and last characters are different

was called 5 times

18.12 Using the method defined in Listing 18-3, give the call stack of isPalindrome(“abcba”).

Look at aa first, then bb, and finally c

18.5 Chapter Exercises

18.13 Using the method defined in Listing 18-4, give the call stack of isPalindrome(“abcba”).

Look at aa first, then bb, and finally c

18.14 Using the method defined in Listing 18-5, give the call stack of selectionSort(new double[]{2,3,5,1}).

1,2,3,4

18.15 What are recursive helper methods?

An overloaded method with more parameters

18.6 Chapter Exercises

18.16 What is the underlying situation of the getSize method?

d itself is a file

18.17 How does the program get all the files and directories in a given directory?

Save to File[]

18.18 If a directory has three subdirectories with four files each, how many times will the getSize method be called?

20 times

18.19 Will the program work if the directory is empty (ie, contains no files)?

Can

18.20 If line 20 is replaced by the following code, will the program work?

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

No, the directory may be empty

18.21 Would the program work if lines 20 and 21 were replaced with the following code?

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

No, files may be null

18.7 Chapter Exercises

18.22 If moveDisks(5, 'A', 'B', 'C') is called in Listing 18-8, how many times will the moveDisks method be called

2 5 − 1 2^5 -1 251

18.8 Chapter Exercises

18.23 How to find the midpoint of two points?

p1.midpoint(p2)

18.24 What is the underlying situation of the displayTriangles method?

order == 0

18.25 How many times will the diaplayTriangles method be called for the 0th order, 1st order, 2nd order, and nth order Sripinski triangles?

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

18.26 What happens if a negative order value is entered? How to fix this problem in the code?

an infinite loop will occur

add if (order < 0)

18.27 Rewrite lines 71-77 of the code to draw three line segments connecting points to draw a triangle, instead of using the original method of drawing a polygon.

// 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 Chapter Exercises

18.28 Which of the following statements are true:

· Any recursive method can be converted to a non-recursive method

Executing a recursive method takes more time and memory than executing a non-recursive method

· Recursive methods are always simpler than non-recursive methods

There is always a select statement in a recursive method that checks whether the base case is reached

right wrong right

18.29 What is the cause of the stack overflow exception?

Running out of stack space

18.10 Chapter Exercises

18.30 Identify tail-recursive methods in this chapter.

18.4, 18.5, 18.6

18.31 Rewrite the fib method in Listing 18-2 using tail recursion.

/** 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);
}

programming exercises

*18.1 (to calculate factorial)

Find the factorial of large numbers (for example, 100!) using the Biglnteger class introduced in Section 10.9. The factorial method is implemented using recursion. Write a program that prompts the user for an integer and then displays its 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)));
        }
    }
}

Output result:

Input an integer: 100
The factorial of 100 is 933262154439xxxxx
*18.2 (Fibonacci number)

Use iteration to rewrite the fib method in Listing 18-2

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

Output result:

Input an index: 11
The fib of 11 is 55
*18.3 (use recursion to find the greatest common divisor)

The gcd(m, n) method for finding the greatest common divisor can also be defined recursively as follows:

• If m%n is 0, then the value of gcd(m, n) is n

• Otherwise, gcd(m, n) is gcd(n, m%n)

Write a recursive method to find the greatest common divisor. Write a test program that prompts the user for two integers and displays their greatest common divisor

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

Output result:

Input two numbers: 4 6
The fib of 4 and 6 is 2
18.4 (Summing a sequence of numbers)

Write a recursive method to compute the following series:

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

Write a test program that displays m(i) for i = 1, 2, ..., 10

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

Output result:

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 (summation of sequence)

Write a recursive method to compute the following series:

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 + 1 _i

Write a test program that displays m(i) for i = 1, 2, ..., 10

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

Output result:

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 (summation of sequence)

Write a recursive method to compute the following series:

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

Write a test program that displays m(i) for i = 1, 2, ..., 10

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

Output result:

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 (Fibonacci sequence)

Modify the program list 18-2 so that the program can find out the number of calls to the fib method

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

Output result:

Enter an index for a Fibonacci number: 4
The Fibonacci number at index 4 is 3 in 9 times
*18.8 (output the numbers in an integer in reverse order)

Write a recursive method that displays an int value on the console in reverse order using the following method header:

public static void reverseDisplay(int value)

For example, reverseDisplay(12345) displays 54321. Write a test program that prompts the user to enter an integer. Then displays its reversed number.

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

Output result:

Enter an integer: 123456
After reverse, the number is 654321
*18.9 (output characters in a string in reverse order)

Write a recursive method that displays a string in reverse order on the console using the following method header:

public static void reverseDisplay(String value)

For example, reverseDisplay("abcd") displays dcba. Write a test program that prompts the user for a string and then displays its reversed string

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

Output result:

Enter an string: abcd
After reverse, the string is dcba
*18.10 (the second occurrence of a specified character in the string)

Write a recursive method that gives the number of occurrences of a specified character in a string using the method header below.

public static int count(String str,char a)

For example, count("Welcome", 'e') returns 2. Write a test program that prompts the user for a string and a character and displays the number of times that character occurs in the string.

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

Output result:

Enter a string and a character: welcome e
Frequency: 2
*18.11 (Use recursion to find the sum of the digits of an integer)

Write a recursive method that computes the sum of the digits of an integer using the following method header:

public static int sumDigits(long n)

For example, sumDigits(234) returns 2+3+4=9. Write a test program that prompts the user to enter an integer and then displays the sum of the digits.

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

Output result:

Enter an integer: 123
Sum: 6
**18.12 (print the characters in the string in reverse order)

Rewrite Programming Exercise 18.9 using a helper method, passing the high subscript of the substring to this method. The helper method header is:

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

Output result:

Enter an string: hello
After reverse, the string is olleh
*18.13 (find the largest number in the array)

Write a recursive method that returns the largest integer in an array. Write a test program that prompts the user for a list of 8 integers and then displays the largest element.

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

Output result:

Enter an integer array: 55 66 8 90
The biggest number in your array is 90
*18.14 (Seek the individual characters of uppercase letters in the string)

Write a recursive method that returns the number of uppercase letters in a string. Write a test program that prompts the user to enter a string and then displays the number of capital letters in the string

There is only one termination condition in this question, and there is no regular execution code block

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

Output result:

Enter an string: srgHRSD
The times that uppercase letters show: 4
*18.15 (the number of occurrences of a specified character in the string)

Rewrite programming exercise 18.10 using a helper method, passing the high subscript of the substring to this method. The helper method header is:

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

Output result:

Enter a String and a character to count it's occurrences:welcome e
Character e occurs 2 times in welcome
*18.16 (find the number of uppercase letters in the array)

Write a recursive method that returns the number of uppercase letters in a character array. The following two methods need to be defined. The second method is a recursive helper method.

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

Write a test program that prompts the user to enter a list of characters on one line, and then displays the number of uppercase letters in the list.

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

Output result:

Enter an string: segfegKBIGBKIUGVIUhnoihHOUH
The times that uppercase letters show: 16
*18.17 (the number of occurrences of a specified character in the array)

Write a recursive method to find the number of occurrences of a specified character in an array. The following two methods need to be defined, the second method is a recursive helper method.

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

Write a test program that prompts the user for a list of characters and a character on one line, and then displays the number of times that character occurs in the list.

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

Output result:

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

Modify Listing 18-8 so that the program can calculate the number of moves needed to move n plates from tower A to tower 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);
        }
    }
}

Output result:

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 (Srepinski triangle)

Modify the program Listing 18-9, develop a program that allows the user to use the "+" and "-" buttons to increase or decrease the current order by 1, as shown in Figure 18-12a. The initial order is 0. If the current order is 0, the button is ignored.

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

Output result:

insert image description here

*18.20 (shows multiple circles)

Write a Java program to display multiple circles, as shown in Figure 18-12b. These circles are all centered in the panel. There is a distance of 10 pixels between two adjacent circles, and a distance of 10 pixels between the panel and the largest circle.

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

Output result:

insert image description here

*18.21 (converts a decimal number to a binary number)

Write a recursive method that converts a decimal number to a string of binary numbers. The method header is as follows:

public static String dec2Bin(int value)

Write a test program that prompts the user to enter a decimal number and then displays the binary equivalent.

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

Output result:

Enter a decimal(base 10) number as an integer: 21
21 converted to binary is: 10101
*18.22 (convert a decimal number to a hexadecimal number)

Write a recursive method that converts a decimal number to a string of hexadecimal numbers. The method header is as follows:

public static String dec2Hex(int value)

Write a test program that prompts the user to enter a decimal number and then displays the equivalent hexadecimal number

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

Output result:

Enter a decimal number to convert to hex: 436
436 is equivalent to hexadecimal number 1B4
*18.23 (convert binary number to decimal number)

Write a recursive method that converts a binary number in string form to a decimal number. The method header is as follows:

public static int bin2Dec(String binarystring)

Write a test program that prompts the user for a binary string and then displays the decimal equivalent.

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

Output result:

Enter the binary number to convert to decimal: 11110001
11110001 binary number is equivalent to the decimal integer: 241
18.24 (converts a hexadecimal number to a decimal number)

Write a recursive method that converts a hexadecimal number in string form to a decimal number. The method header is as follows:

public static int hex2Dec(String hexString)

Write a test program that prompts the user for a hexadecimal string and then displays the decimal equivalent.

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';
    }
}

Output result:

Enter a hex number: B2
B2 is decimal 178
**18.25 (string permutation)

Write a recursive method that outputs all permutations of a string. For example, for the string abc, the output is:

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

Output result:

Enter a string: abc
 abc
 acb
 bac
 bca
 cab
 cba
**18.26 (create a maze)

Write a program that finds a path through a maze, as shown in Figure 18-13a. The maze is represented by an 8 x 8 board. The path must meet the following conditions:

The path is between the upper left cell and the lower right cell of the maze.

The program allows the user to place or remove a flag in a cell. A path consists of adjacent unmarked cells. Two cells are said to be adjacent if they are adjacent horizontally or vertically, but not diagonally.

The path does not contain cells that form a square. For example, the path in Figure 18-13b does not satisfy this condition. (This condition makes the path on the panel easy to identify.)

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

Output result:

insert image description here

**18.27 (Koch Snowflake Fractal)

This chapter gives the fractal of Sripinski's triangle. In this exercise, write a program that displays another fractal called a Koch snowflake, named after a famous Swedish mathematician. Koch snowflakes are generated as follows:

1) Start with an equilateral triangle and use it as a 0th-order (or 0th-order) Koch fractal, as shown in Figure 18-14a.

2) Each side of the triangle is divided into three equal line segments, and an equilateral triangle is drawn outward with the middle line segment as the base to generate a first-order Koch fractal, as shown in Figure 18-14b.

3) Repeat step 2 to generate 2nd-order Koch fractal, 3rd-order Koch fractal, ..., as shown in Figure 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));
    }
}

Output result:

insert image description here

*18.28 (non-recursive directory size)

Rewriting the program Listing 18-7 without recursion

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

Output result:

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 (the number of files in a directory)

Write a program that prompts the user for a directory and then displays the number of files in that directory.

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

Output result:

Enter a directory or a file: /Users/kevinwang/IDEA/demo/src
3 files
**18.30 (find the word)

Write a program that recursively finds the number of occurrences of a word in all files in a directory. Pass arguments from the command line as follows:

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

Output result:

(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 (replace word)

Write a program that recursively replaces occurrences of a word in all files in a directory with a new word. Pass arguments from the command line as follows:

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

Output result:

(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 (Game: Knight's Journey)

The Knight's Journey is an ancient puzzle whose purpose is to make the knight move from any square on the board and pass every other square once, as shown in Figure 18-15a. Note that the knight can only do L-shaped moves (two spaces in one direction and one space in the vertical direction). As shown in Figure 18-15b, the knight can move to eight squares. Write a program to display the movement of the knight, as shown in Figure 18-15C. When a cell is clicked, the knight is placed in that cell. This cell serves as the starting point for the knight. Clicking the Solve button displays the path as the solution.

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

Output result:

insert image description here

***18.33 (Game: Animation of Knight's Journey)

Write a program for the knight's journey problem. The program should allow the user to place the knight on any starting square and click the Solve button to animate the knight's movement along the path, as shown in Figure 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();
                }
            }
        }
    }
}

Output result:

insert image description here

**18.34 (Game: Eight Queens Problem)

The eight queens problem is to find a solution where one queen is placed in each row on the board, and the two queens cannot attack each other. Write a program to solve the eight queens problem using recursion, and display the results in Figure 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()));
            }
        }
    }
}

Output result:

insert image description here

**18.35(H-tree fractal)

An H-tree fractal (introduced at the beginning of this chapter, as shown in Figure 18-1) is defined as follows:

1) Start with the letter H. The three lines of H have the same length, as shown in Figure 18-1a.

2) The letter H (in its sans-serif form, H) has four endpoints. Draw a 1-order H-tree centered on these four endpoints, as shown in Figure 18-1b. The size of these Hs is half the size of H including these four endpoints.

3) Repeat step 2 to create 2-order, 3-order, ..., n-order H-trees, as shown in Figure 18-1c-d.

Write a program to draw the H tree shown in Figure 18-1

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

Output result:

insert image description here

18.36 (Srepinski triangle)

Write a program that lets the user enter an order and then displays the filled Shripinski triangle, as shown in Figure 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);
            }
        }
    }
}

Output result:

insert image description here

**18.37 (Hilbert curve)

The Hilbert curve, first described by the German mathematician Hilbert in 1891, is a space-filling curve that takes the form of 2 x 2, 4 x 4, 8 x 8, 16 x 16, or any Other powers of 2 sizes to visit every point of a grid. Write a program to display the Hilbert curve with a given order, as shown in Figure 18-19.

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

**18.38 (recursive tree)

Write a program to display a recursive tree, as shown in Figure 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);
    }
}

Output result:

insert image description here

**18.39 (drag tree)

Modify programming exercise 18.38, move the tree to the position where the mouse is dragged

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

Output result:

insert image description here

Guess you like

Origin blog.csdn.net/weixin_40020256/article/details/131333685