diff --git a/docs/dsa/binary_search/Iterative_binary_search.md b/docs/dsa/binary_search/Iterative_binary_search.md new file mode 100644 index 000000000..7fab7b613 --- /dev/null +++ b/docs/dsa/binary_search/Iterative_binary_search.md @@ -0,0 +1,134 @@ +--- +id: iterative-binary-search-DSA +title: Iterative Binary Search +sidebar_label: Iterative Binary Search +sidebar_position: 7 +description: "In this blog post, we'll explore the iterative binary search algorithm, a fundamental technique in computer science for efficiently finding an element in a sorted array. You'll learn what iterative binary search is, how it works, and its time complexity. We'll also cover practical applications and common problems you can solve using this algorithm. By the end, you'll have a thorough understanding of iterative binary search and how to implement it in your programming projects." +tags: [dsa, algorithms, binary search, iterative] +--- + +Iterative Binary Search is powerful algorithm that is essential for efficiently finding elements in sorted arrays, making it a staple in the toolkit of any adept programmer. Whether you're optimizing search operations or solving complex algorithmic challenges, understanding iterative binary search is crucial. Let's delve into its mechanics, applications, and implementation. + +## What is Iterative Binary Search? + +Iterative binary search is a highly efficient algorithm used to find an element in a sorted array. It works by repeatedly dividing the search interval in half, using an iterative approach. If the value of the search key is less than the item in the middle of the interval, the algorithm narrows the interval to the lower half. Otherwise, it narrows it to the upper half. The process continues until the search key is found or the interval is empty. + +In pseudo-code, iterative binary search is defined as follows: + +```cpp +FUNCTION iterativeBinarySearch(array, key): +low = 0 +high = array.length - 1 +WHILE low <= high: +mid = (low + high) / 2 +IF array[mid] == key: +RETURN mid +ELSE IF array[mid] < key: +low = mid + 1 +ELSE: +high = mid - 1 +RETURN -1 +``` + +```cpp +int iterativeBinarySearch(int array[], int size, int key) { + int low = 0; + int high = size - 1; + while (low <= high) { + int mid = low + (high - low) / 2; + if (array[mid] == key) { + return mid; + } else if (array[mid] < key) { + low = mid + 1; + } else { + high = mid - 1; + } + } + return -1; +} +``` + +## How Iterative Binary Search Works + +### Step-by-Step Explanation + +1. Initialize: Set two pointers, low at the beginning and high at the end of the array. +2. Middle Element: Calculate the middle element's index. +Comparison: +3. If the middle element is the target, return its index. +4. If the middle element is less than the target, discard the left half by setting low to mid + 1. +5. If the middle element is greater than the target, discard the right half by setting high to mid - 1. +6. Repeat: Repeat steps 2 and 3 until the target is found or the low pointer exceeds the high pointer. + +### Time Complexity + +The time complexity of iterative binary search is $O(logn)$, +where $𝑛$ is the number of elements in the array. This logarithmic time complexity makes iterative binary search significantly faster than linear search for large datasets. + +## Practical Applications + +Iterative binary search is widely used in various real-world applications and algorithmic problems: + +1. Searching in a Sorted Array + The primary use of iterative binary search is to find elements in a sorted array efficiently. It is the foundation for more complex search algorithms. + +2. Dictionary Lookups + Iterative binary search is used in dictionaries (like the one you're reading now) to quickly find words and their definitions. + +3. Binary Search Trees + Iterative binary search is the basis for searching in binary search trees (BSTs), a fundamental data structure in computer science. + +4. Finding Boundaries + Iterative binary search can be adapted to find the first or last occurrence of a target element, making it useful in problems requiring boundary searches. + +Common Problems Solved Using Iterative Binary Search +Iterative binary search can be adapted in various ways to solve different types of problems. Here are a couple of common problems: + +1. Lower Bound and Upper Bound + These variations of iterative binary search are used to find the first and last occurrence of a target element in a sorted array. + +Lower Bound Pseudo-Code: + +```cpp +FUNCTION lowerBound(array, key): + low = 0 + high = array.length + WHILE low < high: + mid = (low + high) / 2 + IF array[mid] < key: + low = mid + 1 + ELSE: + high = mid + RETURN low + +``` + +Upper Bound Pseudo-Code: + +```cpp +FUNCTION upperBound(array, key): + low = 0 + high = array.length + WHILE low < high: + mid = (low + high) / 2 + IF array[mid] <= key: + low = mid + 1 + ELSE: + high = mid + RETURN low + + +``` + +2. Rotated Sorted Array + Iterative binary search can be modified to handle rotated sorted arrays, where the array is sorted but then rotated at some pivot point. + +:::tip +Handle Edge Cases: Ensure your implementation correctly handles cases where the target element is not present or when the array is empty. +Prevent Overflow: When calculating the middle index, use $\text{mid} = \text{low} + \frac{\text{high} - \text{low}}{2}$ instead of $\text{mid} = \frac{\text{low} + \text{high}}{2}$ to prevent potential overflow. +Efficiency: The iterative approach often uses less memory than the recursive approach because it doesn't involve the overhead of multiple recursive function calls. +::: + +## Conclusion + +Iterative binary search is a fundamental algorithm that every programmer should master. Its efficiency and versatility make it a powerful tool for solving a wide range of problems. By understanding how iterative binary search works and how to implement its variations, you'll be well-equipped to tackle numerous challenges in your programming journey. diff --git a/docs/dsa/binary_search/_category_.json b/docs/dsa/binary_search/_category_.json new file mode 100644 index 000000000..34c97b96a --- /dev/null +++ b/docs/dsa/binary_search/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Binary Search", + "position": 7, + "link": { + "type": "generated-index", + "description": "Binary Search Algorithm is a searching algorithm used in a sorted array by repeatedly dividing the search interval in half. The idea of binary search is to use the information that the array is sorted and reduce the time complexity to O(log N). " + } +} diff --git a/docs/dsa/binary_search/binary_search.md b/docs/dsa/binary_search/binary_search.md new file mode 100644 index 000000000..e88ad102f --- /dev/null +++ b/docs/dsa/binary_search/binary_search.md @@ -0,0 +1,133 @@ +--- +id: binary-search-DSA +title: Binary Search +sidebar_label: Binary Search +sidebar_position: 6 +description: "In this blog post, we'll dive into the binary search algorithm, a fundamental technique in computer science for efficiently finding an element in a sorted array. You'll learn what binary search is, how it works, and its time complexity. We'll also cover practical applications, variations of binary search, and common problems you can solve using this algorithm. By the end, you'll have a thorough understanding of binary search and how to implement it in your programming projects." +tags: [dsa, algorithms, binary search] +--- + +Binary Search algorithm is essential for efficiently finding elements in sorted arrays, making it a staple in the toolkit of any adept programmer. Whether you're optimizing search operations or solving complex algorithmic challenges, understanding binary search is crucial. Let's delve into its mechanics, applications, and implementation. + +## What is Binary Search? + +Binary search is a highly efficient algorithm used to find an element in a sorted array. It works by repeatedly dividing the search interval in half. If the value of the search key is less than the item in the middle of the interval, the algorithm narrows the interval to the lower half. Otherwise, it narrows it to the upper half. The process continues until the search key is found or the interval is empty. + +In pseudo-code, binary search is defined as follows: + +```cpp +FUNCTION binarySearch(array, key): +low = 0 +high = array.length - 1 +WHILE low <= high: +mid = (low + high) / 2 +IF array[mid] == key: +RETURN mid +ELSE IF array[mid] < key: +low = mid + 1 +ELSE: +high = mid - 1 +RETURN -1 +``` + +In C++, this can be represented as: + +```cpp +int binarySearch(int array[], int size, int key) { +int low = 0; +int high = size - 1; +while (low <= high) { +int mid = low + (high - low) / 2; +if (array[mid] == key) { +return mid; +} else if (array[mid] < key) { +low = mid + 1; +} else { +high = mid - 1; +} +} +return -1; +} +``` + +## How Binary Search Works + +### Step-by-Step Explanation + +1. Initialize: Set two pointers, low at the beginning and high at the end of the array. +2. Middle Element: Calculate the middle element's index. +3. Comparison: + If the middle element is the target, return its index. + If the middle element is less than the target, discard the left half by setting low to mid + 1. + If the middle element is greater than the target, discard the right half by setting high to mid - 1. +4. Repeat: Repeat steps 2 and 3 until the target is found or the low pointer exceeds the high pointer. + +### Time Complexity + +The time complexity of binary search is $𝑂(log𝑛)$ + +where $n$ is the number of elements in the array. This logarithmic time complexity makes binary search significantly faster than linear search for large datasets. + +## Practical Applications + +Binary search is widely used in various real-world applications and algorithmic problems: + +1. Searching in a Sorted Array + The primary use of binary search is to find elements in a sorted array efficiently. It is the foundation for more complex search algorithms. + +2. Dictionary Lookups + Binary search is used in dictionaries (like the one you're reading now) to quickly find words and their definitions. + +3. Binary Search Trees + Binary search is the basis for searching in binary search trees (BSTs), a fundamental data structure in computer science. + +4. Finding Boundaries + Binary search can be adapted to find the first or last occurrence of a target element, making it useful in problems requiring boundary searches. + +Variations of Binary Search +Binary search can be adapted in various ways to solve different types of problems. Here are a couple of common variations: + +1. Lower Bound and Upper Bound + These variations of binary search are used to find the first and last occurrence of a target element in a sorted array. + +Lower Bound Pseudo-Code: + +```cpp +FUNCTION lowerBound(array, key): +low = 0 +high = array.length +WHILE low < high: +mid = (low + high) / 2 +IF array[mid] < key: +low = mid + 1 +ELSE: +high = mid +RETURN low +``` + +Upper Bound Pseudo-Code: + +```cpp +FUNCTION upperBound(array, key): +low = 0 +high = array.length +WHILE low < high: +mid = (low + high) / 2 +IF array[mid] <= key: +low = mid + 1 +ELSE: +high = mid +RETURN low +``` + +2. Rotated Sorted Array + Binary search can be modified to handle rotated sorted arrays, where the array is sorted but then rotated at some pivot point. + +:::Tip +Handle Edge Cases: Ensure your implementation correctly handles cases where the target element is not present or when the array is empty. +Prevent Overflow: When calculating the middle index, use $\text{mid} = \text{low} + \frac{\text{high} - \text{low}}{2}$ instead of $\text{mid} = \frac{\text{low} + \text{high}}{2}$ to prevent potential overflow. +Iterative vs. Recursive: Both iterative and recursive implementations are valid. Choose based on your preference and the problem constraints. +::: + +## Conclusion +Binary search is a fundamental algorithm that every programmer should master. Its efficiency and versatility make it a powerful tool for solving a wide range of problems. By understanding how binary search works and how to implement its variations, you'll be well-equipped to tackle numerous challenges in your programming journey. diff --git a/docs/dsa/binary_search/recursive_binary_search.md b/docs/dsa/binary_search/recursive_binary_search.md new file mode 100644 index 000000000..4d3c50f27 --- /dev/null +++ b/docs/dsa/binary_search/recursive_binary_search.md @@ -0,0 +1,126 @@ +--- +id: recursive-binary-search-DSA +title: Recursive Binary Search +sidebar_label: Recursive Binary Search +sidebar_position: 8 +description: "In this blog post, we'll explore the recursive binary search algorithm, a fundamental technique in computer science for efficiently finding an element in a sorted array. You'll learn what recursive binary search is, how it works, and its time complexity. We'll also cover practical applications and common problems you can solve using this algorithm. By the end, you'll have a thorough understanding of recursive binary search and how to implement it in your programming projects." +tags: [dsa, algorithms, binary search, recursive] +--- + +Recursive Binary Search algorithm is essential for efficiently finding elements in sorted arrays, making it a staple in the toolkit of any adept programmer. Whether you're optimizing search operations or solving complex algorithmic challenges, understanding recursive binary search is crucial. Let's delve into its mechanics, applications, and implementation. + +## What is Recursive Binary Search? + +Recursive binary search is a highly efficient algorithm used to find an element in a sorted array. It works by repeatedly dividing the search interval in half, using a recursive approach. If the value of the search key is less than the item in the middle of the interval, the algorithm narrows the interval to the lower half. Otherwise, it narrows it to the upper half. The process continues until the search key is found or the interval is empty. + +In pseudo-code, recursive binary search is defined as follows: + +```cpp +FUNCTION recursiveBinarySearch(array, low, high, key): +IF low > high: +RETURN -1 +mid = (low + high) / 2 +IF array[mid] == key: +RETURN mid +ELSE IF array[mid] < key: +RETURN recursiveBinarySearch(array, mid + 1, high, key) +ELSE: +RETURN recursiveBinarySearch(array, low, mid - 1, key) +``` + +```cpp +int recursiveBinarySearch(int array[], int low, int high, int key) { + if (low > high) { + return -1; + } + int mid = low + (high - low) / 2; + if (array[mid] == key) { + return mid; + } else if (array[mid] < key) { + return recursiveBinarySearch(array, mid + 1, high, key); + } else { + return recursiveBinarySearch(array, low, mid - 1, key); + } +} +``` + +## How Recursive Binary Search Works + +### Step-by-Step Explanation + +1. Initialize: Set two pointers, low at the beginning and high at the end of the array. +2. Middle Element: Calculate the middle element's index. +Comparison: +3. If the middle element is the target, return its index. +4. If the middle element is less than the target, discard the left half by setting low to mid + 1. +5. If the middle element is greater than the target, discard the right half by setting high to mid - 1. +6. Repeat: Repeat steps 2 and 3 until the target is found or the low pointer exceeds the high pointer. + +### Time Complexity + +The time complexity of iterative binary search is $𝑂(log𝑛)$. + +where $n$ is the number of elements in the array. This logarithmic time complexity makes iterative binary search significantly faster than linear search for large datasets. + +## Practical Applications + +Iterative binary search is widely used in various real-world applications and algorithmic problems: + +1. Searching in a Sorted Array + The primary use of iterative binary search is to find elements in a sorted array efficiently. It is the foundation for more complex search algorithms. + +2. Dictionary Lookups + Iterative binary search is used in dictionaries (like the one you're reading now) to quickly find words and their definitions. + +3. Binary Search Trees + Iterative binary search is the basis for searching in binary search trees (BSTs), a fundamental data structure in computer science. + +4. Finding Boundaries + Iterative binary search can be adapted to find the first or last occurrence of a target element, making it useful in problems requiring boundary searches. + +Common Problems Solved Using Iterative Binary Search +Iterative binary search can be adapted in various ways to solve different types of problems. Here are a couple of common problems: + +1. Lower Bound and Upper Bound + These variations of iterative binary search are used to find the first and last occurrence of a target element in a sorted array. + +Lower Bound Pseudo-Code: + +```cpp +FUNCTION lowerBound(array, low, high, key): + IF low == high: + RETURN low + mid = (low + high) / 2 + IF array[mid] < key: + RETURN lowerBound(array, mid + 1, high, key) + ELSE: + RETURN lowerBound(array, low, mid, key) + + +``` + +Upper Bound Pseudo-Code: + +```cpp +FUNCTION upperBound(array, low, high, key): + if low == high: + return low + mid = (low + high) / 2 + if array[mid] <= key: + return upperBound(array, mid + 1, high, key) + else: + return upperBound(array, low, mid, key) + +``` + +2. Rotated Sorted Array + Recursive binary search can be modified to handle rotated sorted arrays, where the array is sorted but then rotated at some pivot point. + +:::tip +Handle Edge Cases: Ensure your implementation correctly handles cases where the target element is not present or when the array is empty. +Prevent Stack Overflow: Be mindful of the recursion depth, especially for large arrays, as deep recursion can lead to stack overflow. +Efficiency: The recursive approach can be more intuitive and elegant, but consider the iterative approach for environments with limited stack size. +::: + +## Conclusion +Recursive binary search is a fundamental algorithm that every programmer should master. Its efficiency and versatility make it a powerful tool for solving a wide range of problems. By understanding how recursive binary search works and how to implement its variations, you'll be well-equipped to tackle numerous challenges in your programming journey. diff --git a/dsa-solutions/lc-solutions/0000-0099/0017-letter-combinations-of-a-phone-number.md b/dsa-solutions/lc-solutions/0000-0099/0017-letter-combinations-of-a-phone-number.md index a112c79fb..30a0f1009 100644 --- a/dsa-solutions/lc-solutions/0000-0099/0017-letter-combinations-of-a-phone-number.md +++ b/dsa-solutions/lc-solutions/0000-0099/0017-letter-combinations-of-a-phone-number.md @@ -1,89 +1,104 @@ --- id: letter-combinations-of-a-phone-number title: Letter Combinations of a Phone Number (LeetCode) -sidebar_label: 0017-Letter Combinations of a Phone Number +sidebar_label: 0017 Letter Combinations of a Phone Number tags: - - String - - Backtracking -description: "Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent." + - Back Tracking + - Mapping + - String +description: The problem requires generating all letter combinations corresponding to given digits (2-9). The solution utilizes backtracking to explore all combinations efficiently, employing a recursive approach in Java. --- ## Problem Description | Problem Statement | Solution Link | LeetCode Profile | | :----------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------- | -| [Letter Combinations of a Phone Number](https://leetcode.com/problems/letter-combinations-of-a-phone-number/) | [Letter Combinations of a Phone Number Solution on LeetCode](https://leetcode.com/problems/letter-combinations-of-a-phone-number/solutions/) | [gabaniyash846](https://leetcode.com/u/gabaniyash846/) | +| [Letter Combinations of a Phone Number](https://leetcode.com/problems/Letter Combinations of a Phone Number/) | [Letter Combinations of a Phone Number Solution on LeetCode](https://leetcode.com/problems/Letter Combinations of a Phone Number/solutions/5055810/video-two-pointer-solution/) | [gabaniyash846](https://leetcode.com/u/gabaniyash846/) | ### Problem Description +## Problem Statement: Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent. Return the answer in any order. -A mapping of digit to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters. - ### Examples #### Example 1 + - **Input:** `digits = "23"` - **Output:** `["ad","ae","af","bd","be","bf","cd","ce","cf"]` #### Example 2 + - **Input:** `digits = ""` - **Output:** `[]` + #### Example 3 -- **Input:** `digits = "2"` + +- **Input:** `2` - **Output:** `["a","b","c"]` -### Constraints -- `0 <= digits.length <= 4` -- `digits[i]` is a digit in the range `['2', '9']`. +### Constraints: +- `0 ≤ digits.length ≤ 4` +- `0 ≤ digits.length ≤ 4digits[𝑖]` +- `digits[i] is a digit in the range ['2', '9'].` +- `A mapping of digits to letters (similar to telephone buttons) is given below. Note that 1 does not map to any letters.` -### Topics -- String -- Backtracking +### Approach -### Intuition -- Use backtracking to generate all possible combinations. +1. **Mapping Digits to Letters:** + - Define a mapping of digits to their corresponding letters, similar to telephone buttons. -### Complexity -- **Time Complexity:** $O(3^N \cdot 4^M)$ where $N$ is the number of digits in the input that maps to 3 letters (2, 3, 4, 5, 6, 8) and $M$ is the number of digits in the input that maps to 4 letters (7, 9). -- **Space Complexity:** $O(3^N \cdot 4^M)$ for storing the results. +2. **Backtracking Function:** + - Define a recursive backtracking function to generate all possible combinations. + - The function takes four parameters: + - `index`: The current index in the digits string. + - `path`: The current combination of letters. + - If the index is equal to the length of the digits string, it means we have reached the end of a combination, so we add it to the result list. + - Otherwise, for each letter corresponding to the current digit, we append it to the current combination and recursively call the function with the next index. + - After the recursive call, we remove the last character from the combination (backtracking). -### Solution Code and Explanation +3. **Base Case:** + - If the length of the current combination is equal to the length of the input digits string, we add the combination to the result list. -#### C++ +4. **Main Function:** + - Initialize an empty list to store the combinations. + - Call the backtracking function with the initial index set to 0 and an empty string as the initial combination. + - Return the list of combinations. -```cpp -#include -#include -#include +This approach ensures that all possible combinations are generated using backtracking, and the result is returned in the desired format. -class Solution { -public: - std::vector letterCombinations(std::string digits) { - if (digits.empty()) return {}; - std::unordered_map phoneMap = { - {'2', "abc"}, {'3', "def"}, {'4', "ghi"}, - {'5', "jkl"}, {'6', "mno"}, {'7', "pqrs"}, - {'8', "tuv"}, {'9', "wxyz"} - }; - std::vector result; - backtrack(digits, phoneMap, 0, "", result); - return result; - } +### Solution Code -private: - void backtrack(const std::string& digits, const std::unordered_map& phoneMap, int index, std::string current, std::vector& result) { - if (index == digits.size()) { - result.push_back(current); - return; - } - const std::string& letters = phoneMap.at(digits[index]); - for (const char& letter : letters) { - backtrack(digits, phoneMap, index + 1, current + letter, result); +#### Python + +```python +class Solution: + def letterCombinations(self, digits: str) -> List[str]: + if not digits: + return [] + + digit_to_letters = { + '2': 'abc', + '3': 'def', + '4': 'ghi', + '5': 'jkl', + '6': 'mno', + '7': 'pqrs', + '8': 'tuv', + '9': 'wxyz' } - } -}; + + def backtrack(index, path): + if index == len(digits): + combinations.append(path) + return + for letter in digit_to_letters[digits[index]]: + backtrack(index + 1, path + letter) + + combinations = [] + backtrack(0, '') + return combinations ``` #### Java @@ -94,100 +109,208 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -class Solution { +public class Solution { + private Map digitToLetters = new HashMap<>(); + + public Solution() { + digitToLetters.put('2', "abc"); + digitToLetters.put('3', "def"); + digitToLetters.put('4', "ghi"); + digitToLetters.put('5', "jkl"); + digitToLetters.put('6', "mno"); + digitToLetters.put('7', "pqrs"); + digitToLetters.put('8', "tuv"); + digitToLetters.put('9', "wxyz"); + } + public List letterCombinations(String digits) { - List result = new ArrayList<>(); - if (digits.isEmpty()) return result; - Map phoneMap = new HashMap<>() {{ - put('2', "abc"); - put('3', "def"); - put('4', "ghi"); - put('5', "jkl"); - put('6', "mno"); - put('7', "pqrs"); - put('8', "tuv"); - put('9', "wxyz"); - }}; - backtrack(digits, phoneMap, 0, new StringBuilder(), result); - return result; + List combinations = new ArrayList<>(); + if (digits == null || digits.isEmpty()) { + return combinations; + } + backtrack(combinations, digits, 0, new StringBuilder()); + return combinations; } - private void backtrack(String digits, Map phoneMap, int index, StringBuilder current, List result) { + private void backtrack(List combinations, String digits, int index, StringBuilder path) { if (index == digits.length()) { - result.add(current.toString()); + combinations.add(path.toString()); return; } - String letters = phoneMap.get(digits.charAt(index)); + String letters = digitToLetters.get(digits.charAt(index)); for (char letter : letters.toCharArray()) { - current.append(letter); - backtrack(digits, phoneMap, index + 1, current, result); - current.deleteCharAt(current.length() - 1); + path.append(letter); + backtrack(combinations, digits, index + 1, path); + path.deleteCharAt(path.length() - 1); } } + + public static void main(String[] args) { + Solution solution = new Solution(); + List result = solution.letterCombinations("23"); + System.out.println(result); // Output: [ad, ae, af, bd, be, bf, cd, ce, cf] + } } ``` -#### Python +#### CPP: +```cpp +#include +#include +#include -```python -from typing import List +using namespace std; -class Solution: - def letterCombinations(self, digits: str) -> List[str]: - if not digits: - return [] - - phone_map = { - "2": "abc", "3": "def", "4": "ghi", "5": "jkl", - "6": "mno", "7": "pqrs", "8": "tuv", "9": "wxyz" +class Solution { +private: + unordered_map digitToLetters; + vector combinations; + +public: + Solution() { + digitToLetters = { + {'2', "abc"}, + {'3', "def"}, + {'4', "ghi"}, + {'5', "jkl"}, + {'6', "mno"}, + {'7', "pqrs"}, + {'8', "tuv"}, + {'9', "wxyz"} + }; + } + + vector letterCombinations(string digits) { + if (digits.empty()) return {}; + backtrack(digits, 0, ""); + return combinations; + } + + void backtrack(const string& digits, int index, string path) { + if (index == digits.length()) { + combinations.push_back(path); + return; + } + for (char letter : digitToLetters[digits[index]]) { + backtrack(digits, index + 1, path + letter); + } + } +}; + +int main() { + Solution solution; + vector result = solution.letterCombinations("23"); + for (const string& comb : result) { + cout << comb << " "; + } + // Output: ad ae af bd be bf cd ce cf + return 0; +} +``` + +#### JavaScript +```js +/** + * @param {string} digits + * @return {string[]} + */ +var letterCombinations = function(digits) { + if (digits.length === 0) return []; + + const digitToLetters = { + '2': 'abc', + '3': 'def', + '4': 'ghi', + '5': 'jkl', + '6': 'mno', + '7': 'pqrs', + '8': 'tuv', + '9': 'wxyz' + }; + + const combinations = []; + + const backtrack = (index, path) => { + if (index === digits.length) { + combinations.push(path); + return; + } + const letters = digitToLetters[digits.charAt(index)]; + for (let letter of letters) { + backtrack(index + 1, path + letter); + } + }; + + backtrack(0, ''); + return combinations; +}; + +// Example usage: +console.log(letterCombinations("23")); // Output: ["ad","ae","af","bd","be","bf","cd","ce","cf"] +``` + +#### TypeScript +```ts +class Solution { + private digitToLetters: { [key: string]: string } = { + '2': 'abc', + '3': 'def', + '4': 'ghi', + '5': 'jkl', + '6': 'mno', + '7': 'pqrs', + '8': 'tuv', + '9': 'wxyz' + }; + + letterCombinations(digits: string): string[] { + const combinations: string[] = []; + + const backtrack = (index: number, path: string): void => { + if (index === digits.length) { + combinations.push(path); + return; + } + const letters = this.digitToLetters[digits.charAt(index)]; + for (let letter of letters) { + backtrack(index + 1, path + letter); + } + }; + + if (digits.length !== 0) { + backtrack(0, ''); } - def backtrack(index: int, path: str): - if index == len(digits): - combinations.append(path) - return - possible_letters = phone_map[digits[index]] - for letter in possible_letters: - backtrack(index + 1, path + letter) - - combinations = [] - backtrack(0, "") - return combinations + return combinations; + } +} + +// Example usage: +const solution = new Solution(); +console.log(solution.letterCombinations("23")); // Output: ["ad","ae","af","bd","be","bf","cd","ce","cf"] ``` -### Explanation +### Step-by-Step Algorithm -1. **Initialize Phone Map:** - Create a mapping from digit to corresponding letters. - ```python - phone_map = { - "2": "abc", "3": "def", "4": "ghi", "5": "jkl", - "6": "mno", "7": "pqrs", "8": "tuv", "9": "wxyz" - } - ``` +Here's a step-by-step algorithm for generating all possible letter combinations of a given string of digits using backtracking: -2. **Backtracking Function:** - Define a recursive function `backtrack` to generate combinations. - - **Base case:** If the current index is equal to the length of digits, add the current path to combinations. - - **Recursive case:** For each letter corresponding to the current digit, append the letter to the path and call `backtrack` with the next index. - ```python - def backtrack(index: int, path: str): - if index == len(digits): - combinations.append(path) - return - possible_letters = phone_map[digits[index]] - for letter in possible_letters: - backtrack(index + 1, path + letter) - ``` - -3. **Initiate Backtracking:** - Initialize the result list `combinations` and start the backtracking process. - ```python - combinations = [] - backtrack(0, "") - return combinations - ``` - -### Conclusion - -The above solution efficiently generates all possible letter combinations for a given string of digits. It employs a backtracking approach to explore all potential combinations, leveraging a recursive function to build the combinations step-by-step. The time complexity of $O(3^N \cdot 4^M)$ and space complexity of $O(3^N \cdot 4^M)$ ensure that the algorithm can handle input sizes up to the upper limit specified in the constraints efficiently. +1. **Define a mapping of digits to letters:** + - Create a map where each digit from 2 to 9 is mapped to its corresponding letters on a telephone keypad. + +2. **Define a backtracking function:** + - The function will take the following parameters: + - `index`: The current index in the digits string. + - `path`: The current combination of letters. + - If the index is equal to the length of the digits string, it means we have formed a complete combination, so add it to the result list. + - Otherwise, for each letter corresponding to the current digit at the given index, append it to the current combination and recursively call the function with the next index. + - After the recursive call, remove the last character from the combination (backtracking). + +3. **Base Case:** + - If the length of the current combination is equal to the length of the input digits string, add the combination to the result list. + +4. **Main Function:** + - Initialize an empty list to store the combinations. + - Call the backtracking function with the initial index set to 0 and an empty string as the initial combination. + - Return the list of combinations. + +This algorithm ensures that all possible combinations are generated by exploring all valid paths through backtracking.