Maximum Width Ramp, an O(NlogN) solution
Problem is here: https://leetcode.com/problems/maximum-width-ramp/description/:
There must be an O(N) solution to this problem, but I got an NLogN one (sorry). The idea is the following:
Given an array
A
of integers, a ramp is a tuple (i, j)
for which i < j
and A[i] <= A[j]
. The width of such a ramp is j - i
.
Find the maximum width of a ramp in
A
. If one doesn't exist, return 0.
Example 1:
Input: [6,0,8,2,1,5] Output: 4 Explanation: The maximum width ramp is achieved at (i, j) = (1, 5): A[1] = 0 and A[5] = 5.
Example 2:
Input: [9,8,1,0,1,9,4,0,4,1] Output: 7 Explanation: The maximum width ramp is achieved at (i, j) = (2, 9): A[2] = 1 and A[9] = 1.
Note:
2 <= A.length <= 50000
0 <= A[i] <= 50000
- Create a class called "Pair" which will have the value and index of the array
- Implement an IComparable interface (to be used in the Sort algorithm) in such a way that if the two values are the same, compare based on the index, otherwise compare based on the value
- After that do a linear scan keeping track of the smallest index all the way from 0..i-1, and check at element i whether the best width is greater than the max (using the smallest index var)
Not very fast by any stretch of the imagination. Would love to hear about an O(N) solution. Cheers, Marcelo
public class Solution { public int MaxWidthRamp(int[] A) { Pair[] pairs = new Pair[A.Length]; for (int i = 0; i < A.Length; i++) { pairs[i] = new Pair(A[i], i); } Array.Sort(pairs); int maxWidth = 0; int smallestIndex = pairs[0].index; for (int i = 1; i < pairs.Length; i++) { maxWidth = Math.Max(maxWidth, pairs[i].index - smallestIndex); smallestIndex = Math.Min(smallestIndex, pairs[i].index); } return maxWidth; } } public class Pair : IComparable { public int val; public int index; public Pair(int val, int index) { this.val = val; this.index = index; } public int CompareTo(Object obj) { Pair p = obj as Pair; if (this.val == p.val) return this.index.CompareTo(p.index); return this.val.CompareTo(p.val); } }
Very clever! I've decided to use a balanced binary search tree to keep track of the indices where I've seen the largest number starting from the end and using a binary search to find an index of the first element that is greater or equal to the current element.
ReplyDeleteclass Solution {
public:
int maxWidthRamp(vector& A) {
map largest;
largest[A.back()] = A.size()-1;
int max_width = 0;
for (int i = A.size()-2; i >= 0; --i) {
auto found = largest.lower_bound(A[i]);
if (found != largest.cend()) {
max_width = max(max_width, found->second - i);
}
if (A[i] > largest.rbegin()->first) {
largest[A[i]] = i;
}
}
return max_width;
}
};
https://gist.github.com/ttsugriy/81744e62aed3f84bdd58a0ffe9c79fcb
This implementation has the same O(N*log(N)) complexity, but I have a gut feeling that it can be done in linear time...
yep, my gut feeling was correct - we don't actually need a map to maintain the largest index seen so far when scanning from the end - we can use a stack/list to maintain it. Another important observation is that we don't need to do a binary search to find the first appropriate index if we do a second scan from the beginning, since we can discard all elements that are greater or equal to this the current value, since the only other elements they would be greater or equal to them would be to the right of the current element if we don't take the current element. The implementation of this idea in Rust is below:
Deleteimpl Solution {
pub fn max_width_ramp(a: Vec) -> i32 {
let mut largest: Vec = vec![(a.len()-1) as usize];
for (i, val) in a.iter().enumerate().rev().skip(1) {
if *val > a[*largest.last().unwrap()] {
largest.push(i);
}
}
let mut max_width = 0;
for (i, val) in a.iter().enumerate() {
while !largest.is_empty() && *val <= a[*largest.last().unwrap()] {
max_width = std::cmp::max(max_width, largest.pop().unwrap() - i);
}
}
max_width as i32
}
}
https://gist.github.com/ttsugriy/d106f66e30bd8db704ef96d41b4ae6d4
btw, the Rust solution above takes 4ms :)
Delete