Java tricks for Competitive Programming (for Java 8)

In competitive programming it does not only require ability and skill to solve problems but also the ability to solve the problem efficiently. In Java, here are some tips and tricks that can help one to perform better when solving problems under time constraint.

1. Checking if the number is even or odd without using the % operator

In Binary representation, even numbers are those in which the least significant bit (the right most bit) is 0 while odd numbers are those in which the least significant bit is 1, this is the basis of using bitwise technique. Thus, considering the bitwise AND operation between a given number and 1, the least significant bit will be found. If the result is 0, the number is even; else, the number is odd if the result obtained is 1.

Filename: EvenOddChecker.java

Output:

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

Explanation

The checkEvenOrOdd method takes an integer number as input and determines whether it is even or odd using bitwise AND operation. In the main method, an array numbers contains example numbers to test. A loop iterates through each number in the array and calls checkEvenOrOdd method for each number.

2. Fast Multiplication or Division by 2

Fast multiplication or division by 2, also known as bit shifting, is a technique used to multiply or divide an integer by 2 without using the traditional arithmetic operators (* for multiplication and / for division), this technique is particularly useful in scenarios where performance is critical, such as in competitive programming or optimization-focused development.

Filename: FastMultiplicationDivision.java

Output:

 
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. Swapping of two numbers using XOR

Swapping two numbers without using a temporary variable is a common problem in programming interviews and competitive programming. One of the most efficient ways to achieve this is by using the bitwise XOR operation. XOR (exclusive OR) is a bitwise operation that results in 1 only if the bits being compared are different; otherwise, it results in 0, this property makes XOR particularly useful for swapping two numbers without using additional memory.

Filename: SwapUsingXOR.java

Output:

 
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. Using String Buffer for String Manipulations

In Java, strings are immutable, meaning once a String object is created, it cannot be changed and this immutability can lead to performance issues when performing multiple string manipulations, as each modification creates a new String object. To address this, Java provides StringBuffer class for efficient string manipulation. StringBuffer is designed for use in scenarios where strings undergo frequent modifications, such as appending, inserting, or deleting characters. Unlike StringBuilder, StringBuffer is synchronized, making it thread-safe but slightly slower in single-threaded environments.

Filename: StringBufferDemo.java

Output:

 
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. Calculating the Most Significant Digit

The most significant digit (MSD) of a number is the leftmost digit in its decimal representation. For example, the most significant digit of 12345 is 1, this digit can provide valuable information about the scale of the number. Calculating the MSD can be efficiently done using logarithms. Compute the base-10 logarithm of the number. Subtract the floor of the logarithm from the logarithm to get the fractional part. Raise 10 to the power of this fractional part. The integer part of the result is the most significant digit.

Filename: MostSignificantDigit.java

Output:

 
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. Calculating the Number of Digits Using Logarithms

Calculating the number of digits in a number is a common operation in many algorithms and applications. Compute the base-10 logarithm of the absolute value of the number. Take the floor of the logarithm. Add 1 to the result to get the number of digits.

Filename: NumberOfDigits.java

Output:

 
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. Inbuilt GCD Method

In Java, you can compute the GCD of two numbers efficiently using the java.math.BigInteger class, which provides an inbuilt method gcd to calculate the GCD of two BigInteger objects. This method simplifies the process, ensuring accurate and efficient computation without the need for manually implementing the algorithm.

Filename: GCDExample.java

Output:

 
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. Checking for a Prime Number

A prime number is a natural number greater than 1 that has no positive divisors other than 1 and itself. Java provides a convenient and efficient method for prime checking in the java.math.BigInteger class, called isProbablePrime(), this method determines if a BigInteger is probably prime with a certain degree of confidence or definitely composite.

Filename: PrimeCheckExample.java

Output:

 
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. Efficiently Checking if a Number is a Power of 2

A number is a power of 2 if and only if it has exactly one '1' bit in its binary representation. This property can be used to check if a number is a power of 2 more efficiently than repeatedly dividing by 2. The normal division technique has a time complexity of O(logN), but we can solve it in O(n) where n is the number of digits in the binary representation of the number.

Filename: PowerOfTwoChecker.java

Output:

 
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. Sorting Algorithm

Java provides two primary methods for sorting: Arrays.sort() for arrays and Collections.sort() for collections. Both methods are part of the Java standard library and offer efficient ways to sort elements.

In the following code, we use Arrays.sort() for sorting.

Filename: ArraysSortExample.java

Output:

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

In the following code, we use Collections.sort() for sorting.

Filename: CollectionsSortExample.java

Output:

 
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. Searching Algorithm

Java provides two primary methods for binary search: Arrays.binarySearch() for arrays and Collections.binarySearch() for collections. Both methods implement the binary search algorithm, which requires the data to be sorted and has a time complexity of O(logn).

In the folllowing code, we use Arrays.binarySearch() for searching.

Filename: ArraysBinarySearchExample.java

Output:

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

In the following code, we use Collections.binarySearch() for searching.

Filename: CollectionsBinarySearchExample.java

Output:

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

12. Copy Algorithms

Copying arrays and collections is a common operation in programming, particularly when you need to duplicate data structures without altering the original ones. Java provides built-in methods for this purpose, including Arrays.copyOf(), Arrays.copyOfRange(), and Collections.copy(). These methods offer a straightforward way to create copies of arrays and collections.

The below code shows usage of Arrays.copyOf() method:

Filename: ArraysCopyExample.java

Output:

 
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]

The below code demonstrates the usage of Arrays.copyOfRange() method:

Filename: ArraysCopyOfRangeExample.java

Output:

 
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]

The following code demonstrates the usage of Collections.copy() method:

Filename: CollectionsCopyExample.java

Output:

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

13. Frequency and Rotation

Java provides several utility methods in the Collections class to perform common operations on collections. Two of these useful methods are Collections.rotate() and Collections.frequency(). These methods help in manipulating and analyzing collections easily and efficiently.

Filename: CollectionsRotateFrequencyExample.java

Output:

 
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 Collection Framework

The Java Collection Framework is a unified architecture for representing and manipulating collections of objects. It includes interfaces, implementations, and algorithms to help manage groups of objects effectively, this framework is part of the java.util package and provides a set of well-designed classes and interfaces to handle different types of collections, such as lists, sets, and maps.

An example code using a LinkedList:

Filename: LinkedListExample.java

Output:

 
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. Radix Conversion Using Wrapper Classes

Radix conversion involves changing the base of a number from one base to another. Commonly used bases include binary (base 2), octal (base 8), decimal (base 10), and hexadecimal (base 16). Java provides built-in methods in wrapper classes like Integer to facilitate these conversions easily.

Radix: The base of a number system. For example, binary is base 2, decimal is base 10, hexadecimal is base 16.

Wrapper Classes: Classes in Java that provide methods for manipulating primitive data types as objects. The examples include Integer, Double, Character, etc.

Filename: RadixConversionExample.java

Output:

 
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

A NullPointerException is a runtime exception in Java that occurs when an application attempts to use an object reference that has not been initialized or points to null, this can happen in various scenarios such as calling a method on a null object, accessing or modifying a field of a null object, or taking the length of a null array.

Filename: NullPointerExceptionExample.java

Output:

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

Conclusion

In conclusion, competitive programming often demands efficient, concise, and reliable code. Java 8 offers a rich set of features that, when leveraged properly, can significantly enhance your performance in competitions.