# Memoization and Tabulation Approaches

In competitive programming, efficiency and speed are key factors to solve problems within the given constraints. One way to optimize the solutions is by using memoization and tabulation approaches. These techniques help in avoiding repetitive calculations and store intermediate results for future use. In this article, we will explore both methods and understand their usage in solving complex problems using Java.

## Memoization Approach

Memoization is a top-down approach where we store the results of expensive function calls and retrieve them when needed again. It is typically implemented using a data structure, such as an array or a hashmap, to store the function results. To apply memoization, we follow these steps:

1. Define a storage data structure to store the results of function calls.
2. Before making a recursive function call, check if the result for the given inputs is already computed and stored.
3. If the result is available, return it from the storage. Otherwise, calculate the result and store it for future use.
4. Finally, return the result.

Let's understand memoization with an example. Consider the problem of finding the nth Fibonacci number. The Fibonacci sequence is defined as follows: `f(0) = 0, f(1) = 1, f(n) = f(n-1) + f(n-2)`. We can solve this problem using memoization as shown below:

``````// Define memoization storage
int[] memo = new int[n + 1];

public int fibonacci(int n) {
// Check if result is already memoized
if (memo[n] != 0) {
return memo[n];
}

// Base cases
if (n == 0) {
return 0;
}
if (n == 1) {
return 1;
}

// Recursive calls
memo[n] = fibonacci(n - 1) + fibonacci(n - 2); // Compute and memoize result

return memo[n];
}``````

In the above example, we check if the result is already stored in the `memo` array before making recursive calls. If the result is available, we directly return it, saving computation time. Otherwise, we compute the result, store it in the `memo` array, and return it.

## Tabulation Approach

Tabulation is a bottom-up approach where we build a table to store and retrieve the results of subproblems. Unlike memoization, it avoids recursion and calculates results iteratively. To apply tabulation, we follow these steps:

1. Define a table data structure to store the results of subproblems.
2. Initialize the table with base cases.
3. Fill the table iteratively, building up solutions to larger subproblems using results from smaller subproblems.
4. Finally, return the required solution from the table.

Let's illustrate tabulation with an example. Consider the problem of finding the nth Fibonacci number using tabulation. We can solve it as follows:

``````public int fibonacci(int n) {
// Initialize table with base cases
int[] table = new int[n + 1];
table[0] = 0;
table[1] = 1;

// Fill table iteratively
for (int i = 2; i <= n; i++) {
table[i] = table[i - 1] + table[i - 2];
}

return table[n];
}``````

In the above example, we initialize the `table` array with base cases and iteratively fill it up to reach the desired solution. By avoiding recursive calls, we optimize the computation time. This approach is particularly useful when the problem exhibits overlapping subproblems.

## Conclusion

Memoization and tabulation approaches are powerful techniques to optimize solutions in competitive programming. By avoiding redundant calculations and reusing intermediate results, these approaches improve efficiency and speed. Java provides various data structures and methods to implement both approaches effectively. Understanding these techniques and their appropriate usage can significantly enhance your problem-solving skills in competitive programming using Java.