Solution: Fraction to Recurring Decimal
Let's solve the Fraction to Recurring Decimal problem using the Hash Map pattern.
Statement
Given the two integer values of a fraction, numerator
and denominator
, implement a function that returns the fraction in string format. If the fractional part repeats, enclose the repeating part in parentheses.
Constraints:
denominator
-
numerator
,denominator
Solution
So far, you’ve probably brainstormed some approaches and have an idea of how to solve this problem. Let’s explore some of these approaches and figure out which one to follow based on considerations such as time complexity and any implementation constraints.
Naive approach
A naive approach is to use an array to store the remainders to determine the repetition of remainders. We can detect repetitions by checking if the remainder already exists in the array during each calculation. This approach involves using a nested loop, with the outer loop calculating the remainder and the inner loop searching for the remainder in the array.
The time complexity of this approach is , where is the number of digits in the denominator. This is because we’re using a nested loop. Let’s see if we can use the hash map pattern to find a faster solution.
Optimized approach using hash maps
To solve this problem, we can use the hash map pattern to develop a faster solution. The idea is to use a hashmap to store the remainder, and every time we calculate the remainder, we can check if the remainder is already in the hash map in time complexity.
Note: In the following section, we will gradually build the solution. Alternatively, you can skip straight to just the code.
Step-by-step solution construction
Let’s start with the initial conditions:
-
If the numerator is zero, simply return
0
. -
If either of the numerator or denominator is negative, append the minus character (
-
) to theresult
string, and make the numerator and denominator positive.
class FractionToDecimal{public static String fractionToDecimal(int numerator, int denominator){String result = "";HashMap<Integer, Integer> remainderMap = new HashMap<Integer, Integer>();System.out.println("\n\tnumerator: " + numerator);System.out.println("\tdenominator: " + denominator);// if numerator is 0, then return 0 in the stringif(numerator == 0){System.out.println("\tAs numerator is 0, so return 0.");return "0";}// If numerator or denominator is negative, then append "-" to the resultif(numerator < 0 ^ denominator < 0){System.out.println("\tAs numerator or denominator is negative. Appending - to the result");result += '-';}// Make numerator and denominator positive after adding "-" to the resultnumerator = Math.abs(numerator);denominator = Math.abs(denominator);return result;}// Driver codepublic static void main(String[] args){int [][]inputs = {{0, 4}, {4, 2}, {5, 333}, {2, 3}, {47, 18}, {93, 7} , {-5, 333}, {47, -18}, {-4, -2}};for(int i = 0;i < inputs.length; i++){System.out.print(i + 1 + ".\tInput: fraction_to_decimal(");for (int j = 0; j < inputs[i].length-1; j++){System.out.print(inputs[i][j]);System.out.print(", ");}System.out.println(inputs[i][inputs[i].length - 1] + ")");String result = fractionToDecimal(inputs[i][0],inputs[i][1]);System.out.println(new String(new char[100]).replace('\0', '-'));}}}
We calculate the quotient and remainder of the division and then perform the following steps:
-
Append the quotient of the division to the
result
string. -
If the remainder is , return the
result
string, since the denominator completely divides the numerator. -
Otherwise, if the remainder is not , append the dot (
.
) character to theresult
string.
class FractionToDecimal{public static String fractionToDecimal(int numerator, int denominator){String result = "";HashMap<Integer, Integer> remainderMap = new HashMap<Integer, Integer>();// if numerator is 0, then return 0 in the stringif(numerator == 0){return "0";}// If numerator or denominator is negative, then append "-" to the resultif(numerator < 0 ^ denominator < 0){result += '-';}// Make numerator and denominator positive after adding "-" to the resultnumerator = Math.abs(numerator);denominator = Math.abs(denominator);// Calculate the quotientint quotient = numerator / denominator;System.out.println("\n\tquotient: " + quotient);int remainder = (numerator % denominator) * 10;System.out.println("\tremainder: " + remainder);// Append the quotient part in the resultresult += Integer.toString(quotient);System.out.println("\tresult: " + result);if(remainder == 0)return result;else{System.out.println("\tAs remainder is not zero. Append . to the result.");// Append . before the right partresult += ".";System.out.println("\tNow result is: " + result);}return result;}// Driver codepublic static void main(String[] args){int [][]inputs = {{2, 4}, {4, 2}, {5, 333}, {2, 3}, {47, 18}, {93, 7} , {-5, 333}, {47, -18}, {-4, -2}};for(int i = 0;i < inputs.length; i++){System.out.print(i + 1 + ".\tInput: fraction_to_decimal(");for (int j = 0; j < inputs[i].length-1; j++){System.out.print(inputs[i][j]);System.out.print(", ");}System.out.println(inputs[i][inputs[i].length - 1] + ")");String result = fractionToDecimal(inputs[i][0],inputs[i][1]);System.out.println(new String(new char[100]).replace('\0', '-'));}}}
Start a loop until the remainder is and every time check, if the remainder exists in the keys of the hash map:
-
If it does, do the following:
-
Store the value of the corresponding key in a variable,
beginning
. This variable will store the starting index of the recurring part of the decimal from theresult
string. -
Store the non-recurring part of the
result
variable in theleft
variable. This is done by slicing theresult
string from the index0
to the indexbeginning
. -
Store the recurring part of the
result
variable in theright
variable. This is done by slicing theresult
string from the indexbeginning
to its last index. -
Update the
result
variable by storing the following concatenated string in it:left
+'('
+right
+')'
.
-
-
Otherwise, if the remainder is not present in the hashmap, do the following:
-
Insert the remainder (key) and the current length of the
result
string (value) in the hash map. -
Calculate the new quotient and remainder by dividing the current remainder by the denominator.
-
Append the quotient to the
result
string.
-
The slides below illustrate how we’d like the algorithm to run:
import java.util.*;class FractionToDecimal {public static String fractionToDecimal(int numerator, int denominator) {String result = "";HashMap<Integer, Integer> remainderMap = new HashMap<Integer, Integer>();System.out.println("\n\tnumerator: " + numerator);System.out.println("\tdenominator: " + denominator);// if numerator is 0, then return 0 in the stringif (numerator == 0) {System.out.println("\tAs numerator is 0, so return 0.");return "0";}// If numerator or denominator is negative, then append "-" to the resultif (numerator < 0 ^ denominator < 0) {System.out.println("\tAs numerator or denominator is negative. Appending - to the result");result += '-';}// Make numerator and denominator positive after adding "-" to the resultnumerator = Math.abs(numerator);denominator = Math.abs(denominator);// Calculate the quotientint quotient = numerator / denominator;System.out.println("\n\tquotient: " + quotient);int remainder = (numerator % denominator) * 10;System.out.println("\tremainder: " + remainder);// Append the quotient part in the resultresult += Integer.toString(quotient);System.out.println("\tresult: " + result);if (remainder == 0)return result;else {System.out.println("\tAs remainder is not zero. Append . to the result.");// Append . before the right partresult += ".";System.out.println("\tNow result is: " + result);// Right side of the decimal pointwhile (remainder != 0) {System.out.println("\tremainder: " + remainder);// if digits are repeating, it means the remainder is already present in the hashmapif (remainderMap.containsKey(remainder)) {int beginning = remainderMap.get(remainder);String left = result.substring(0, beginning);String right = result.substring(beginning, result.length());result = left + "(" + right + ")";System.out.println("\n\tAs remainder " + remainder + " is in hashmap " + remainderMap+ "\n\tbeginning: " + beginning + "\tleft: " + left + "\tright: " + right + "\tresult: "+ result);return result;} else {// Otherwise put the remainder in the hashmapSystem.out.println("\tPutting remainder in hashmap...");remainderMap.put(remainder, result.length());quotient = remainder / denominator;result += String.valueOf(quotient);remainder = (remainder % denominator) * 10;}}return result;}}// Driver Codepublic static void main(String[] args) {int[][] inputs = { { 0, 4 }, { 4, 2 }, { 5, 333 }, { 2, 3 }, { 47, 18 }, { 93, 7 }, { -5, 333 }, { 47, -18 },{ -4, -2 } };for (int i = 0; i < inputs.length; i++) {System.out.print(i + 1 + ".\tInput: fraction_to_decimal(");for (int j = 0; j < inputs[i].length - 1; j++) {System.out.print(inputs[i][j]);System.out.print(", ");}System.out.println(inputs[i][inputs[i].length - 1] + ")");String result = fractionToDecimal(inputs[i][0], inputs[i][1]);System.out.println("\n\tOutput: " + result);System.out.println(new String(new char[100]).replace('\0', '-'));}}}
Just the code
Here's the complete solution to this problem:
import java.util.*;class FractionToDecimal {public static String fractionToDecimal(int numerator, int denominator) {String result = "";HashMap<Long, Integer> remainderMap = new HashMap<>();if (numerator == 0) {return "0";}if ((numerator < 0) ^ (denominator < 0)) {result += '-';}long num = Math.abs((long) numerator);long den = Math.abs((long) denominator);long quotient = num / den;long remainder = (num % den) * 10;result += quotient;if (remainder == 0) {return result;} else {result += ".";while (remainder != 0) {if (remainderMap.containsKey(remainder)) {int beginning = remainderMap.get(remainder);result = result.substring(0, beginning) + "(" + result.substring(beginning) + ")";return result;} else {remainderMap.put(remainder, result.length());quotient = remainder / den;remainder = (remainder % den) * 10;result += quotient;}}return result;}}public static void main(String[] args) {int[][] inputs = { { 0, 4 }, { 4, 2 }, { 5, 333 }, { 2, 3 }, { 47, 18 }, { 93, 7 }, { -5, 333 }, { 47, -18 },{ -4, -2 } };for (int i = 0; i < inputs.length; i++) {System.out.print(i + 1 + ".\tInput: fraction_to_decimal(");for (int j = 0; j < inputs[i].length - 1; j++) {System.out.print(inputs[i][j]);System.out.print(", ");}System.out.println(inputs[i][inputs[i].length - 1] + ")");String result = fractionToDecimal(inputs[i][0], inputs[i][1]);System.out.println("\tOutput: " + result);System.out.println(new String(new char[100]).replace('\0', '-'));}}}
Solution summary
To recap, the solution to this problem can be divided into the following parts:
-
If the numerator is zero, simply return 0.
-
If either of the numerator or denominator is negative, append the minus character (-) to the result string, and make the numerator and denominator positive.
-
Calculate the quotient and the remainder. Append the quotient part to the result.
-
Check if the remainder is , then return the result.
-
If the remainder is not , append the dot (
'.'
) character to the result. -
Start a loop until the remainder is , and every time check the remainder in the hash map. If the remainder already exists in the hash map, then make the recurring decimal from the fraction. If the remainder does not exist in the hash map, put it in the hash map.
Time complexity
The time complexity of our solution is , where is the lowest denominator we can get after simplifying the expression . For example, with , is , and with , is . This is because we will encounter at most remainders in our calculations, and therefore, iterations of the loop will be completed in the worst case.
Space complexity
The space complexity of our solution is , where is the lowest denominator we can get after simplifying the expression . For example, with , is , and with , is . This is because we will encounter at most remainders in our calculations, all of which will need to be stored in the hash map we use to detect the recurrence of remainders.
Level up your interview prep. Join Educative to access 70+ hands-on prep courses.