refactor(problem/description): improve clarity and formatting of problem description

This commit is contained in:
cfngc4594 2025-02-25 19:45:20 +08:00
parent 2e33dea853
commit 4975d79d35
2 changed files with 76 additions and 81 deletions

View File

@ -1,12 +1,12 @@
export const DEFAULT_PROBLEM_DESCRIPTION = `
# Two Sum
# 1. Two Sum
Given an array of integers \`nums\` and an integer \`target\`, return *indices* of the **two numbers** such that they add up to \`target\`.
Given an array of integers \`nums\` and an integer \`target\`, return *indices of the two numbers such that they add up to \`target\`*.
You **must not** use the same element twice, and you are guaranteed to have **exactly one solution** for each input.
You may assume that each input would have ***exactly* one solution**, and you may not use the same element twice.
The order of the indices you return does not matter.
You can return the answer in any order.
## Examples
@ -23,7 +23,6 @@ Explanation: Because nums[0] + nums[1] == 9, we return [0, 1].
\`\`\`bash
Input: nums = [3,2,4], target = 6
Output: [1,2]
Explanation: Because nums[1] + nums[2] == 6, we return [1, 2].
\`\`\`
### Example 3
@ -31,19 +30,30 @@ Explanation: Because nums[1] + nums[2] == 6, we return [1, 2].
\`\`\`bash
Input: nums = [3,3], target = 6
Output: [0,1]
Explanation: Because nums[0] + nums[1] == 6, we return [0, 1].
\`\`\`
## Constraints
* \`2 <= nums.length <= 10^4\`
\`\`\`math
2 <= nums.length <= 10^4
\`\`\`
* \`-10^9 <= nums[i] <= 10^9\`
\`\`\`math
-10^9 <= nums[i] <= 10^9
\`\`\`
* \`-10^9 <= target <= 10^9\`
\`\`\`math
-10^9 <= target <= 10^9
\`\`\`
* **It is guaranteed that only one valid answer exists.**
<div align="center">
**Follow-up:** Can you devise an algorithm with a time complexity less than \`O(n^2)\`?
Only one valid answer exists.
</div>
---
**Follow-up:** Can you come up with an algorithm that is less than $O(n^2)$ time complexity?
`;

View File

@ -1,25 +1,16 @@
export const DEFAULT_PROBLEM_SOLUTION = `
# Solution Article: Two Sum Problem
This article explores different approaches to solve the "Two Sum" problem, ranging from a straightforward brute-force method to more efficient hash table based solutions.
# Solution Article
## Approach 1: Brute Force
### Intuition
The most intuitive approach is to check every possible pair of numbers in the input array \`nums\`. For each number, we iterate through the rest of the array to find its complement that sums up to the \`target\`.
### Algorithm
1. Iterate through the \`nums\` array with index \`i\` from 0 to \`numsSize - 1\`.
2. For each element \`nums[i]\`, iterate through the rest of the array with index \`j\` from \`i + 1\` to \`numsSize - 1\`.
3. Check if the sum of \`nums[i] + nums[j]\` equals the \`target\`.
4. If the sum equals the \`target\`, return the indices \`[i, j]\`.
The brute force approach is simple. Loop through each element $x$ and find if there is another value that equals to $target - x$.
### Implementation
\`\`\`c showLineNumbers title="Brute Force Implementation in C" caption="Simple nested loop approach." {2-3} {8,14} /target - nums[i]/
\`\`\`c showLineNumbers title="Brute Force Implementation in C" caption="Simple nested loop approach." {2-4} {6-7} {14} /target - nums[i]/
int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
for (int i = 0; i < numsSize; i++) {
for (int j = i + 1; j < numsSize; j++) {
@ -40,8 +31,13 @@ int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
### Complexity Analysis
- **Time complexity: O(n^2)**. The nested loops result in a quadratic time complexity because, in the worst case, we compare each number with every other number in the array.
- **Space complexity: O(1)**. The algorithm uses a constant amount of extra space, regardless of the input size.
- **Time complexity:** $O(n^2)$.
For each element, we try to find its complement by looping through the rest of the array which takes $O(n)$ time. Therefore, the time complexity is $O(n^2)$.
- **Space complexity:** $O(1)$.
The space required does not depend on the size of the input array, so only constant space is used.
---
@ -49,21 +45,17 @@ int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
### Intuition
To optimize the runtime, we can use a hash table to reduce the lookup time for the complement. Instead of iterating through the rest of the array to find the complement, we can check for its existence in the hash table in near-constant time on average.
To improve our runtime complexity, we need a more efficient way to check if the complement exists in the array. If the complement exists, we need to get its index. What is the best way to maintain a mapping of each element in the array to its index? A hash table.
We can reduce the lookup time from $O(n)$ to $O(1)$ by trading space for speed. A hash table is well suited for this purpose because it supports fast lookup in near constant time. I say "near" because if a collision occurred, a lookup could degenerate to $O(n)$ time. However, lookup in a hash table should be amortized $O(1)$ time as long as the hash function was chosen carefully.
### Algorithm
1. Create an empty hash table.
2. **First pass:** Iterate through the \`nums\` array and insert each element's value as the key and its index as the value into the hash table.
3. **Second pass:** Iterate through the \`nums\` array again. For each element \`nums[i]\`, calculate its complement \`complement = target - nums[i]\`.
4. Check if the \`complement\` exists as a key in the hash table.
5. If the \`complement\` exists and its index in the hash table is not equal to \`i\` (to avoid using the same element twice), return the indices \`[i, index of complement from hash table]\`.
A simple implementation uses two iterations. In the first iteration, we add each element's value as a key and its index as a value to the hash table. Then, in the second iteration, we check if each element's complement ($target - nums[i]$) exists in the hash table. If it does exist, we return current element's index and its complement's index. Beware that the complement must not be $nums[i]$ itself!
### Implementation
\`\`\`c showLineNumbers title="Two-pass Hash Table Implementation in C" caption="Using a hash table for faster lookups." {4-8} {22,27,35} /hashTable/ /HASH_ADD_INT/ /HASH_FIND_INT/ /HASH_DEL/ /HASH_ITER/
#include "uthash.h" // Assuming uthash.h is included for hash table implementation
\`\`\`c showLineNumbers title="Two-pass Hash Table Implementation in C" caption="Using a hash table for faster lookups." {2-6} {9,15-16,24,26-27} /hashTable/ /HASH_ADD_INT/ /HASH_FIND_INT/ /HASH_DEL/ /HASH_ITER/
int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
struct hashTable {
int key;
@ -71,67 +63,55 @@ int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
UT_hash_handle hh;
} *hashTable = NULL, *item, *tmpItem;
// First pass: Populate hash table
for (int i = 0; i < numsSize; i++) {
item = malloc(sizeof(struct hashTable));
item->key = nums[i];
item->value = i;
HASH_ADD_INT(hashTable, key, item);
}
// Second pass: Find complement
for (int i = 0; i < numsSize; i++) {
int complement = target - nums[i];
HASH_FIND_INT(hashTable, &complement, item);
if (item && item->value != i) { // Ensure not using the same element twice (though problem guarantees only one solution, and using same index is avoided by algorithm logic anyway)
HASH_FIND_INT(hashTable, &nums[i], item);
if (item) {
int* result = malloc(sizeof(int) * 2);
result[0] = i;
result[1] = item->value;
result[0] = item->value;
result[1] = i;
*returnSize = 2;
HASH_ITER(hh, hashTable, item, tmpItem) { // Free hash table
HASH_ITER(hh, hashTable, item, tmpItem) {
HASH_DEL(hashTable, item);
free(item);
}
return result;
}
item = malloc(sizeof(struct hashTable));
item->key = target - nums[i];
item->value = i;
HASH_ADD_INT(hashTable, key, item);
}
HASH_ITER(hh, hashTable, item, tmpItem) { // Free hash table if no solution found (though should not happen as per problem constraints)
HASH_ITER(hh, hashTable, item, tmpItem) {
HASH_DEL(hashTable, item);
free(item);
}
*returnSize = 0;
// If no valid pair is found, return an empty array
return malloc(sizeof(int) * 0);
}
\`\`\`
### Complexity Analysis
- **Time complexity: O(n)**. We iterate through the array twice. Hash table operations (insertion and lookup) take O(1) time on average.
- **Space complexity: O(n)**. We use a hash table to store at most \`n\` elements, resulting in linear space complexity.
- **Time complexity:** $O(n)$.
We traverse the list containing $n$ elements exactly twice. Since the hash table reduces the lookup time to $O(1)$, the overall time complexity is $O(n)$.
- **Space complexity:** $O(n)$.
The extra space required depends on the number of items stored in the hash table, which stores exactly $n$ elements.
---
## Approach 3: One-pass Hash Table
### Intuition
We can optimize the two-pass hash table approach into a single pass. While iterating through the array, for each number, we immediately check if its complement already exists in the hash table. If it does, we have found the pair. If not, we add the current number to the hash table for future lookups.
### Algorithm
1. Create an empty hash table.
2. Iterate through the \`nums\` array with index \`i\` from 0 to \`numsSize - 1\`.
3. For each element \`nums[i]\`, calculate its complement \`complement = target - nums[i]\`.
4. Check if the \`complement\` exists as a key in the hash table.
5. If the \`complement\` exists in the hash table, return the indices \`[index of complement from hash table, i]\`.
6. If the \`complement\` does not exist, add the current element \`nums[i]\` as the key and its index \`i\` as the value to the hash table.
It turns out we can do it in one-pass. While we are iterating and inserting elements into the hash table, we also look back to check if current element's complement already exists in the hash table. If it exists, we have found a solution and return the indices immediately.
### Implementation
\`\`\`c showLineNumbers title="One-pass Hash Table Implementation in C" caption="Efficient single-pass solution." {4-8} {15-16,18,28-29} /hashTable/ /HASH_ADD_INT/ /HASH_FIND_INT/ /HASH_CLEAR/
#include "uthash.h" // Assuming uthash.h is included for hash table implementation
\`\`\`c showLineNumbers title="One-pass Hash Table Implementation in C" caption="Efficient single-pass solution." {2-6} {10,16,22,25} /hashTable/ /HASH_ADD_INT/ /HASH_FIND_INT/ /HASH_CLEAR/
int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
struct hashTable {
int key;
@ -144,28 +124,33 @@ int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
HASH_FIND_INT(hashTable, &complement, item);
if (item) {
int* result = malloc(sizeof(int) * 2);
result[0] = item->value; // Index of complement (which was added earlier)
result[1] = i; // Current index
result[0] = item->value;
result[1] = i;
*returnSize = 2;
HASH_CLEAR(hh, hashTable); // Free hash table
HASH_CLEAR(hh, hashTable); // Free the hash table
return result;
} else {
item = malloc(sizeof(struct hashTable));
item->key = nums[i];
item->value = i;
HASH_ADD_INT(hashTable, key, item);
}
item = malloc(sizeof(struct hashTable));
item->key = nums[i];
item->value = i;
HASH_ADD_INT(hashTable, key, item);
}
*returnSize = 0;
HASH_CLEAR(hh, hashTable); // Free hash table if no solution found (though should not happen)
return malloc(0); // Allocate 0 bytes - indicates no solution
HASH_CLEAR(hh, hashTable); // Free the hash table
// Return an empty array if no solution is found
return malloc(0); // Allocate 0 bytes
}
\`\`\`
### Complexity Analysis
- **Time complexity: O(n)**. We iterate through the array only once. Hash table operations are O(1) on average.
- **Space complexity: O(n)**. Similar to the two-pass approach, we use a hash table that can store up to \`n\` elements.
- **Time complexity:** $O(n)$.
We traverse the list containing $n$ elements only once. Each lookup in the table costs only $O(1)$ time.
- **Space complexity:** $O(n)$.
The extra space required depends on the number of items stored in the hash table, which stores at most $n$ elements.
---
@ -173,8 +158,8 @@ int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
| Approach | Time Complexity | Space Complexity |
| ------------------- | --------------- | ---------------- |
| Brute Force | O(n^2) | O(1) |
| Two-pass Hash Table | O(n) | O(n) |
| One-pass Hash Table | O(n) | O(n) |
| Brute Force | $O(n^2)$ | $O(1)$ |
| Two-pass Hash Table | $O(n)$ | $O(n)$ |
| One-pass Hash Table | $O(n)$ | $O(n)$ |
`;