Showing posts with label Monotonic Stack. Show all posts
Showing posts with label Monotonic Stack. Show all posts

LeetCode 1019 - Next Greater Node In Linked List


https://leetcode.com/problems/next-greater-node-in-linked-list/
We are given a linked list with head as the first node.  Let's number the nodes in the list: node_1, node_2, node_3, ... etc.
Each node may have a next larger value: for node_i, next_larger(node_i) is the node_j.val such that j > i, node_j.val > node_i.val, and j is the smallest possible choice.  If such a j does not exist, the next larger value is 0.
Return an array of integers answer, where answer[i] = next_larger(node_{i+1}).
Note that in the example inputs (not outputs) below, arrays such as [2,1,5] represent the serialization of a linked list with a head node value of 2, second node value of 1, and third node value of 5.

Example 1:
Input: [2,1,5]
Output: [5,5,0]
Example 2:
Input: [2,7,4,3,5]
Output: [7,0,5,5,0]
Example 3:
Input: [1,7,5,1,9,2,5,1]
Output: [7,9,9,9,0,5,0,0]

Note:
  1. 1 <= node.val <= 10^9 for each node in the linked list.
  2. The given list has length in the range [0, 10000]

X. https://leetcode.com/problems/next-greater-node-in-linked-list/discuss/265508/JavaC%2B%2BPython-Next-Greater-Element
Transform the linked list to an arraylist,
then it's a normal "next larger element" problem,
solved by stack.


    public int[] nextLargerNodes(ListNode head) {
        ArrayList<Integer> A = new ArrayList<>();
        for (ListNode node = head; node != null; node = node.next)
            A.add(node.val);
        int[] res = new int[A.size()];
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < A.size(); ++i) {
            while (!stack.isEmpty() && A.get(stack.peek()) < A.get(i))
                res[stack.pop()] = A.get(i);
            stack.push(i);
        }
        return res;
    }
X. https://leetcode.com/problems/next-greater-node-in-linked-list/discuss/265687/Java-simple-one-pass-solution-O(n)-time-complexity
int[] res;

public int[] nextLargerNodes(ListNode head) {
    calNode(head, 0, new Stack<>());
    return res;
}

public void calNode(ListNode node, int index, Stack<Integer> stack) {
    if(node == null) {
        res = new int[index];
        return;
    }
    calNode(node.next, index + 1, stack);
    while(!stack.empty() && stack.peek() <= node.val)
        stack.pop();
    res[index] = stack.empty() ? 0 : stack.peek();
    stack.push(node.val);
}




LeetCode 768 - Max Chunks To Make Sorted II


Related: LeetCode 769 - Max Chunks To Make Sorted
https://leetcode.com/problems/max-chunks-to-make-sorted-ii/
This question is the same as "Max Chunks to Make Sorted" except the integers of the given array are not necessarily distinct, the input array could be up to length 2000, and the elements could be up to 10**8.

Given an array arr of integers (not necessarily distinct), we split the array into some number of "chunks" (partitions), and individually sort each chunk.  After concatenating them, the result equals the sorted array.
What is the most number of chunks we could have made?
Example 1:
Input: arr = [5,4,3,2,1]
Output: 1
Explanation:
Splitting into two or more chunks will not return the required result.
For example, splitting into [5, 4], [3, 2, 1] will result in [4, 5, 1, 2, 3], which isn't sorted.
Example 2:
Input: arr = [2,1,3,4,4]
Output: 4
Explanation:
We can split into two chunks, such as [2, 1], [3, 4, 4].
However, splitting into [2, 1], [3], [4], [4] is the highest number of chunks possible.
Note:
  • arr will have length in range [1, 2000].
  • arr[i] will be an integer in range [0, 10**8]
X.
http://www.cnblogs.com/grandyang/p/8850299.html
这道题的时间复杂度可以优化到线性,不过就是需要花点空间。下面这种解法也相当的巧妙,我们需要两个数组forward和backward,其中 foward[i] 表示 [0, i] 范围内最大的数字,而 backward[i] 表示 [i, n-1] 范围内最小的数字,实际上就是要知道已经遍历过的最大值,和还未遍历的到的最小值。为啥我们对这两个值感兴趣呢?这是本解法的精髓所在,我们知道可以拆分为块儿的前提是之后的数字不能比当前块儿中的任何数字小,不然那个较小的数字就无法排到前面。就像例子1,为啥不能拆出新块儿,就因为最小的数字在末尾。那么这里我们能拆出新块儿的条件就是,当前位置出现过的最大值小于等于之后还未遍历到的最小值时,就能拆出新块儿。比如例子2中,当 i=1 时,此时出现过的最大数字为2,未遍历到的数字中最小值为3,所以可以拆出新块儿

如果把一个array的subarray视为一个整体(元素),如果在array中的某个位置,所有左边的元素都小于右边的元素,那么可以形成新的chunk
https://leetcode.com/problems/max-chunks-to-make-sorted-ii/discuss/113462/Java-solution-left-max-and-right-min.
Iterate through the array, each time all elements to the left are smaller (or equal) to all elements to the right, there is a new chunck.
Use two arrays to store the left max and right min to achieve O(n) time complexity. Space complexity is O(n) too.
This algorithm can be used to solve ver1 too.
    public int maxChunksToSorted(int[] arr) {
        int n = arr.length;
        int[] maxOfLeft = new int[n];
        int[] minOfRight = new int[n];

        maxOfLeft[0] = arr[0];
        for (int i = 1; i < n; i++) {
            maxOfLeft[i] = Math.max(maxOfLeft[i-1], arr[i]);
        }

        minOfRight[n - 1] = arr[n - 1];
        for (int i = n - 2; i >= 0; i--) {
            minOfRight[i] = Math.min(minOfRight[i + 1], arr[i]);
        }

        int res = 0;
        for (int i = 0; i < n - 1; i++) {
            if (maxOfLeft[i] <= minOfRight[i + 1]) res++;
        }

        return res + 1;
    }
我们可以优化一下空间复杂度,因为我们可以在遍历的过程中维护一个当前最大值curMax,所以就不用一个专门的forward数组了,但是backward数组还是要的
https://leetcode.com/problems/max-chunks-to-make-sorted-ii/discuss/113469/C%2B%2B-O(n)-greedy-with-proof
Going from left to right, if there is a number to the right that is less than the current max of the chunk or if the chunk is empty, then the next number to the right has to be included in the current chunk. Now if all the numbers to the right are at least as large as all numbers in the current chunk, then in an optimal solution, we can always split the current chunk out.
    int maxChunksToSorted(vector<int>& a) {
        int n = a.size();
        vector<int> b(n);
        b[n-1] = a[n-1];
        for( int i = n-2; i >=0; --i) b[i] = min(a[i],b[i+1]);
        int cnt = 1, ans = 1, mx = a[0];
        for (int i=1; i <n;++i) {
            if (b[i] < mx) ++cnt,mx = max(mx,a[i]);
            else cnt = 1, ++ans, mx = a[i];
        }
        return ans;
    }
https://blog.csdn.net/magicbean2/article/details/79641307
    int maxChunksToSorted(vector<int>& arr) {
        int n = arr.size();
        vector<int> right_min(n, 0);            // right_min[i] means the min value to its right
        right_min[n - 1] = arr[n - 1];
        for( int i = n - 2; i >= 0; --i) {
            right_min[i] = min(arr[i], right_min[i+1]);
        }
        int ans = 1, left_max = arr[0];
        for (int i = 1; i < n; ++i) {
            if (right_min[i] < left_max) {      // the right side has small value(s), hence cannot split
                left_max = max(left_max, arr[i]);
            }
            else {
                ++ans;
                left_max = arr[i];
            }
        }
        return ans;
    }
X. https://leetcode.com/problems/max-chunks-to-make-sorted-ii/discuss/113464/C%2B%2B-9-lines-15ms
it can even work on negative number.
Change int into long long than it can be safe for this question.
int maxChunksToSorted(vector<int>& arr) {
        int sum1 = 0, sum2 = 0, ans = 0;
        vector<int> t = arr;
        sort(t.begin(), t.end());
        for(int i = 0; i < arr.size(); i++) {
            sum1 += t[i];
            sum2 += arr[i];
            if(sum1 == sum2) ans++;
        }
 return ans;
    }
X.
下面这种使用单调栈Monotonous Stack的解法的题也十分的巧妙,我们维护一个单调递增的栈,遇到大于等于栈顶元素的数字就压入栈,当遇到小于栈顶元素的数字后,处理的方法很是巧妙啊:首先取出栈顶元素,这个是当前最大值,因为我们维护的就是单调递增栈啊,然后我们再进行循环,如果栈不为空,且新的栈顶元素大于当前数字,则移除栈顶元素。这步简直绝了,这里我们单调栈的元素个数实际上是遍历到当前数字之前可以拆分成的块儿的个数,我们遇到一个大于栈顶的元素,就将其压入栈,suppose其是一个新块儿的开头,但是一旦后面遇到小的数字了,我们就要反过来检查前面的数字,有可能我们之前认为的可以拆分成块儿的地方,现在就不能拆了,举个栗子来说吧:
比如数组为 [1 0 3 3 2],我们先把第一个数字1压入栈,此时栈为:
stack:1
然后遍历到第二个数字0,发现小于栈顶元素,将栈顶元素1取出存入curMax,此时栈为空了,不做任何操作,将curMax压回栈,此时栈为:
stack:1
然后遍历到第三个数字3,大于栈顶元素,压入栈,此时栈为:
stack:1,3
然后遍历到第四个数字3,等于栈顶元素,压入栈,此时栈为:
stack:1,3,3
然后遍历到第五个数字2,小于栈顶元素,将栈顶元素3取出存入curMax,此时新的栈顶元素3,大于当前数字2,移除此栈顶元素3,然后新的栈顶元素1,小于当前数字2,循环结束,将curMax压回栈,此时栈为:
stack:1,3
所以最终能拆为两个块儿,即stack中数字的个数
    int maxChunksToSorted(vector<int>& arr) {
        stack<int> st;
        for (int i = 0; i < arr.size(); ++i) {
            if (st.empty() || st.top() <= arr[i]) {
                st.push(arr[i]);
            } else {
                int curMax = st.top(); st.pop();
                while (!st.empty() && st.top() > arr[i]) st.pop();
                st.push(curMax);
            }
        }
        return st.size();
    }
https://leetcode.com/articles/max-chunks-to-make-sorted-ii/
Approach #1: Sliding Window [Accepted]
Let's try to find the smallest left-most chunk.
Algorithm
Notice that if a_1, a_2, \dots, a_m is a chunk, and a_1, a_2, \dots, a_n is a chunk (m &lt; n), then a_{m+1}, a_{m+2}, \dots, a_n is a chunk too. This shows that a greedy approach produces the highest number of chunks.
We know the array arr should end up like expect = sorted(arr). If the count of the first k elements minus the count what those elements should be is zero everywhere, then the first k elements form a valid chunk. We repeatedly perform this process.
We can use a variable nonzero to count the number of letters where the current count is non-zero.
  public int maxChunksToSorted(int[] arr) {
    Map<Integer, Integer> count = new HashMap();
    int ans = 0, nonzero = 0;

    int[] expect = arr.clone();
    Arrays.sort(expect);

    for (int i = 0; i < arr.length; ++i) {
      int x = arr[i], y = expect[i];

      count.put(x, count.getOrDefault(x, 0) + 1);
      if (count.get(x) == 0)
        nonzero--;
      if (count.get(x) == 1)
        nonzero++;

      count.put(y, count.getOrDefault(y, 0) - 1);
      if (count.get(y) == -1)
        nonzero++;
      if (count.get(y) == 0)
        nonzero--;

      if (nonzero == 0)
        ans++;
    }

    return ans;
  }


Approach #2: Sorted Count Pairs [Accepted]
As in Approach #1, let's try to find the smallest left-most chunk, where we have some expectation expect = sorted(arr)
If the elements were distinct, then it is enough to find the smallest k with max(arr[:k+1]) == expect[k], as this must mean the elements of arr[:k+1] are some permutation of expect[:k+1].
Since the elements are not distinct, this fails; but we can amend the cumulative multiplicity of each element to itself to make the elements distinct.
Algorithm
Instead of elements x, have counted elements (x, count) where count ranges from 1 to the total number of x present in arr.
Now cur will be the cumulative maximum of counted[:k+1], where we expect a result of Y = expect[k]. We count the number of times they are equal.
  public int maxChunksToSorted(int[] arr) {
    Map<Integer, Integer> count = new HashMap();
    List<Pair> counted = new ArrayList();
    for (int x : arr) {
      count.put(x, count.getOrDefault(x, 0) + 1);
      counted.add(new Pair(x, count.get(x)));
    }

    List<Pair> expect = new ArrayList(counted);
    Collections.sort(expect, (a, b) -> a.compare(b));

    Pair cur = counted.get(0);
    int ans = 0;
    for (int i = 0; i < arr.length; ++i) {
      Pair X = counted.get(i), Y = expect.get(i);
      if (X.compare(cur) > 0)
        cur = X;
      if (cur.compare(Y) == 0)
        ans++;
    }

    return ans;
  }
class Pair {
  int val, count;

  Pair(int v, int c) {
    val = v;
    count = c;
  }

  int compare(Pair that) {
    return this.val != that.val ? this.val - that.val : this.count - that.count;
  }
}




LeetCode 962 - Maximum Width Ramp



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:
  1. 2 <= A.length <= 50000
  2. 0 <= A[i] <= 50000

X.
https://zxi.mytechroad.com/blog/stack/leetcode-962-maximum-width-ramp/
  1. Using a stack to store start candidates’ (decreasing order) index
  2. Scan from right to left, compare the current number with the one on the top of the stack, pop if greater.
e.g.
A = [6,0,8,2,1,5]
stack = [0, 1] => [6, 0]
cur: A[5] = 5, stack.top = A[1] = 0, ramp = 5, stack.pop()
cur: A[4] = 1, stack.top = A[0] = 6
cur: A[3] = 2, stack.top = A[0] = 6
cur: A[2] = 8, stack.top = A[0] = 6, ramp = 2, stack.pop()
stack.isEmpty() => END
https://leetcode.com/problems/maximum-width-ramp/discuss/210812/3-Java-Solutions-1)-Brute-Force-2)-Sort-3)-Stack-with-Detailed-Comments
    // Stack is often used for problems like
    // find nearest larger/smaller elem (like water container problem)
    // here it's to find furthest larger/smaller elem (a bit harder than water container problme)
    // Time=O(N) Space=O(N)
    public int maxWidthRamp(int[] A) {
        // scanning from left to right to get all possible indices of the min element seen so far
        // think a bit and you'll discover they are valid START INDICES candidates for the widest ramp
        // they are [i0, i1, i2, i3, i4] as in the pic attached, you could use the pic to think on the algorithm
        int N = A.length;
        Stack<Integer> s = new Stack();
        for (int i = 0; i < N; i++) {
            if (s.isEmpty() || A[i] < A[s.peek()]) {
                s.push(i);
            }
        }
        // now scanning backwards for all other non-min elements, let them pair with all the candidates
        // we've collected in the first step in stack. Meanwhile, if we've discover that the current index i could
        // form a ramp with a min idx j, we know j couldn't form a better solution since i is going backwards
        // so we pop j out of stack
        int res = 0;
        for (int i = N - 1; i >= 0; i--) {
            while (!s.isEmpty() && A[s.peek()] <= A[i]) {
                res = Math.max(res, i - s.pop());
            }      
        }
        return res;
    }

https://leetcode.com/problems/maximum-width-ramp/discuss/208331/C%2B%2B-5-lines-search-in-decreasing-stack-O(n-log-n)


You may wonder why are we checking elements left to right while the maximum is likely to be on the far right side? If we check elements right to left, we do not need to keep smaller elements in the stack. So, we first populate the stack left to right with elements in decreasing order, and then go right to left and pop all elements smaller than the current one. This gives us O(n) runtime. One additional optimization is to populate stack until the top <= A[A.size() - 1].
int maxWidthRamp(vector<int>& A, int res = 0) {
  vector<int> v = { 0 };
  for (int i = 1; A[v.back()] > A[A.size() - 1]; ++i)
    if (A[v.back()] > A[i]) v.push_back(i);

  for (int j = A.size() - 1; j > res; --j)
    while (!v.empty() && A[v.back()] <= A[j]) {
      res = max(res, j - v.back());
      v.pop_back();
    }
  return res;
}
https://leetcode.com/problems/maximum-width-ramp/discuss/208348/JavaC%2B%2BPython-O(N)-Using-Stack
Improve the idea above.
Still one pass and keep a decraesing stack.
based on my understanding, the problem is :
for each element A[i], we need to go back left to find the farthest element that is smaller than A[i].
I do spent a while to figure out why we need to pop() when we have while (!s.empty() && A[s.top()] <= A[i]), then I realize, if A[s.top()] <= A[i] A[s.top()], since we keep a descending order stack , so we also have A[s.top()] <= A[i - 1], and since A[i] - s.top() > A[i - 1] - s.top(), so we need to pop to avoid duplicated calculation.
Then people may ask why descending order stack? because for each element we need to look back for a smaller or equal value, and a descending order stack can guarantee that the top element is always smaller or equal to current element.

The stack is used to store all candidate start points.
And by comparing elements (starting from the end of the array) with those candidates, we can eliminate points that are not bigger than the candidate.
Time Complexity:
O(N)


    public int maxWidthRamp(int[] A) {
        Stack<Integer> s = new Stack<>();
        int res = 0, n = A.length;
        for (int i = 0; i < n; ++i)
            if (s.empty() || A[s.peek()] > A[i])
                s.add(i);
        for (int i = n - 1; i > res; --i)
            while (!s.empty() && A[s.peek()] <= A[i])
                res = Math.max(res, i - s.pop());
        return res;
    }


Keep a decraesing stack.
For each number,
binary search the first smaller number in the stack.
When the number is smaller the the last,
push it into the stack.


Time Complexity:
O(NlogN)


    public int maxWidthRamp(int[] A) {
        List<Integer> s = new ArrayList<>();
        int res = 0, n = A.length;
        for (int i = 0; i < n; ++i) {
            if (s.size() == 0 || A[i] < A[s.get(s.size() - 1)]) {
                s.add(i);
            } else {
                int left = 0, right = s.size() - 1, mid = 0;
                while (left < right) {
                    mid = (left + right) / 2;
                    if (A[s.get(mid)] > A[i]) {
                        left = mid + 1;
                    } else {
                        right = mid;
                    }
                }
                res = Math.max(res, i - s.get(left));
            }
        }
        return res;
    }
X. https://blog.csdn.net/fuxuemingzhu/article/details/85223568
这里用到的是单调递减栈,如果遇到一个数字,比栈顶元素更小,那么就入栈;否则就在栈里边向后找到第一个刚好小于等于它的元素,此时的距离就是最远距离。
Not efficent
    def maxWidthRamp(self, A):
        """
        :type A: List[int]
        :rtype: int
        """
        N = len(A)
        stack = []
        res = 0
        for i, a in enumerate(A):
            if not stack or stack[-1][1] > a:
                stack.append((i, a))
            else:
                x = len(stack) - 1
                while x >= 0 and stack[x][1] <= a:
                    res = max(res, i - stack[x][0])
                    x -= 1
        return res
X. Left + right array
https://www.acwing.com/solution/LeetCode/content/672/
1. 维护一个前缀最小值数组 minlminl,和一个后缀最大值数组 maxrmaxr。例如,[6,0,8,2,1,5] 的两个数组分别为 [6,0,0,0,0,0], [8,8,8,5,5,5]。
2. 定义两个指针,初始时分别指向两个数组的开头,如果 minl[i] <= maxr[j],则更新答案后 j;否则 i;
3. 解释:如果 minl[i] <= maxr[j],则说明在 i 之前(包括 i)必定有一个数字小于等于 j之后(包括 j)的某个数字。且左端的数字就是 A[i],因为 i 是从最小的开始往后走的,故此时只需要向后移动 j,去尝试 j之后的数字某个是否也可行。
4. 如果 minl[i] > maxr[j],则说明 i 之前(包括 i)的所有数字都比 j 之后(包括 j)的所有数字要大,所以 i 需要向后移动寻找一个小一些的数字。

https://leetcode.com/problems/maximum-width-ramp/discuss/209582/O(n)-time-O(n)-space-using-two-array
Use two array to solve this problem. min_arr[i] is the minimum of the first i + 1 elements of A, max_arr[j] is the maximum of the last len(A) - j elements of A.
The idea of this solution is: Suppose (i, j) is the answer, then A[i] must be the smallest element among the first i + 1 elements of A and A[j] must be the largeset element among the last len(A) - j elements of A. Otherwise, we can always find an element smaller than A[i] or larger than A[j], so that (i, j) is not the maximum width ramp.
For example, the input is [6, 0, 8, 2, 1, 5]. Then min_arr=[6, 0, 0, 0, 0, 0] and max_arr=[8, 8, 8, 5, 5, 5].
We can find the ramp use two points, left and right. left starts from the beginning of min_arr[i] and right starts from the beginning of max_arr[i]. Increase right by 1 when min_arr[left] <= max_arr[right], else increase left by 1
https://leetcode.com/problems/maximum-width-ramp/discuss/208341/O(N)-JAVA
    public int maxWidthRamp(int[] A) {
        int n = A.length;
        int i, j , max = 0;
        int[] maxR = new int[n], minL = new int[n];
        minL[0] = A[0];
        for (i = 1; i < n; i++){
            minL[i] = Math.min(A[i], minL[i - 1]);
        }
        maxR[n - 1] = A[n - 1];
        for (j = n - 2; j >= 0; j--){
            maxR[j] = Math.max(A[j], maxR[j + 1]);
        }
        i = 0; j = 0;
        while (i < n && j < n){
            if (minL[i] <= maxR[j]){
                max = Math.max(max, j - i);
                j++;
            }else{
                i++;
            }
        }
        return max;
    }


You don't need to calculate minL, only maxR should work!
    public int maxWidthRamp(int[] A) {
        if(A.length == 0){
            return 0;
        }
        int[] maxR = new int[A.length];
        maxR[A.length-1] = A[A.length-1];
        for(int i=A.length-2;i>=0;i--){
            maxR[i] = Math.max(A[i], maxR[i+1]);
        }
        
        int i=0;
        int j=0;
        int res = 0;
        while(i<A.length && j<A.length){
            if(A[i]<=maxR[j]){
                 res = Math.max(res, j-i);
                 j++;
            }else{
                i++;
            }
        }
        
        return res;
    }
X. Two Pointers
https://leetcode.com/problems/maximum-width-ramp/discuss/208328/Two-pointer-O(nlogn)-C%2B%2B-O(n)-space
After i had stored the useful information from problem using vector of pair. I sorted the array.
After I used two pointer consecutive to each other ( pointer at begining and end is not feasible)

int maxWidthRamp(vector<int>& A) 
    {
        vector<pair<int, int>> vec;
        int n = A.size();
        
        for (int i = 0; i < n; ++i) vec.push_back({A[i], i});
        sort(vec.begin(), vec.end());
        
        int i = 0, j = 1, res = 0;
        while (j < n)
        {
            if (vec[i].second <= vec[j].second)
            {
                res = max(res, vec[j].second - vec[i].second);
                ++j;
            }
            else ++i;
        }
        
        return res;
    }
    public int maxWidthRamp(int[] A) {
        int modA[][] = new int[A.length][2];
        for (int i = 0 ; i < A.length ; i++)    {
            modA[i] = new int[] { A[i], i};
        }
        
        Arrays.sort(modA, (m1, m2) -> m1[0] - m2[0]);
        
        int i = 0;
        int j = 1;
        int maxWidth = 0;
        while (j < A.length)    {
            if (modA[j][1] > modA[i][1])    {
                maxWidth = Integer.max(maxWidth, modA[j][1] - modA[i][1]);
                j++;
            }
            else
                i++;
            
            if (i == j)
                j++;
        }
        
        return maxWidth;
    }

X. https://leetcode.com/articles/maximum-width-ramp/
Approach 1: Sort
For all elements like A[i] = v, let's write the indices i in sorted order of their values v. For example with A[0] = 7, A[1] = 2, A[2] = 5, A[3] = 4, we can write the order of indices i=1, i=3, i=2, i=0.
Then, whenever we write an index i, we know there was a ramp of width i - min(indexes_previously_written) (if this quantity is positive). We can keep track of the minimum of all indexes previously written as m.

  public int maxWidthRamp(int[] A) {
    int N = A.length;
    Integer[] B = new Integer[N];
    for (int i = 0; i < N; ++i)
      B[i] = i;

    Arrays.sort(B, (i, j) -> ((Integer) A[i]).compareTo(A[j]));

    int ans = 0;
    int m = N;
    for (int i : B) {
      ans = Math.max(ans, i - m);
      m = Math.min(m, i);
    }

    return ans;
  }

    // 1. sort on value
    // 2. sort on index <-- used in this problem Time=O(NlgN), Space=O(N)
    public int maxWidthRamp2(int[] A) {
        // sort index according to values and then do a dp
        int N = A.length;
        Integer[] indices = new Integer[N]; // need to use Integer for Arrays.sort with lambda to work
        Arrays.setAll(indices, i -> i);
        Arrays.sort(indices, (i, j) -> A[i] != A[j] ? A[i] - A[j] : i - j);
        // use dp to find min idx so far
        int minIdx = indices[0];
        int res = 0;
        for (int idx : indices) {
            res = Math.max(res, idx - minIdx);
            minIdx = Math.min(minIdx, idx);
        }
        return res;
    }
Approach 2: Binary Search Candidates
https://www.jianshu.com/p/cb458cc61e31
Keep a decresing stack.
For each number,
binary search the first smaller number in the stack.
When the number is smaller the the last,
push it into the stack.
    def maxWidthRamp(self, A):
        stack = []
        res = 0
        for i in range(len(A))[::-1]:
            if not stack or A[i] > stack[-1][0]:
                stack.append([A[i], i])
            else:
                j = stack[bisect.bisect(stack, [A[i], i])][1]
                res = max(res, j - i)
        return res
Consider i in decreasing order. We want to find the largest j with A[j] >= A[i] if it exists.
Thus, the candidates for j are decreasing: if there is j1 < j2 and A[j1] <= A[j2] then we strictly prefer j2.
Algorithm
Let's keep a list of these candidates j. For example, with A = [0,8,2,7,5], the candidates for i = 0 would be candidates = [(v=5, i=4), (v=7, i=3), (v=8, i=1)]. We keep the list of candidates in decreasing order of i and increasing order of v.
Now we can binary search to find the largest j with A[j] >= A[i]: it's the first one in this list of candidates with v >= A[i].

  public int maxWidthRamp(int[] A) {
    int N = A.length;

    int ans = 0;
    List<Point> candidates = new ArrayList();
    candidates.add(new Point(A[N - 1], N - 1));

    // candidates: i's decreasing, by increasing value of A[i]
    for (int i = N - 2; i >= 0; --i) {
      // Find largest j in candidates with A[j] >= A[i]
      int lo = 0, hi = candidates.size();
      while (lo < hi) {
        int mi = lo + (hi - lo) / 2;
        if (candidates.get(mi).x < A[i])
          lo = mi + 1;
        else
          hi = mi;
      }

      if (lo < candidates.size()) {
        int j = candidates.get(lo).y;
        ans = Math.max(ans, j - i);
      } else {
        candidates.add(new Point(A[i], i));
      }
    }
    return ans;
  }
X. TreeMap
https://jinx19.github.io/2018/12/23/LeetCode-Solution-2018-12-23-LeetCode-Solution-962-Maximum-Width-Ramp/
https://leetcode.com/problems/maximum-width-ramp/discuss/208659/Simple-java-solution-with-TreeMap
This solution is similar to decending stack solution, but with TreeMap, we can further simplify the logic as treemap maintains the natural ordering, to search a particular value is more straight forward with method of: TreeMap.floorEntry().
The map stores the value to postion mapping. The values is sorted in ascending order.
  public int maxWidthRamp(int[] A) {
    int max = 0;
    NavigableMap<Integer, Integer> valPosMap = new TreeMap<>();
    for (int i = 0; i < A.length; i++) {
      if (i == 0 || A[i] < valPosMap.firstKey())
        valPosMap.put(A[i], i);
      else {
        int cur = i - valPosMap.floorEntry(A[i]).getValue();
        if (cur > max)
          max = cur;
      }
    }
    return max;
  }
https://leetcode.com/problems/maximum-width-ramp/discuss/209060/TreeMap-O(nlogn)-8-lines-of-Java
    public int maxWidthRamp(int[] A) {  
        TreeMap<Integer, Integer> tm = new TreeMap<>();
        int res = 0; 
        for (int i=A.length-1; i>=0; i--) {
            Integer k = tm.ceilingKey(A[i]);
            if (k == null) 
                tm.put(A[i], i);
            else 
                res = Integer.max(res, tm.get(k)-i);
        }
        return res;
    }
X. https://leetcode.com/problems/maximum-width-ramp/discuss/210812/3-Java-Solutions-1)-Brute-Force-2)-Sort-3)-Stack-with-Detailed-Comments
    //------ Solution 1 Brute force -------------//
    // brute force with fast break
    // Time=O(N^2) (a bit better) Space=O(1)
    public int maxWidthRamp1(int[] A) {
        int N = A.length;
        int res = 0;
        rightIdx: for(int i = 0; i < A.length; ++i) {
            leftIdx: for(int j = 0; j < i; ++j) {
                if (A[i] >= A[j]) {
                    res = Math.max(res, i - j);
                    continue rightIdx; // stop inner loop at the first valid index j
                }
            }
        }
        return res;
    }
https://kingsfish.github.io/2018/12/23/Leetcode-962-Maximum-Width-Ramp/
这种思路的最差时间复杂度是O(n^2),最优时间复杂度是O(1),空间复杂度是O(1)。

public int maxWidthRamp(int[] A) {
int n = A.length;
int max = 0;
for (int i = 0; i < n; i ++) {
if(max >= n - 1 - i){
break;
}
for (int j = n - 1; j > i; j --) {
if (A[j] >= A[i]) {
max = Math.max(max, j - i);
}
}
}
return max;
}
https://leetcode.com/problems/maximum-width-ramp/discuss/211756/Normal-DP-%2B-pruning-solution-in-Java
    public int maxWidthRamp(int[] A) {
        int[] dp = new int[A.length+1];
        int res = 0;
        for(int i = 2; i < A.length + 1; ++i){
            int maxval = 0;
            for(int j = 1; j < i; ++j) {
                if(A[i-1] >= A[j-1]) {
                    maxval = Math.max(maxval, dp[j-1] + (i-j));   
                    break;
                }
            }
            res = Math.max(res, maxval);
        }
        return res;
    }

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts