Java 竞争性编程技巧(Java 8)

2025年1月6日 | 阅读18分钟

在竞赛编程中,解决问题不仅需要能力和技巧,还需要高效解决问题的能力。在Java中,以下是一些技巧,可以帮助您在时间限制内解决问题时表现更好。

1. 不使用 % 运算符检查数字是奇数还是偶数

在二进制表示中,偶数是指最低有效位(最右边的位)为0的数,而奇数是指最低有效位为1的数。这就是使用按位运算技巧的基础。因此,通过给定数字与1进行按位AND运算,可以找到最低有效位。如果结果是0,则该数字是偶数;否则,如果得到的结果是1,则该数字是奇数。

文件名: EvenOddChecker.java

输出

 
10 is even.
15 is odd.
24 is even.
37 is odd.

解释

checkEvenOrOdd 方法以整数作为输入,并使用按位AND运算来确定它是偶数还是奇数。在 main 方法中,一个数组 numbers 包含用于测试的示例数字。一个循环遍历数组中的每个数字,并为每个数字调用 checkEvenOrOdd 方法。

2. 快速乘以或除以 2

快速乘以或除以 2,也称为位移,是一种在不使用传统算术运算符(* 用于乘法,/ 用于除法)的情况下将整数乘以或除以 2 的技术。这项技术在性能至关重要的场景中特别有用,例如在竞赛编程或注重优化的开发中。

文件名: FastMultiplicationDivision.java

输出

 
Number: 10
Fast Multiply by 2: 20
Fast Divide by 2: 5
Number: 15
Fast Multiply by 2: 30
Fast Divide by 2: 7
Number: 30
Fast Multiply by 2: 60
Fast Divide by 2: 15

3. 使用 XOR 交换两个数字

在编程面试和竞赛编程中,在不使用临时变量的情况下交换两个数字是一个常见问题。最有效的方法之一是使用按位异或(XOR)运算。XOR(异或)是一种按位运算,当且仅当比较的位不同时才结果为 1;否则,结果为 0。这种特性使 XOR 特别适用于在不使用额外内存的情况下交换两个数字。

文件名: SwapUsingXOR.java

输出

 
Before swapping: a = 10, b = 20
After swapping: a = 20, b = 10
Before swapping: a = 5, b = 15
After swapping: a = 15, b = 5

4. 使用 StringBuffer 进行字符串操作

在 Java 中,字符串是不可变的,这意味着一旦创建了 String 对象,就无法更改它。这种不可变性在执行多个字符串操作时可能导致性能问题,因为每次修改都会创建一个新的 String 对象。为了解决这个问题,Java 提供了 StringBuffer 类来进行高效的字符串操作。StringBuffer 旨在用于字符串频繁修改的场景,例如追加、插入或删除字符。与 StringBuilder 不同,StringBuffer 是同步的,使其成为线程安全的,但在单线程环境中速度稍慢。

文件名: StringBufferDemo.java

输出

 
After append: Hello World
After insert: Hello, World
After delete: Hello World
After replace: Hello Java
After reverse: avaJ olleH
Current capacity: 21
Current length: 10
After setLength(5): Hello
After appending various data types: Value: 100 99.99 true
After insert at index 2: 12abc345
After deleting from index 2 to 5: 12345
After replacing from index 1 to 3: 1XYZ45
After reversing: 54ZYX1

5. 计算最高有效数字

数字的最高有效数字(MSD)是其十进制表示中最左边的数字。例如,12345 的最高有效数字是 1。这个数字可以提供关于数字尺度的宝贵信息。可以使用对数高效地计算 MSD。计算数字的以 10 为底的对数。从对数中减去对数的整数部分,得到小数部分。将 10 提高到这个小数部分的幂。结果的整数部分就是最高有效数字。

文件名: MostSignificantDigit.java

输出

 
The most significant digit of 12345 is: 1
The most significant digit of 2024 is: 2
The most significant digit of 56 is: 5
The most significant digit of 1 is: 1

6. 使用对数计算数字的位数

计算数字的位数是许多算法和应用程序中的常见操作。计算数字绝对值的以 10 为底的对数。取对数的整数部分。将结果加 1 即可得到数字的位数。

文件名: NumberOfDigits.java

输出

 
The number of digits in 12345 is: 5
The number of digits in 2024 is: 4
The number of digits in 987 is: 3
The number of digits in 56 is: 2
The number of digits in 1 is: 1

7. 内置 GCD 方法

在 Java 中,您可以使用 java.math.BigInteger 类高效地计算两个数字的 GCD,该类提供了一个内置的 gcd 方法来计算两个 BigInteger 对象的 GCD。此方法简化了过程,确保了准确高效的计算,而无需手动实现算法。

文件名: GCDExample.java

输出

 
The GCD of 56 and 98 is: 14
The GCD of 54 and 24 is: 6
The GCD of 100 and 25 is: 25
Error: GCD is not defined for both numbers being zero. (num1: 0, num2: 0)

8. 检查一个数字是否为素数

素数是大于 1 且除了 1 和自身之外没有正约数的自然数。Java 在 java.math.BigInteger 类中提供了一个方便高效的素数检查方法,称为 isProbablePrime()。此方法可确定 BigInteger 是否可能是素数,具有一定的置信度,或者肯定是合数。

文件名: PrimeCheckExample.java

输出

 
Is 2 a prime number? true
Is 4 a prime number? false
Is 17 a prime number? true
Is 18 a prime number? false

9. 高效检查一个数字是否为 2 的幂

一个数字是 2 的幂当且仅当它的二进制表示中恰好有一个“1”位。这个属性可以比重复除以 2 更有效地检查一个数字是否为 2 的幂。正常的除法技术的时间复杂度为 O(logN),但我们可以以 O(n) 的时间解决它,其中 n 是数字二进制表示中的位数。

文件名: PowerOfTwoChecker.java

输出

 
Is 16 a power of 2? true
Is 100 a power of 2? false
Is 512 a power of 2? true
Is 2097 a power of 2? false

10. 排序算法

Java 提供了两种主要的排序方法:用于数组的 Arrays.sort() 和用于集合的 Collections.sort()。这两种方法都属于 Java 标准库,并提供了高效排序元素的方法。

在以下代码中,我们使用 Arrays.sort() 进行排序。

文件名: ArraysSortExample.java

输出

 
Sorted int array: [1, 2, 3, 5, 8]
Sorted String array: [apple, banana, cherry, date]

在以下代码中,我们使用 Collections.sort() 进行排序。

文件名: CollectionsSortExample.java

输出

 
Sorted Integer list: [1, 2, 3, 5, 8]
Sorted String list: [apple, banana, cherry, date]
Sorted String list in reverse order: [date, cherry, banana, apple]

11. 搜索算法

Java 提供了两种主要的二分搜索方法:用于数组的 Arrays.binarySearch() 和用于集合的 Collections.binarySearch()。这两种方法都实现了二分搜索算法,该算法要求数据已排序,时间复杂度为 O(logn)。

在以下代码中,我们使用 Arrays.binarySearch() 进行搜索。

文件名: ArraysBinarySearchExample.java

输出

 
Index of 3 in intArray: 2
Index of 'cherry' in stringArray: 2
Index of 'Banana' in sorted stringArray (case insensitive): 1

在以下代码中,我们使用 Collections.binarySearch() 进行搜索。

文件名: CollectionsBinarySearchExample.java

输出

 
Index of 3 in intList: 2
Index of 'cherry' in stringList: 2
Index of 'Banana' in sorted stringList (case insensitive): 1

12. 复制算法

复制数组和集合是编程中常见的操作,特别是当您需要复制数据结构而不更改原始数据结构时。Java 为此目的提供了内置方法,包括 Arrays.copyOf()、Arrays.copyOfRange() 和 Collections.copy()。这些方法提供了一种简单的方法来创建数组和集合的副本。

下面的代码显示了 Arrays.copyOf() 方法的用法

文件名: ArraysCopyExample.java

输出

 
Original int array: [1, 2, 3, 4, 5]
Copied int array: [1, 2, 3, 4, 5, 0, 0]
Original String array: [apple, banana, cherry]
Copied String array: [apple, banana, cherry, null, null]

下面的代码演示了 Arrays.copyOfRange() 方法的用法

文件名: ArraysCopyOfRangeExample.java

输出

 
Original int array: [1, 2, 3, 4, 5]
Range copied int array: [2, 3, 4]
Original String array: [apple, banana, cherry]
Range copied String array: [apple, banana]

以下代码演示了 Collections.copy() 方法的用法

文件名: CollectionsCopyExample.java

输出

 
Original source list: [apple, banana, cherry]
Original destination list: [, , ]
Copied destination list: [apple, banana, cherry]

13. 频率和旋转

Java 在 Collections 类中提供了多个实用方法来对集合执行常见操作。其中两个有用的方法是 Collections.rotate() 和 Collections.frequency()。这些方法有助于轻松高效地操作和分析集合。

文件名: CollectionsRotateFrequencyExample.java

输出

 
Original list: [a, b, c, d, e]
List after rotating by 2: [d, e, a, b, c]
List after rotating by -3: [b, c, d, e, a]
List: [apple, banana, apple, cherry, banana, apple]
Frequency of 'apple': 3
Frequency of 'banana': 2
Frequency of 'cherry': 1

14. Java 集合框架

Java 集合框架是表示和操作对象集合的统一体系结构。它包括接口、实现和算法,以帮助有效地管理对象组。该框架是 java.util 包的一部分,并提供了一组设计良好的类和接口来处理不同类型的集合,如列表、集和映射。

使用 LinkedList 的示例代码

文件名: LinkedListExample.java

输出

 
LinkedList: [Apple, Banana, Cherry, Date]
First element: Apple
Second element: Banana
After adding elements: [Mango, Apple, Banana, Cherry, Date, Grapes]
After removing elements: [Apple, Banana, Cherry, Date]

15. 使用包装器类进行基数转换

基数转换涉及将数字的基从一个基更改为另一个基。常用的基包括二进制(基 2)、八进制(基 8)、十进制(基 10)和十六进制(基 16)。Java 在 Integer 等包装器类中提供了内置方法,以方便地进行这些转换。

基数: 数字系统的基数。例如,二进制是基 2,十进制是基 10,十六进制是基 16。

包装器类: Java 中的类,提供将原始数据类型作为对象进行操作的方法。示例包括 Integer、Double、Character 等。

文件名: RadixConversionExample.java

输出

 
Binary representation of 255 is: 11111111
Octal representation of 255 is: 377
Hexadecimal representation of 255 is: ff
Parsed binary number (11111111) is: 255
Parsed octal number (377) is: 255
Parsed hexadecimal number (ff) is: 255

16. NullPointerException

NullPointerException 是 Java 中的一个运行时异常,当应用程序尝试使用一个未初始化或指向 null 的对象引用时会发生此异常。这可能发生在各种情况下,例如在 null 对象上调用方法、访问或修改 null 对象的字段,或者获取 null 数组的长度。

文件名: NullPointerExceptionExample.java

输出

 
Caught NullPointerException: str is null
Caught NullPointerException: person is null
Caught NullPointerException: numbers array is null
Length using Optional: 0

结论

总而言之,竞赛编程通常需要高效、简洁和可靠的代码。Java 8 提供了丰富的功能集,如果使用得当,可以显著提高您在比赛中的表现。