One-Dimensional Arrays: Problem Solving
Array problems become easier when each requirement is converted into a repeatable traversal pattern. Some problems need an accumulated result, some maintain the best value found so far, some compare neighboring elements, and others use two indexes moving from opposite ends. These patterns form the foundation for solving more advanced array problems.
Problem 1: Find the Sum and Average
Given an integer array, the first task is to calculate the sum of all its elements. The average is then calculated by dividing that sum by the number of elements.
For the array {10, 20, 30, 40}:
The sum is
10 + 20 + 30 + 40 = 100.The number of elements is
4.The average is
100 / 4 = 25.
Using an Accumulator
An accumulator is a variable that stores a result while the array is being traversed. For summation, it begins at 0 because adding zero does not affect the result. Each array element is then added to the current sum.
Initially,
sum = 0.After processing
10,sum = 10.After processing
20,sum = 30.After processing
30,sum = 60.After processing
40,sum = 100.
The statement sum += arr[i] is a shorter form of sum = sum + arr[i]. Once every element has been processed, the accumulator contains the required total.
Handling Integer Division
The average can contain a fractional part, so its return type should be double. Dividing one integer by another performs integer division before the result is assigned. For example, integer division evaluates 5 / 2 as 2, not 2.5.
At least one operand must therefore be converted to double before division. The expression static_cast<double>(sum) / n preserves the fractional part.
#include <iostream>using namespace std;int sumOfArray(int arr[], int n) { int sum = 0; for (int i = 0; i < n; i++) { sum += arr[i]; } return sum;}double averageOfArray(int arr[], int n) { int sum = sumOfArray(arr, n); return static_cast(sum) / n;}int main() { int arr[] = {10, 20, 30, 40}; int n = sizeof(arr) / sizeof(arr[0]); cout << "Sum: " << sumOfArray(arr, n) << '\n'; cout << "Average: " << averageOfArray(arr, n) << '\n'; return 0;}The sum function performs one traversal, so its time complexity is O(n) and its auxiliary space complexity is O(1). The average function reuses sumOfArray instead of repeating the traversal logic. It assumes that n is greater than zero because an empty array has no arithmetic mean.
Problem 2: Find the Maximum and Minimum
Given a non-empty array, the task is to return its largest and smallest elements. For {4, 9, 2, 7}, the maximum is 9 and the minimum is 2.
The Current Champion Method
The first element can be treated as the current best answer because the array is guaranteed to be non-empty. The remaining elements are then compared with this current champion.
To find the maximum in {4, 9, 2, 7}:
Start with
maximum = 4.9is greater than4, so the maximum becomes9.2is not greater than9, so no update is needed.7is not greater than9, so the final maximum is9.
The minimum follows the same pattern with the comparison reversed:
Start with
minimum = 4.9is not smaller than4.2is smaller than4, so the minimum becomes2.7is not smaller than2, so the final minimum is2.
Traversal begins at index 1 because index 0 has already been used to initialize the answer. Initializing with the first element is more reliable than starting with an arbitrary value such as 0. If every array element were negative, using zero as the initial maximum would produce an incorrect result.
#include <iostream>using namespace std;int maximumElement(int arr[], int n) { int maximum = arr[0]; for (int i = 1; i < n; i++) { if (arr[i] > maximum) { maximum = arr[i]; } } return maximum;}int minimumElement(int arr[], int n) { int minimum = arr[0]; for (int i = 1; i < n; i++) { if (arr[i] < minimum) { minimum = arr[i]; } } return minimum;}int main() { int arr[] = {4, 9, 2, 7}; int n = sizeof(arr) / sizeof(arr[0]); cout << "Maximum: " << maximumElement(arr, n) << '\n'; cout << "Minimum: " << minimumElement(arr, n) << '\n'; return 0;}Each function examines every relevant element once. The time complexity is O(n), while the auxiliary space complexity is O(1). A single-element array also works: its only value is both the maximum and the minimum, and the loop executes zero times.
Problem 3: Check Whether an Array Is Sorted
An array is sorted in non-decreasing order when every element is greater than or equal to the element immediately before it. Equal neighboring values are allowed, which is why the term is non-decreasing rather than strictly increasing.
{1, 2, 2, 5, 8}is sorted because it never decreases while moving from left to right.{1, 4, 3, 7}is not sorted because3is smaller than the preceding value4.
Comparing Adjacent Elements
The element at index 0 has no element to its left, so comparison begins at index 1. At every index i, compare arr[i] with arr[i - 1].
For {1, 2, 2, 5, 8}, the comparisons are:
2 >= 12 >= 25 >= 28 >= 5
Every comparison is valid, so the function returns true. The equality in 2 >= 2 is valid and must not be treated as a violation.
For {1, 4, 3, 7}, 4 >= 1 is valid, but 3 >= 4 is false. Equivalently, arr[i] < arr[i - 1] identifies a decrease. Once one violation is found, the complete array cannot be sorted, so the function can return false immediately.
#include <iostream>using namespace std;bool isArraySorted(int arr[], int n) { for (int i = 1; i < n; i++) { if (arr[i] < arr[i - 1]) { return false; } } return true;}int main() { int first[] = {1, 2, 2, 5, 8}; int second[] = {1, 4, 3, 7}; cout << isArraySorted(first, 5) << '\n'; cout << isArraySorted(second, 4) << '\n'; return 0;}By default, C++ prints true as 1 and false as 0, so this program prints 1 followed by 0. If no violating pair is found, the loop finishes and the final return true; executes.
The worst-case time complexity is O(n), and the auxiliary space complexity is O(1). Because of the early return, an unsorted array may require fewer comparisons. For example, if the first adjacent pair is decreasing, the function finishes after a single comparison.
Problem 4: Reverse an Array
Reversing an array means placing its elements in the opposite order. The array {10, 20, 30, 40, 50} becomes {50, 40, 30, 20, 10}.
The requirement to reverse the array in place is important. It means that the original array must be updated without creating another array proportional to the input size. A useful problem-solving process is to begin with the most direct method and then improve it to satisfy this constraint.
Brute-Force Approach Using a Duplicate Array
The direct approach creates an auxiliary array of the same size. Traverse the original array from its last index to its first index and place each value into the auxiliary array from left to right.
For {10, 20, 30, 40, 50}:
Read
50from index4and place it at duplicate index0.Read
40from index3and place it at duplicate index1.Continue until
10is placed at duplicate index4.Copy the completed duplicate array back into the original array.
#include <iostream>using namespace std;void reverseBruteForce(int arr[], int n) { int duplicate[n]; int j = 0; for (int i = n - 1; i >= 0; i--) { duplicate[j] = arr[i]; j++; } for (int i = 0; i < n; i++) { arr[i] = duplicate[i]; }}int main() { int arr[] = {10, 20, 30, 40, 50}; int n = sizeof(arr) / sizeof(arr[0]); reverseBruteForce(arr, n); for (int i = 0; i < n; i++) { cout << arr[i] << ' '; } return 0;}This method uses two traversals: one to build the reversed copy and another to copy it back. Its total time complexity is still O(n) because O(n) + O(n) simplifies to O(n). However, its auxiliary space complexity is O(n), so it does not satisfy the in-place requirement.
Optimal In-Place Approach Using Two Pointers
Observe how positions change during reversal. The first and last elements exchange places, then the second and second-last elements exchange places, and the pattern continues toward the center. This leads to the two-pointer technique.
Set
left = 0to point to the first element.Set
right = n - 1to point to the last element.Swap
arr[left]andarr[right].Move
leftone position forward andrightone position backward.Continue while
left < right.
Dry Run for an Odd Number of Elements
Consider {10, 20, 30, 40, 50}:
Swap
10and50to get{50, 20, 30, 40, 10}.Move inward and swap
20and40to get{50, 40, 30, 20, 10}.Both pointers now meet at
30. The middle element is already in its correct position, so no further swap is needed.
Dry Run for an Even Number of Elements
Consider {1, 2, 3, 4, 5, 6}:
Swap
1and6to get{6, 2, 3, 4, 5, 1}.Swap
2and5to get{6, 5, 3, 4, 2, 1}.Swap
3and4to get{6, 5, 4, 3, 2, 1}.The pointers then cross, so reversal is complete.
For an odd-length array, the pointers eventually meet. For an even-length array, they cross. Both cases are handled by continuing only while left < right. Equivalently, the process stops when left >= right.
#include <iostream>#include <utility>using namespace std;void reverseInPlace(int arr[], int n) { int left = 0; int right = n - 1; while (left < right) { swap(arr[left], arr[right]); left++; right--; }}int main() { int arr[] = {10, 20, 30, 40, 50}; int n = sizeof(arr) / sizeof(arr[0]); reverseInPlace(arr, n); for (int i = 0; i < n; i++) { cout << arr[i] << ' '; } return 0;}The loop performs approximately n / 2 swaps. Its time complexity is O(n), and it uses only the two pointer variables, giving it O(1) auxiliary space. The original array is modified directly, so the function does not need to return it.
The pointer updates can also be placed inside the swap expression as swap(arr[left++], arr[right--]). Postfix increment and decrement use the current indexes for the swap and update them afterward. Keeping the updates on separate lines is usually clearer and makes the movement of both pointers explicit.
Comparing the Problem-Solving Patterns
Accumulation: Start with a neutral value and combine every element into it, as in array summation.
Current champion: Keep the best candidate found so far and replace it whenever a better value appears, as in minimum and maximum problems.
Adjacent comparison: Compare each element with its predecessor to validate an ordering property.
Early termination: Return immediately when a condition has already determined the answer, as in detecting an unsorted pair.
Brute force followed by optimization: Begin with a correct direct solution, identify the unnecessary resource, and remove it.
Two pointers: Place indexes at opposite ends and move them inward when the problem involves paired positions.
Summary
These problems demonstrate that array problem solving is largely about recognizing how information should move during traversal. Sum and average use an accumulator, minimum and maximum maintain a current champion, sorted-order checking compares neighboring elements with an early return, and reversal pairs elements from opposite ends. Once these patterns become familiar, many array questions can be reduced to a small and precise loop with clear boundaries.
Be the first to add a comment.