3637. Trionic Array I
Problem Description
You are given an integer array nums
of length n
.
An array is called trionic if it can be divided into three parts with the following properties:
- The first part
nums[0...p]
is strictly increasing - The second part
nums[p...q]
is strictly decreasing - The third part
nums[q...n-1]
is strictly increasing
where p
and q
are indices satisfying 0 < p < q < n - 1
.
Your task is to determine if the given array is trionic. Return true
if nums
is trionic, otherwise return false
.
Key requirements:
- The array must have at least 4 elements (to accommodate three non-empty parts with the index constraints)
- Each part must be strictly monotonic (no equal consecutive elements allowed)
- The transition points
p
andq
must exist within the valid range - The first part ends at index
p
, the second part starts atp
and ends atq
, and the third part starts atq
and goes to the end
For example:
[1, 3, 2, 4]
would be trionic withp=1
andq=2
: increasing[1,3]
, decreasing[3,2]
, increasing[2,4]
[1, 2, 3, 4]
would not be trionic as there's no decreasing section[4, 3, 2, 1]
would not be trionic as there's no initial increasing section or final increasing section
Intuition
The key insight is that a trionic array has a very specific shape: it goes up, then down, then up again. We can think of it like a valley between two hills. To verify this pattern, we need to find the two transition points where the direction changes.
Since we need to check for strict monotonicity in each section, we can traverse the array once and identify where these transitions occur. The natural approach is to:
-
Start from the beginning and keep moving forward while the array is strictly increasing. This gives us the peak of the first hill (point
p
). -
From that peak, continue moving forward while the array is strictly decreasing. This takes us to the bottom of the valley (point
q
). -
From the valley bottom, check if the remaining elements form a strictly increasing sequence until the end.
The elegance of this approach lies in its simplicity - we're essentially following the "shape" of the array. If at any point we can't find the expected pattern (like if there's no initial increase, or no decrease after the first peak, or no final increase), we know the array isn't trionic.
We also need to be careful about edge cases:
- If
p = 0
, it means we never found an increasing part at the start - If
q = p
, it means we never found a decreasing part after the first peak - If
q = n - 1
, it means there's no room for a final increasing part - If we can't reach the end with the final increasing check, the third part isn't fully increasing
This linear scan approach naturally validates all the constraints while finding the transition points in a single pass through the array.
Solution Approach
The implementation uses a single-pass algorithm with pointer manipulation to identify the three required sections of a trionic array.
Step 1: Find the end of the strictly increasing section
We initialize a pointer p = 0
and move it rightward as long as nums[p] < nums[p + 1]
. This loop continues while we have a strictly increasing sequence. The loop stops when we either reach the position n - 2
(to avoid index out of bounds) or find an element that doesn't satisfy the strict increase condition.
while p < n - 2 and nums[p] < nums[p + 1]: p += 1
After this loop, p
points to the last element of the first increasing section. We check if p = 0
, which would mean no increasing section was found at the beginning, and return false
immediately.
Step 2: Find the end of the strictly decreasing section
We initialize q = p
(starting from where the increasing section ended) and move it rightward as long as nums[q] > nums[q + 1]
. This identifies the strictly decreasing section.
while q < n - 1 and nums[q] > nums[q + 1]: q += 1
After this loop, q
points to the last element of the decreasing section. We check two conditions:
- If
q = p
: No decreasing section was found - If
q = n - 1
: The decreasing section extends to the end, leaving no room for the third increasing section
If either condition is true, we return false
.
Step 3: Verify the final strictly increasing section
Starting from the current position of q
, we check if the remaining elements form a strictly increasing sequence:
while q < n - 1 and nums[q] < nums[q + 1]: q += 1
Step 4: Final validation
If q = n - 1
after the third loop, it means we successfully traversed the entire array following the trionic pattern (increase → decrease → increase). We return true
. Otherwise, the third section didn't extend to the end of the array as required, so we return false
.
The time complexity is O(n)
since we traverse the array at most once, and the space complexity is O(1)
as we only use two pointers.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the algorithm with the array nums = [2, 5, 3, 1, 4, 6]
.
Initial Setup:
- Array length
n = 6
- Initialize pointer
p = 0
Step 1: Find the first increasing section
- Start at
p = 0
: Check ifnums[0] < nums[1]
→2 < 5
✓, movep
to 1 - At
p = 1
: Check ifnums[1] < nums[2]
→5 < 3
✗, stop - First increasing section:
[2, 5]
(indices 0 to 1) p = 1
(last index of increasing section)
Step 2: Find the decreasing section
- Initialize
q = p = 1
- At
q = 1
: Check ifnums[1] > nums[2]
→5 > 3
✓, moveq
to 2 - At
q = 2
: Check ifnums[2] > nums[3]
→3 > 1
✓, moveq
to 3 - At
q = 3
: Check ifnums[3] > nums[4]
→1 > 4
✗, stop - Decreasing section:
[5, 3, 1]
(indices 1 to 3) q = 3
(last index of decreasing section)
Step 3: Verify the final increasing section
- At
q = 3
: Check ifnums[3] < nums[4]
→1 < 4
✓, moveq
to 4 - At
q = 4
: Check ifnums[4] < nums[5]
→4 < 6
✓, moveq
to 5 - At
q = 5
: We've reachedn - 1
, stop - Final increasing section:
[1, 4, 6]
(indices 3 to 5)
Step 4: Final validation
- Check if
q == n - 1
→5 == 5
✓ - The array successfully forms the pattern: increase → decrease → increase
- Return
true
The array [2, 5, 3, 1, 4, 6]
is trionic with transition points at p = 1
and q = 3
.
Solution Implementation
1class Solution:
2 def isTrionic(self, nums: List[int]) -> bool:
3 """
4 Check if the array follows a trionic pattern:
5 1. First phase: strictly increasing
6 2. Second phase: strictly decreasing
7 3. Third phase: strictly increasing
8 Each phase must contain at least one transition.
9 """
10 n = len(nums)
11
12 # Phase 1: Find the end of the first increasing sequence
13 first_peak_index = 0
14 while first_peak_index < n - 2 and nums[first_peak_index] < nums[first_peak_index + 1]:
15 first_peak_index += 1
16
17 # If no increasing phase found at the start, pattern is invalid
18 if first_peak_index == 0:
19 return False
20
21 # Phase 2: Find the end of the decreasing sequence (valley point)
22 valley_index = first_peak_index
23 while valley_index < n - 1 and nums[valley_index] > nums[valley_index + 1]:
24 valley_index += 1
25
26 # Check if decreasing phase exists and doesn't reach the end
27 # valley_index == first_peak_index means no decreasing phase
28 # valley_index == n - 1 means no room for final increasing phase
29 if valley_index == first_peak_index or valley_index == n - 1:
30 return False
31
32 # Phase 3: Find the end of the final increasing sequence
33 final_index = valley_index
34 while final_index < n - 1 and nums[final_index] < nums[final_index + 1]:
35 final_index += 1
36
37 # Valid trionic pattern if the final increasing phase reaches the end
38 return final_index == n - 1
39
1class Solution {
2 /**
3 * Checks if the array follows a trionic pattern:
4 * 1. Strictly increasing sequence
5 * 2. Followed by strictly decreasing sequence
6 * 3. Followed by strictly increasing sequence
7 * All three segments must exist with at least one element each.
8 *
9 * @param nums input array to check
10 * @return true if array follows trionic pattern, false otherwise
11 */
12 public boolean isTrionic(int[] nums) {
13 int arrayLength = nums.length;
14 int currentIndex = 0;
15
16 // Phase 1: Find the first strictly increasing segment
17 // Move forward while elements are strictly increasing
18 while (currentIndex < arrayLength - 2 && nums[currentIndex] < nums[currentIndex + 1]) {
19 currentIndex++;
20 }
21
22 // If no increasing segment found at the beginning, pattern is invalid
23 if (currentIndex == 0) {
24 return false;
25 }
26
27 // Phase 2: Find the strictly decreasing segment
28 // Save the peak position where decreasing starts
29 int peakIndex = currentIndex;
30
31 // Move forward while elements are strictly decreasing
32 while (currentIndex < arrayLength - 1 && nums[currentIndex] > nums[currentIndex + 1]) {
33 currentIndex++;
34 }
35
36 // Check if decreasing segment exists and doesn't reach the end
37 // (we need room for the final increasing segment)
38 if (currentIndex == peakIndex || currentIndex == arrayLength - 1) {
39 return false;
40 }
41
42 // Phase 3: Find the final strictly increasing segment
43 // Move forward while elements are strictly increasing
44 while (currentIndex < arrayLength - 1 && nums[currentIndex] < nums[currentIndex + 1]) {
45 currentIndex++;
46 }
47
48 // Pattern is valid only if we've reached the end of the array
49 return currentIndex == arrayLength - 1;
50 }
51}
52
1class Solution {
2public:
3 bool isTrionic(vector<int>& nums) {
4 int n = nums.size();
5
6 // Phase 1: Find the end of the first increasing segment
7 // Start from index 0 and move right while values are increasing
8 int firstPeakIndex = 0;
9 while (firstPeakIndex < n - 2 && nums[firstPeakIndex] < nums[firstPeakIndex + 1]) {
10 firstPeakIndex++;
11 }
12
13 // If no increasing segment found at the beginning, pattern is invalid
14 if (firstPeakIndex == 0) {
15 return false;
16 }
17
18 // Phase 2: Find the end of the decreasing segment (valley)
19 // Start from the peak and move right while values are decreasing
20 int valleyIndex = firstPeakIndex;
21 while (valleyIndex < n - 1 && nums[valleyIndex] > nums[valleyIndex + 1]) {
22 valleyIndex++;
23 }
24
25 // If no decreasing segment found or it reaches the end, pattern is invalid
26 if (valleyIndex == firstPeakIndex || valleyIndex == n - 1) {
27 return false;
28 }
29
30 // Phase 3: Verify the final increasing segment
31 // Start from the valley and check if values increase until the end
32 int currentIndex = valleyIndex;
33 while (currentIndex < n - 1 && nums[currentIndex] < nums[currentIndex + 1]) {
34 currentIndex++;
35 }
36
37 // Valid trionic pattern if the final increasing segment reaches the last element
38 return currentIndex == n - 1;
39 }
40};
41
1/**
2 * Checks if an array forms a trionic pattern (mountain-valley-mountain).
3 * A trionic pattern consists of:
4 * 1. An increasing sequence (ascending)
5 * 2. Followed by a decreasing sequence (descending)
6 * 3. Followed by another increasing sequence (ascending)
7 *
8 * @param nums - The input array of numbers to check
9 * @returns true if the array forms a trionic pattern, false otherwise
10 */
11function isTrionic(nums: number[]): boolean {
12 const arrayLength: number = nums.length;
13 let currentIndex: number = 0;
14
15 // Phase 1: Find the first ascending sequence
16 // Move forward while numbers are increasing
17 while (currentIndex < arrayLength - 2 && nums[currentIndex] < nums[currentIndex + 1]) {
18 currentIndex++;
19 }
20
21 // If no ascending sequence found at the beginning, not trionic
22 if (currentIndex === 0) {
23 return false;
24 }
25
26 // Store the peak position of the first mountain
27 let peakIndex: number = currentIndex;
28
29 // Phase 2: Find the descending sequence (valley)
30 // Move forward while numbers are decreasing
31 while (peakIndex < arrayLength - 1 && nums[peakIndex] > nums[peakIndex + 1]) {
32 peakIndex++;
33 }
34
35 // If no descending sequence found or it reaches the end, not trionic
36 if (peakIndex === currentIndex || peakIndex === arrayLength - 1) {
37 return false;
38 }
39
40 // Phase 3: Find the second ascending sequence
41 // Move forward while numbers are increasing again
42 while (peakIndex < arrayLength - 1 && nums[peakIndex] < nums[peakIndex + 1]) {
43 peakIndex++;
44 }
45
46 // Valid trionic pattern if we've reached the end of the array
47 return peakIndex === arrayLength - 1;
48}
49
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array. The algorithm makes a single pass through the array using three sequential while loops. In the worst case, each element is visited at most once across all three loops combined. The first loop iterates through elements while they're increasing, the second loop continues from where the first stopped and iterates while elements are decreasing, and the third loop continues from where the second stopped and iterates while elements are increasing again. Since each element is processed at most once, the total time complexity is linear.
The space complexity is O(1)
, using only constant extra space. The algorithm uses only a fixed number of variables (n
, p
, and q
) regardless of the input size. No additional data structures like arrays, lists, or recursive call stacks are created, so the memory usage remains constant.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Off-by-One Errors with Index Boundaries
A critical mistake occurs when developers misunderstand how the indices p
and q
relate to the array segments. After the first while loop, p
points to the last element of the increasing section, not the first element of the decreasing section. This confusion often leads to incorrect boundary checks.
Incorrect approach:
# Wrong: Treating p as the start of decreasing section if p >= n - 2: # This check is too restrictive return False
Correct approach:
# Right: p is the last element of increasing section # We need at least 2 more elements after p for valid trionic if p == 0: # No increasing section found return False
Pitfall 2: Not Handling Adjacent Duplicate Values
The problem requires strictly monotonic sequences, meaning consecutive equal values invalidate the pattern. Developers often use <=
or >=
comparisons instead of strict <
or >
.
Incorrect approach:
# Wrong: Allows equal consecutive values while p < n - 2 and nums[p] <= nums[p + 1]: # Should be strict < p += 1
Correct approach:
# Right: Strict inequality ensures no duplicates while p < n - 2 and nums[p] < nums[p + 1]: p += 1
Pitfall 3: Incorrect Minimum Array Length Validation
Some developers add explicit length checks that are either unnecessary or incorrect. The algorithm naturally handles the minimum length requirement through its logic.
Incorrect approach:
def isTrionic(self, nums):
n = len(nums)
if n < 3: # Wrong threshold - should be 4 minimum
return False
# ... rest of code
Better approach:
The current solution implicitly handles this through the while loop conditions. The first loop requires p < n - 2
, ensuring at least 3 elements. The subsequent checks ensure we need at least 4 elements total for a valid trionic pattern.
Pitfall 4: Misunderstanding the Shared Boundary Elements
A subtle but important detail: the element at index p
belongs to both the first increasing section (as its last element) and the second decreasing section (as its first element). Similarly for index q
.
Example with [1, 3, 2, 4]:
- First section: indices 0 to 1, values [1, 3]
- Second section: indices 1 to 2, values [3, 2]
- Third section: indices 2 to 3, values [2, 4]
The boundaries overlap - this is correct and intended behavior. Developers sometimes try to "fix" this by adjusting indices unnecessarily, breaking the algorithm.
What is the best way of checking if an element exists in a sorted array once in terms of time complexity? Select the best that applies.
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!