Find All Good Indices In The Given Array In C++ - C++ Programming Tutorial
C++ Course / Arrays / Find All Good Indices In The Given Array In C++

Find All Good Indices In The Given Array In C++

BLUF: Mastering Find All Good Indices In The Given Array In C++ is a critical step in becoming a proficient C++ developer. This lesson provides a deep dive into the syntax, performance considerations, and real-world applications of this concept.
Key Performance Insight: Find All Good Indices In The Given Array In C++

C++ is renowned for its efficiency. Learn how Find All Good Indices In The Given Array In C++ enables low-level control and high-performance computing in the tutorial below.

Overview:

In the realm of troubleshooting and coding, efficiently exploring an array's attributes for specific indices is a common challenge. Locating optimal indices within an array poses a similar challenge. An ideal index typically meets specific criteria such as maintaining non-decreasing or non-increasing subarrays of a defined length in its vicinity. This tutorial will demonstrate the process of identifying all favorable indices within a provided array using C++ programming language.

Problem Statement:

For an array arr of length n and an integer k, an index i is good if:

  • The k elements up to index i constitute a non-increasing subarray.
  • The k elements at or after index i is a non-decreasing subarray.
  • The index i must be valid, i.e., it must have at least k elements on both sides of it.

The task is to identify and retrieve all such valid indexes within the array.

Approach: Two-Pass Approach

In order to solve this problem efficiently, we use a two-pass approach:

  • Precompute non-increasing counts: Create an array left where left[i] stores the number of consecutive elements before i that are non-increasing.
  • Precompute non-decreasing counts: Create an array right where right[i] stores the number of consecutive elements after i that are non-decreasing.
  • Find valid indices: Traverse the array and check if both left[i] >= k and right[i] >= k. If both conditions are satisfied, i is a good index.

This method guarantees that we only have to iterate through the array a limited number of times, leading to an effective solution with a time complexity of O(n).

Example:

Let's consider an example to locate all valid indices within the provided array in C++.

Example

#include <iostream>
#include <vector>
using namespace std;
vector<int> goodIndices(vector<int>& nums, int k) 
{
    int n = nums.size();
    vector<int> left(n, 0), right(n, 0), result;    
    // Compute non-increasing sequence lengths
    for (int i = 1; i < n; i++) 
{
        if (nums[i] <= nums[i - 1]) 
{
            left[i] = left[i - 1] + 1;
        }
    }    
    // Compute non-decreasing sequence lengths
    for (int i = n - 2; i >= 0; i--) 
{
        if (nums[i] <= nums[i + 1]) 
{
            right[i] = right[i + 1] + 1;
        }
    }    
    // Find good indices
    for (int i = k; i < n - k; i++) 
{
        if (left[i - 1] >= k - 1 && right[i + 1] >= k - 1) 
{
            result.push_back(i);
        }
    }  
    return result;
}
int main() 
{
    vector<int> nums = {2, 1, 1, 1, 3, 4, 1, 2, 5, 6};
    int k = 2;
    vector<int> indices = goodIndices(nums, k);
    cout << "Good Indices: ";
    for (int idx : indices) 
{
        cout << idx << " ";
    }
    return 0;
}

Output:

Output

Good Indices: 2 3 7

Explanation of Code:

  • Initialization: The left keeps track of the length of the previous consecutive non-increasing elements. The right keeps track of the length of the next consecutive non-decreasing elements. The result keeps track of the good indices.
  • Populating the left array: If nums[i] <= nums[i-1], we increase left[i].
  • Populating the right array: If nums[i] <= nums[i+1], we increase right[i].
  • Valid index checking: If left[i-1] >= k-1 and right[i+1] >= k-1, index i is a good index and added to the result.
  • The left keeps track of the length of the previous consecutive non-increasing elements.
  • The right keeps track of the length of the next consecutive non-decreasing elements.
  • The result keeps track of the good indices.
  • If nums[i] <= nums[i-1], we increase left[i].
  • If nums[i] <= nums[i+1], we increase right[i].
  • If left[i-1] >= k-1 and right[i+1] >= k-1, index i is a good index and added to the result.
  • Complexity Analysis:

  • Building left and right: O(n)
  • Checking valid indices: O(n)
  • Overall complexity: O(n)

It enhances the efficiency and viability for processing extensive inputs.

Example Walkthrough:

Consider nums = {2, 1, 1, 1, 3, 4, 1, 2, 5, 6} with k = 2:

  • Compute left: left = {0, 1, 2, 3, 0, 0, 1, 0, 0, 0}
  • Compute right: right = {0, 0, 0, 0, 1, 2, 0, 1, 2, 3}
  • Find good indices: Check if left[i-1] >= 1 and right[i+1] >= 1 for valid indices. The result is: {3, 4, 5}.
  • Check if left[i-1] >= 1 and right[i+1] >= 1 for valid indices.
  • The result is: {3, 4, 5}.
  • Edge Cases Considered:

  • Small array size (n < 2k): No valid indices exist, so return an empty list.
  • Already sorted increasing/decreasing array: The middle elements can be good indices.
  • All elements are the same: Every index (except boundary conditions) can be good.
  • No valid indices: If no index satisfies the condition, return an empty list.
  • No valid indices exist, so return an empty list.
  • The middle elements can be good indices.
  • Every index (except boundary conditions) can be good.
  • If no index satisfies the condition, return an empty list.
  • Alternative Approach (Sliding Window):

Instead of computing left and right beforehand, we can opt for a sliding window approach to dynamically verify the non-increasing and non-decreasing characteristics. This method maintains an O(n) time complexity but could pose challenges in achieving an elegant implementation.

Conclusion:

In summary, effective indexes in an array represent a common challenge addressed efficiently by precalculated arrays. The subsequent O(n) resolution delivers peak efficiency while retaining simplicity. This serves as a prime illustration of utilizing prefix calculations and dynamic criteria to enhance array-centric algorithms.

By employing the dual-pass technique, we can efficiently identify favorable indexes within an array, which can then be utilized to address similar challenges in competitive programming and technical interview scenarios.

Input Required

This code uses input(). Please provide values below:

Logic Practice
Install Logic Practice
Add to home screen for a faster app-like experience