Java 面试难题和编程练习

这些 Java 难题及其解决方案将帮助你通过下一次 Java 面试。解决这些难题的关键是提出一个高效的算法,并编写干净的代码。

Java Interview Puzzles

这些 Java 难题和编程练习及其答案将帮助你通过下一次 Java 面试。提前学习并练习这些解决方案,以便在下一次面试中留下更好的印象。

请记住,解决这些难题的关键是理解问题,提出一个高效的算法,并编写干净且结构良好的代码。

1. FizzBuzz

编写一个程序,打印从 1 到 100 的数字。但对于 5 的倍数,打印“Fizz”而不是数字,对于 7 的倍数,打印“Buzz”。对于同时是 5 和 7 的倍数的数字,打印“FizzBuzz”。

要解决上述难题,我们需要遍历从 1 到 10 的数字,并检查每个数字,使得

if number is divisible by 5.
 	if number is divisible by 7 the print FizzBuzz.
 	else print Fizz.
if number is divisible by 7 the print Buzz.
	else print the number.

以下 Java 程序演示了使用上述伪步骤来使用 Java 8 Stream API 解决 FizzBuzz 问题。它使用 rangeClosed(1, 100) 方法遍历从 1 到 100 的数字流。然后我们使用 mapToObj() 方法来检查每个数字是否符合我们的算法,最后打印输出。

IntStream.rangeClosed(1, 100)
  .mapToObj(i -> i % 5 == 0 ? (i % 7 == 0 ? "FizzBuzz" : "Fizz") : (i % 7 == 0 ? "Buzz" : i))
  .forEach(System.out::println);

程序输出

1
2
3
4
Fizz
6
Buzz
...
34
FizzBuzz
36
...

2. 反转字符串

编写一个程序来反转给定的字符串,而无需使用任何内置的字符串操作函数。

要解决此难题,我们需要首先将字符串转换为字符数组,然后使用两个指针(startend)迭代地从开始和结束索引交换字符,直到它们在中心相遇。它反转整个数组。

最后,我们将反转的字符数组转换回字符串并将其作为字符串返回。

private static String reverse(String str) {
    
  char[] chars = str.toCharArray();
  int start = 0;
  int end = chars.length - 1;

  while (start < end) {
    // Swap characters at start and end indices
    char temp = chars[start];
    chars[start] = chars[end];
    chars[end] = temp;

    // Move the indices towards the center
    start++;
    end--;
  }

  return new String(chars);
}

让我们用一个简单的字符串测试此函数。


String string = "howtodoinjava";

String reverseString = reverse(string);

System.out.println(reverseString);   //avajniodotwoh

3. 回文程序

编写一个程序来检查给定的字符串是否为 回文(正向和反向读取相同)。

要检查字符串是否为回文,我们需要反转字符串并将其与反转的字符串进行比较。如果两个字符串匹配,则该字符串是回文。

我们已经在前面的问题中看到了一个 Java 程序来反转字符串。我们可以使用上面的 reverse() 函数,或者可以使用内置的 Java API 进行快速示例。

String string = "naman";
String reversed = new StringBuilder(originalString).reverse().toString();
boolean isPalindrome = string.equalsIgnoreCase(reversed); //prints 'true'

请注意,我们不关心字符串比较中的大小写,可以使用equals() 方法代替 equalsIgnoreCase()

4. 字母异位词程序

编写一个程序来检查两个给定的字符串是否互为字母异位词。字母异位词是指通过重新排列另一个单词或短语的字母而形成的单词或短语。

解决此难题可以有不同的方法。例如

  • 我们遍历第一个字符串中的字符,并从第二个字符串数组中删除每个字符。最后,如果第二个字符串为空,则表示两个字符串包含相同的字符,因此它们是字母异位词。
  • 我们对两个字符串进行排序并比较它们。如果两个字符串相等,则表示字符串是字母异位词。

请注意,检查字符串的长度是一个好主意。如果两个字符串的长度不相等,我们可以安全地得出结论,字符串不是字母异位词。

让我们使用第二种方法来编写解决方案。

private static boolean checkAnagrams(String str1, String str2) {

  if (str1.length() != str2.length()) {
    return false;
  }

  char[] charArray1 = str1.toCharArray();
  char[] charArray2 = str2.toCharArray();
  Arrays.sort(charArray1);
  Arrays.sort(charArray2);

  return Arrays.equals(charArray1, charArray2);;
}

让我们用一个例子来测试这个函数。

String str1 = "listen";
String str2 = "silent";

System.out.println(checkAnagrams(str1, str2));  //true

5. HiLo 程序

在 HiLo 游戏中,有两个简单的规则

  • 在最多 6 次尝试中猜出秘密数字。
  • 秘密数字是 1 到 100 之间的整数,包括 1 和 100。

每次,如果您猜的数字低于秘密数字(只有 JRE 知道它),将打印“LO”。类似地,当您猜的数字高于秘密数字时,将打印“HI”。您必须调整您的下一次猜测,以便能够在六次尝试内猜出正确的数字。

我们已经在 HiLo 猜数字游戏 文章中讨论了此难题的解决方案,您可以查看。

6. 检查括号是否平衡

编写一个 Java 程序来检查给定的括号字符串是否平衡。例如,“([])”是平衡的,但“([)]”不是。

可以使用 堆栈 数据结构来解决此难题。堆栈以 LIFO(后进先出)的顺序存储元素。我们可以使用 LIFO 特性来匹配括号。

从理论上讲,我们遍历给定字符串中的所有字符,并且每次找到一个开括号时,我们将其推入堆栈。当找到闭括号时,我们检查堆栈中最后推入的元素,它们应该匹配相同的开/闭括号集。例如,'{' 将匹配 '}'

最后,当程序完成时,堆栈中不应该留下任何括号。

public static boolean checkBalancedParentheses(String expression) {
  Stack<Character> stack = new Stack<>();

  for (char ch : expression.toCharArray()) {
    if (ch == '(' || ch == '{' || ch == '[') {
      stack.push(ch);
    } else if (ch == ')' || ch == '}' || ch == ']') {
      
      if (stack.isEmpty()) {
        return false;
      }

      char top = stack.pop();
      if ((ch == ')' && top != '(') ||
          (ch == '}' && top != '{') ||
          (ch == ']' && top != '[')) {
        return false;
      }
    }
  }
  return stack.isEmpty();
}

让我们用一个正确和不正确的平衡表达式来测试上面的函数。

String expression1 = "{[ a; ( b; ) c; ]}";
String expression2 = "{[ a; ( b; ) c; }]"; //incorrectly matched in last two characters

checkBalancedParentheses(expression1);  //true
checkBalancedParentheses(expression2);  //false

7. 查找缺失的数字

给定一个包含从 0, 1, 2, …, n 取出的 n 个不同数字的数组,找到缺失的数字。假设数组未排序。

为了解决这个难题,我们可以使用简单的数学。我们将数组中的所有数字相加,并将其减去如果缺少一个数字的总数。结果将是缺失的数字。

int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12};

int N = numbers[numbers.length-1];  //The last element in the array
int expectedSum = (N * (N + 1)) / 2;
int actualSum = Arrays.stream(numbers).sum();

int missingNumber = expectedSum - actualSum;
System.out.println(missingNumber);	//Prints '10'

8. 查找数组中的重复项

给定一个整数数组,编写一个 Java 程序来查找数组中存在的所有重复元素。

为了解决上述难题,最简单的方法是创建一个类似于多重映射的数据结构。当我们在多重映射中放置键值对时,如果键已存在,它会用新值覆盖以前的值。相反,新值将以列表形式添加到同一键,以便多重映射将以列表形式包含相同键的两个值。

然后我们检查多重映射中的每个键,如果某个键的值多于 1,则相应的键或数字是重复的。

Integer[] array = {1, 2, 3, 2, 4, 3, 5, 6, 5, 7, 8, 8, 9};

MultiMap multiMap = new MultiHashMap();
for (int num : array) {
  multiMap.put(num, num);
}

List duplicates = multiMap.keySet().stream()
    .filter(i -> ((ArrayList) multiMap.get(i)).size() > 1)
    .toList();

System.out.println(duplicates);	//Prints [2, 3, 5, 8]

9. 两数之和

给定一个整数数组和一个目标和,找到两个数字相加等于目标和。

为了解决这个难题,我们遍历数组中的数字。并且对于每个数字

  • 从期望的总和中减去该数字,以找到补数
  • 检查数组是否包含补数。

让我们编写一个简单的 Java 程序来解决两数之和问题。

private static int[] twoSum(int[] nums, int sum) {

  for (int i = 0; i < nums.length; i++) {
    int complement = sum - nums[i];
    int foundAtIndex = Arrays.binarySearch(nums, complement);

    if(foundAtIndex > 0) {
      return new int[] {nums[i], nums[foundAtIndex]};
    }
  }
  return null;
}

上述函数返回一个数组,其中包含两个加起来等于指定 总和 的数字。让我们测试一下。

int[] nums = {2, 7, 11, 18};
int sum = 9;

int[] result = twoSum(nums, sum);

System.out.println(Arrays.toString(result));	//[2, 7]

10. 查找数组中最大的元素

编写一个程序来查找整数数组中最大的元素。

要查找数组中最大的数字,我们创建一个变量并用数组中的第一个值初始化它。接下来,我们遍历数组元素,并将每个数字与该变量进行比较。如果该数字大于该变量,则我们将该数字分配给该变量。

当循环结束时,该变量包含数组中的最大值。

public static int findLargestElement(int[] array) {

  if (array == null || array.length == 0) {
    throw new IllegalArgumentException("Array is empty or null.");
  }
  
  int largest = array[0];
  
  for (int i = 1; i < array.length; i++) {
    if (array[i] > largest) {
        largest = array[i];
    }
  }

  return largest;
}

让我们用一个简单的 Java 程序来测试上述函数。

int[] array = {10, 20, 30, 40, 90, 23, 12, 60};

int largest = findLargestElement(array);
System.out.println("The largest element in the array is: " + largest);	//Prints 90

11. 查找没有重复字符的最长子字符串

给定一个字符串,编写一个 Java 程序来查找没有重复字符的最长子字符串的长度。

我们可以使用滑动窗口方法来解决这个难题。它使用两个变量 leftright 来跟踪指定字符串中子字符串的起始和结束索引。最初,它们可以是 0 和 0。

一个 Set 代表当前正在测试的子字符串。最初,该集合可以是空的。

我们遍历字符串,同时扩展子字符串的 right 边界。如果当前字符已经存在于集合中,则意味着我们有重复的字符,并将其从滑动窗口的左侧删除,并将左边界向右移动。此过程将继续,直到当前子字符串中没有重复字符。

最后,我们返回表示最大子字符串的 Set 的大小。

public static int findLongestSubstringLength(String str) {

  int maxLength = 0;
  int left = 0;
  int right = 0;
  Set<Character> slidingWindow = new HashSet<>();
  
  while (right < str.length()) {

    char currentChar = str.charAt(right);
    
    if (slidingWindow.contains(currentChar)) {
      slidingWindow.remove(str.charAt(left));
      left++;
    } else {
      slidingWindow.add(currentChar);
      maxLength = Math.max(maxLength, right - left + 1);
      right++;
    }
  }
  
  return maxLength;
}

让我们测试该程序。

String input = "abcabcbb";
int length = findLongestSubstringLength(input);

System.out.println(length); //3

12. 查找多个排序数组中的公共元素

给定多个排序数组,查找所有数组中存在的公共元素。

可以使用不同的技术解决上述难题。例如,我们可以遍历第一个数组中的元素,并检查其余数组中的每个元素。如果该元素存在于所有其他数组中,则将其添加到公共元素列表中。但是,此方法需要大量的迭代,并且不是合适的解决方案。

另一种解决方案是逐步查找两个数组之间的公共元素,然后检查第三个数组中的公共元素,依此类推。

Create a list commonElements and initialize it with 1st array.
Iterate over the elements of commonElements
	Search each element in the 2nd array; if element is found, add to the temp array;
	Assign temp array to commonElements
Repeat

让我们为上述伪代码编写一个 Java 程序。

public static List<Integer> findCommonElements(Integer[][] arrays) {
  if (arrays == null || arrays.length == 0) {
    return List.of(); //empty arguments
  }

  List<Integer> commonElements = Arrays.asList(arrays[0]);

  for (int i = 1; i < arrays.length; i++) {
    List<Integer> temp = new ArrayList<>();
    for (int num : arrays[i]) {
      if (commonElements.contains(num)) {
        temp.add(num);
      }
    }
    commonElements = temp;
  }
  return commonElements;
}

现在用几个数组测试上述程序。

Integer[][] arrays = {
    {1, 2, 3, 4, 5},
    {2, 4, 6, 8},
    {2, 3, 4, 7},
    {4, 5, 8, 9}
};

List<Integer> commonElements = findCommonElements(arrays);
System.out.println("Common elements in the arrays: " + commonElements);	//Prints [4]

13. 旋转数组

编写一个 Java 程序将大小为 n 的数组向右旋转 k 个位置。

要解决这个难题,我们可以使用以下伪代码。我们还将使用一个示例数组来更好地理解它。

假设,我们有初始数组 [1, 2, 3, 4, 5] 并且我们想将其旋转 2 个位置。

Intialize the variables n = array.length (5); and k = 2;
Rotate the complete array ->			[5, 4, 3, 2, 1]
Reverse the array from positions 0 to k-1 -> 	[4, 5, 3, 2, 1]
Reverse the array from positions k to n-1 -> [4, 5, 1, 2, 3]

程序完成后,数组将反转 k 个位置。

让我们为上述伪代码编写一个 Java 程序。

public static void rotateArray(int[] array, int k) {
  if (array == null || array.length == 0) {
    return;
  }

  int n = array.length;
  k = k % n; // Adjust k if it is greater than n

  reverseArray(array, 0, n - 1); // Reverse the entire array
  reverseArray(array, 0, k - 1); // Reverse the first k elements
  reverseArray(array, k, n - 1); // Reverse the remaining n-k elements
}

public static void reverseArray(int[] array, int start, int end) {

  while (start < end) {
    int temp = array[start];
    array[start] = array[end];
    array[end] = temp;
    start++;
    end--;
  }
}

我们可以用以下示例测试逻辑

int[] array = {1, 2, 3, 4, 5};
int k = 2;

System.out.println("Original Array: " + Arrays.toString(array));

rotateArray(array, k);

System.out.println("Rotated Array: " + Arrays.toString(array));

Program output: 

Original Array: [1, 2, 3, 4, 5]
Rotated Array: [4, 5, 1, 2, 3]

14. 更多难题

如果您遇到更多此类难题,请不要忘记分享 – 如果您认为它们可能会帮助他人。

祝您学习愉快!!

Github 上的源代码

评论

订阅
通知
0 条评论
最多投票
最新 最旧
内联反馈
查看所有评论

关于我们

HowToDoInJava 提供 Java 和相关技术的教程和操作指南。

它还分享最佳实践、算法和解决方案以及经常被问到的面试题。

我们的博客

REST API 教程

关注我们