Given an array arr[] of n distinct integers (not necessarily sorted), consider the following search for a target:
- Maintain a range [lo, hi], initially [0, n-1].
- At each step, pick any index p in [lo, hi] (not necessarily the middle) as pivot. If arr[p] == target, stop - found.
- If target < arr[p], set hi = p - 1; if target > arr[p], set lo = p + 1. If lo > hi, stop - not found.
Count the number of indices i such that searching for target = arr[i] succeeds no matter which pivot is chosen at every step (i.e., for every possible sequence of pivot choices).
Examples:
Input: arr[] = [3, 7, 9, 8, 15]
Output: 3
Explanation: 3, 7, and 15 (indices 0, 1, 4) are always found: every possible pivot comparison happens to steer the search toward them, no matter the order. 9 and 8 are not: e.g. if the first pivot picked is 8, searching for 9 gets pushed right and index 2 is lost forever; if the first pivot is 9, searching for 8 gets pushed left and index 3 is lost forever.Input: arr[] = [9, 4, 1, 10, 22, 23, 20]
Output: 1
Explanation: Only 10 (index 3) is always found — every pivot, left or right, happens to steer toward it.
Everything else can fail on some pivot order: e.g. picking 4 first pushes a search for 9 away permanently, and picking 20 first makes a search for 22 run off the end of the array entirely.
Table of Content
[Naive Approach] Brute Force Approach - O(n^2) Time and O(1) Space
The idea is to observe the key condition i.e. for arr[i] to be found, every element on its left must be smaller than arr[i] and every element on its right must be greater than arr[i]. So, for every index i, we explicitly check both sides.
- Initialize count = 0.
- For every index i, consider arr[i] as the target.
- Check all indices j < i; if arr[j] > arr[i], mark it invalid.
- Check all indices j > i; if arr[j] < arr[i], mark it invalid.
- If arr[i] is valid, increment count.
- Return count.
#include <bits/stdc++.h>
using namespace std;
int countAlwaysFound(vector<int> &arr)
{
int n = arr.size();
// Stores the count of elements that are always found.
int count = 0;
// Consider every element as the target.
for (int i = 0; i < n; i++)
{
bool valid = true;
// Check all elements on the left.
// Every left element must be smaller than arr[i].
for (int j = 0; j < i; j++)
{
if (arr[j] > arr[i])
{
valid = false;
break;
}
}
// Check all elements on the right.
// Every right element must be greater than arr[i].
if (valid)
{
for (int j = i + 1; j < n; j++)
{
if (arr[j] < arr[i])
{
valid = false;
break;
}
}
}
// If both conditions are satisfied,
// arr[i] is always found.
if (valid)
count++;
}
return count;
}
int main()
{
vector<int> arr = {3, 7, 9, 8, 15};
int result = countAlwaysFound(arr);
cout << result << endl;
return 0;
}
class GFG {
static int countAlwaysFound(int[] arr)
{
int n = arr.length;
// Stores the count of elements that are always
// found.
int count = 0;
// Consider every element as the target.
for (int i = 0; i < n; i++) {
boolean valid = true;
// Check all elements on the left.
// Every left element must be smaller than
// arr[i].
for (int j = 0; j < i; j++) {
if (arr[j] > arr[i]) {
valid = false;
break;
}
}
// Check all elements on the right.
// Every right element must be greater than
// arr[i].
if (valid) {
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[i]) {
valid = false;
break;
}
}
}
// If both conditions are satisfied,
// arr[i] is always found.
if (valid)
count++;
}
return count;
}
public static void main(String[] args)
{
int[] arr = { 3, 7, 9, 8, 15 };
int result = countAlwaysFound(arr);
System.out.println(result);
}
}
def countAlwaysFound(arr):
n = len(arr)
# Stores the count of elements that are always found.
count = 0
# Consider every element as the target.
for i in range(n):
valid = True
# Check all elements on the left.
# Every left element must be smaller than arr[i].
for j in range(i):
if arr[j] > arr[i]:
valid = False
break
# Check all elements on the right.
# Every right element must be greater than arr[i].
if valid:
for j in range(i + 1, n):
if arr[j] < arr[i]:
valid = False
break
# If both conditions are satisfied,
# arr[i] is always found.
if valid:
count += 1
return count
# Driver Code
if __name__ == "__main__":
arr = [3, 7, 9, 8, 15]
result = countAlwaysFound(arr)
print(result)
using System;
class GFG {
static int countAlwaysFound(int[] arr)
{
int n = arr.Length;
// Stores the count of elements that are always
// found.
int count = 0;
// Consider every element as the target.
for (int i = 0; i < n; i++) {
bool valid = true;
// Check all elements on the left.
// Every left element must be smaller than
// arr[i].
for (int j = 0; j < i; j++) {
if (arr[j] > arr[i]) {
valid = false;
break;
}
}
// Check all elements on the right.
// Every right element must be greater than
// arr[i].
if (valid) {
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[i]) {
valid = false;
break;
}
}
}
// If both conditions are satisfied,
// arr[i] is always found.
if (valid)
count++;
}
return count;
}
static void Main()
{
int[] arr = { 3, 7, 9, 8, 15 };
int result = countAlwaysFound(arr);
Console.WriteLine(result);
}
}
function countAlwaysFound(arr)
{
const n = arr.length;
// Stores the count of elements that are always found.
let count = 0;
// Consider every element as the target.
for (let i = 0; i < n; i++) {
let valid = true;
// Check all elements on the left.
// Every left element must be smaller than arr[i].
for (let j = 0; j < i; j++) {
if (arr[j] > arr[i]) {
valid = false;
break;
}
}
// Check all elements on the right.
// Every right element must be greater than arr[i].
if (valid) {
for (let j = i + 1; j < n; j++) {
if (arr[j] < arr[i]) {
valid = false;
break;
}
}
}
// If both conditions are satisfied,
// arr[i] is always found.
if (valid)
count++;
}
return count;
}
// Driver Code
const arr = [ 3, 7, 9, 8, 15 ];
const result = countAlwaysFound(arr);
console.log(result);
Output
3
[Expected Approach] Using Prefix Maximum and Suffix Minimum - O(n) Time and O(n) Space
The idea is same as above that is arr[i] is always found only if all elements before it are smaller and all elements after it are larger.
To check this efficiently, we precompute the maximum value up to each index using prefMax[] and the minimum value from each index onward using sufMin[].An element arr[i] is always found only when it is both the prefix maximum(everything to its left is smaller) and suffix minimum(everything to its right is larger), so we count elements satisfying arr[i] == prefMax[i] && arr[i] == sufMin[i].
- Create prefMax[] to store the maximum value seen from the left.
- Create sufMin[] to store the minimum value seen from the right.
- For every index i, check whether arr[i] == prefMax[i].
- Also check whether arr[i] == sufMin[i].
- If both conditions hold, count arr[i].
- Return the final count.
#include <bits/stdc++.h>
using namespace std;
int countAlwaysFound(vector<int> &arr)
{
int n = arr.size();
if (n == 0)
return 0;
// Stores the maximum element from index 0 to i.
vector<int> prefMax(n);
// Stores the minimum element from index i to n - 1.
vector<int> sufMin(n);
// Build the prefix maximum array.
prefMax[0] = arr[0];
for (int i = 1; i < n; i++)
prefMax[i] = max(prefMax[i - 1], arr[i]);
// Build the suffix minimum array.
sufMin[n - 1] = arr[n - 1];
for (int i = n - 2; i >= 0; i--)
sufMin[i] = min(sufMin[i + 1], arr[i]);
// Stores the count of elements that are always found.
int count = 0;
// Check every element.
for (int i = 0; i < n; i++)
{
// arr[i] must be the maximum of its prefix
// and the minimum of its suffix.
if (arr[i] == prefMax[i] && arr[i] == sufMin[i])
count++;
}
return count;
}
int main()
{
vector<int> arr = {3, 7, 9, 8, 15};
int result = countAlwaysFound(arr);
cout << result << endl;
return 0;
}
class GFG {
static int countAlwaysFound(int[] arr)
{
int n = arr.length;
if (n == 0)
return 0;
// Stores the maximum element from index 0 to i.
int[] prefMax = new int[n];
// Stores the minimum element from index i to n - 1.
int[] sufMin = new int[n];
// Build the prefix maximum array.
prefMax[0] = arr[0];
for (int i = 1; i < n; i++)
prefMax[i] = Math.max(prefMax[i - 1], arr[i]);
// Build the suffix minimum array.
sufMin[n - 1] = arr[n - 1];
for (int i = n - 2; i >= 0; i--)
sufMin[i] = Math.min(sufMin[i + 1], arr[i]);
// Stores the count of elements that are always
// found.
int count = 0;
// Check every element.
for (int i = 0; i < n; i++) {
// arr[i] must be the maximum of its prefix
// and the minimum of its suffix.
if (arr[i] == prefMax[i] && arr[i] == sufMin[i])
count++;
}
return count;
}
public static void main(String[] args)
{
int[] arr = { 3, 7, 9, 8, 15 };
int result = countAlwaysFound(arr);
System.out.println(result);
}
}
def countAlwaysFound(arr):
n = len(arr)
if n == 0:
return 0
# Stores the maximum element from index 0 to i.
prefMax = [0] * n
# Stores the minimum element from index i to n - 1.
sufMin = [0] * n
# Build the prefix maximum array.
prefMax[0] = arr[0]
for i in range(1, n):
prefMax[i] = max(prefMax[i - 1], arr[i])
# Build the suffix minimum array.
sufMin[n - 1] = arr[n - 1]
for i in range(n - 2, -1, -1):
sufMin[i] = min(sufMin[i + 1], arr[i])
# Stores the count of elements that are always found.
count = 0
# Check every element.
for i in range(n):
# arr[i] must be the maximum of its prefix
# and the minimum of its suffix.
if arr[i] == prefMax[i] and arr[i] == sufMin[i]:
count += 1
return count
# Driver Code
if __name__ == "__main__":
arr = [3, 7, 9, 8, 15]
result = countAlwaysFound(arr)
print(result)
using System;
class GFG {
static int countAlwaysFound(int[] arr)
{
int n = arr.Length;
if (n == 0)
return 0;
// Stores the maximum element from index 0 to i.
int[] prefMax = new int[n];
// Stores the minimum element from index i to n - 1.
int[] sufMin = new int[n];
// Build the prefix maximum array.
prefMax[0] = arr[0];
for (int i = 1; i < n; i++)
prefMax[i] = Math.Max(prefMax[i - 1], arr[i]);
// Build the suffix minimum array.
sufMin[n - 1] = arr[n - 1];
for (int i = n - 2; i >= 0; i--)
sufMin[i] = Math.Min(sufMin[i + 1], arr[i]);
// Stores the count of elements that are always
// found.
int count = 0;
// Check every element.
for (int i = 0; i < n; i++) {
// arr[i] must be the maximum of its prefix
// and the minimum of its suffix.
if (arr[i] == prefMax[i] && arr[i] == sufMin[i])
count++;
}
return count;
}
static void Main()
{
int[] arr = { 3, 7, 9, 8, 15 };
int result = countAlwaysFound(arr);
Console.WriteLine(result);
}
}
function countAlwaysFound(arr)
{
const n = arr.length;
if (n === 0)
return 0;
// Stores the maximum element from index 0 to i.
const prefMax = new Array(n);
// Stores the minimum element from index i to n - 1.
const sufMin = new Array(n);
// Build the prefix maximum array.
prefMax[0] = arr[0];
for (let i = 1; i < n; i++)
prefMax[i] = Math.max(prefMax[i - 1], arr[i]);
// Build the suffix minimum array.
sufMin[n - 1] = arr[n - 1];
for (let i = n - 2; i >= 0; i--)
sufMin[i] = Math.min(sufMin[i + 1], arr[i]);
// Stores the count of elements that are always found.
let count = 0;
// Check every element.
for (let i = 0; i < n; i++) {
// arr[i] must be the maximum of its prefix
// and the minimum of its suffix.
if (arr[i] === prefMax[i] && arr[i] === sufMin[i])
count++;
}
return count;
}
// Driver Code
const arr = [ 3, 7, 9, 8, 15 ];
const result = countAlwaysFound(arr);
console.log(result);
Output
3
[Optimal Approach] Using Prefix Maximum - O(n) Time and O(n) Space
The idea is same as the above approach and it is space optimized version of the above approach.
Here, we first build a leftMax[] array to store the maximum value up to each index. Then, while traversing from right to left, we maintain a single rightMin variable for the minimum value seen on the right.
An element is always found when it is both the prefix maximum and suffix minimum, i.e., arr[i] == leftMax[i] && arr[i] == rightMin.
- If the array is empty, return 0.
- Create leftMax[], where leftMax[i] stores the maximum element from index 0 to i.
- Initialize rightMin with the last element and count = 0.
- Count the last element if it is equal to leftMax[n-1].
- Traverse from right to left. If arr[i] == leftMax[i] and arr[i] < rightMin, increment count.
- Update rightMin = min(rightMin, arr[i]) after checking each element.
- Return count.
Consider the following example for better understanding:
#include <bits/stdc++.h>
using namespace std;
int countAlwaysFound(vector<int> &arr)
{
int n = arr.size();
if (n == 0)
return 0;
// Stores the maximum element from index 0 to i.
vector<int> leftMax(n);
// Initialize the first prefix maximum.
leftMax[0] = arr[0];
// Build the prefix maximum array.
for (int i = 1; i < n; i++)
leftMax[i] = max(leftMax[i - 1], arr[i]);
// Stores the minimum element seen on the right.
int rightMin = arr[n - 1];
int count = 0;
// The last element is always a candidate because
// there are no elements on its right.
if (arr[n - 1] == leftMax[n - 1])
count++;
// Check the remaining elements from right to left.
for (int i = n - 2; i >= 0; i--)
{
// arr[i] must be the maximum of its prefix
// and smaller than every element on its right.
if (arr[i] == leftMax[i] && arr[i] < rightMin)
count++;
// Update the minimum element seen on the right.
rightMin = min(rightMin, arr[i]);
}
return count;
}
int main()
{
vector<int> arr = {3, 7, 9, 8, 15};
int result = countAlwaysFound(arr);
cout << result << endl;
return 0;
}
class GFG {
static int countAlwaysFound(int[] arr)
{
int n = arr.length;
if (n == 0)
return 0;
// Stores the maximum element from index 0 to i.
int[] leftMax = new int[n];
// Initialize the first prefix maximum.
leftMax[0] = arr[0];
// Build the prefix maximum array.
for (int i = 1; i < n; i++)
leftMax[i] = Math.max(leftMax[i - 1], arr[i]);
// Stores the minimum element seen on the right.
int rightMin = arr[n - 1];
int count = 0;
// The last element is always a candidate because
// there are no elements on its right.
if (arr[n - 1] == leftMax[n - 1])
count++;
// Check the remaining elements from right to left.
for (int i = n - 2; i >= 0; i--) {
// arr[i] must be the maximum of its prefix
// and smaller than every element on its right.
if (arr[i] == leftMax[i] && arr[i] < rightMin)
count++;
// Update the minimum element seen on the right.
rightMin = Math.min(rightMin, arr[i]);
}
return count;
}
public static void main(String[] args)
{
int[] arr = { 3, 7, 9, 8, 15 };
int result = countAlwaysFound(arr);
System.out.println(result);
}
}
def countAlwaysFound(arr):
n = len(arr)
if n == 0:
return 0
# Stores the maximum element from index 0 to i.
leftMax = [0] * n
# Initialize the first prefix maximum.
leftMax[0] = arr[0]
# Build the prefix maximum array.
for i in range(1, n):
leftMax[i] = max(leftMax[i - 1], arr[i])
# Stores the minimum element seen on the right.
rightMin = arr[n - 1]
count = 0
# The last element is always a candidate because
# there are no elements on its right.
if arr[n - 1] == leftMax[n - 1]:
count += 1
# Check the remaining elements from right to left.
for i in range(n - 2, -1, -1):
# arr[i] must be the maximum of its prefix
# and smaller than every element on its right.
if arr[i] == leftMax[i] and arr[i] < rightMin:
count += 1
# Update the minimum element seen on the right.
rightMin = min(rightMin, arr[i])
return count
# Driver Code
if __name__ == "__main__":
arr = [3, 7, 9, 8, 15]
result = countAlwaysFound(arr)
print(result)
using System;
class GFG {
static int countAlwaysFound(int[] arr)
{
int n = arr.Length;
if (n == 0)
return 0;
// Stores the maximum element from index 0 to i.
int[] leftMax = new int[n];
// Initialize the first prefix maximum.
leftMax[0] = arr[0];
// Build the prefix maximum array.
for (int i = 1; i < n; i++)
leftMax[i] = Math.Max(leftMax[i - 1], arr[i]);
// Stores the minimum element seen on the right.
int rightMin = arr[n - 1];
int count = 0;
// The last element is always a candidate because
// there are no elements on its right.
if (arr[n - 1] == leftMax[n - 1])
count++;
// Check the remaining elements from right to left.
for (int i = n - 2; i >= 0; i--) {
// arr[i] must be the maximum of its prefix
// and smaller than every element on its right.
if (arr[i] == leftMax[i] && arr[i] < rightMin)
count++;
// Update the minimum element seen on the right.
rightMin = Math.Min(rightMin, arr[i]);
}
return count;
}
static void Main()
{
int[] arr = { 3, 7, 9, 8, 15 };
int result = countAlwaysFound(arr);
Console.WriteLine(result);
}
}
function countAlwaysFound(arr)
{
const n = arr.length;
if (n === 0)
return 0;
// Stores the maximum element from index 0 to i.
const leftMax = new Array(n);
// Initialize the first prefix maximum.
leftMax[0] = arr[0];
// Build the prefix maximum array.
for (let i = 1; i < n; i++)
leftMax[i] = Math.max(leftMax[i - 1], arr[i]);
// Stores the minimum element seen on the right.
let rightMin = arr[n - 1];
let count = 0;
// The last element is always a candidate because
// there are no elements on its right.
if (arr[n - 1] === leftMax[n - 1])
count++;
// Check the remaining elements from right to left.
for (let i = n - 2; i >= 0; i--) {
// arr[i] must be the maximum of its prefix
// and smaller than every element on its right.
if (arr[i] === leftMax[i] && arr[i] < rightMin)
count++;
// Update the minimum element seen on the right.
rightMin = Math.min(rightMin, arr[i]);
}
return count;
}
// Driver Code
const arr = [ 3, 7, 9, 8, 15 ];
const result = countAlwaysFound(arr);
console.log(result);
Output
3