Binary Search Trees · 17 of 22
Replace every element with the least greater element on its right
For each number in a row, find the smallest bigger number somewhere to its right, by reading the row backwards into a sorted tree.
- Simple way
- time O(n^2)
room O(1) - Clever way
- time O(n h)
room O(n)
The problem, simply
You get an array of numbers. Replace each number with the smallest number that is bigger than it and sits somewhere to its right. If no number to its right is bigger, write -1.
Note the difference from "next greater element": we do not want the first bigger number to the right, we want the smallest bigger one anywhere to the right.
A story to remember it
Picture it
Read the row from the right and put each number into a BST. When 7 goes in, it turns left at 8, its last left turn. So 8 is the smallest number to its right that is bigger than 7.
- looking now
- answer
- path
- just added
Examples
A mixed row
- In
[7, 3, 9, 1, 8, 2, 6, 5]- Out
[8, 5, -1, 2, -1, 5, -1, -1]
For 3, the bigger numbers to its right are 9, 8, 6 and 5. The smallest of them is 5.
The classic
- In
[8, 58, 71, 18, 31, 32, 63, 92, 43, 3, 91, 93, 25, 80, 28]- Out
[18, 63, 80, 25, 32, 43, 80, 93, 80, 25, 93, -1, 28, -1, -1]
For 8, the smallest bigger number to its right is 18, even though 58 comes first.
Going down
- In
[5, 4, 3, 2, 1]- Out
[-1, -1, -1, -1, -1]
Nothing to the right of any number is bigger.
Going up
- In
[1, 2, 3, 4]- Out
[2, 3, 4, -1]
The smallest bigger number is the very next one.
Repeats
- In
[2, 2, 3]- Out
[3, 3, -1]
An equal number is not bigger. Both 2s get 3.
Way 1: the simple way
For each number, scan everything to its right and keep the smallest number that is bigger.
- For each
i, start withbest = infinity. - For each
j > i, ifa[j] > a[i]anda[j] < best, setbest = a[j]. - Write
best, or-1if it stayed at infinity.
class Solution {
public int[] leastGreater(int[] a) {
int n = a.length;
int[] out = new int[n];
for (int i = 0; i < n; i++) {
int best = Integer.MAX_VALUE;
boolean found = false;
for (int j = i + 1; j < n; j++) {
if (a[j] > a[i] && a[j] <= best) {
best = a[j];
found = true;
}
}
out[i] = found ? best : -1;
}
return out;
}
}How long it takes: about n * n / 2 comparisons, O(n^2). Extra room besides the answer: O(1).
Way 2: the clever way
Read the array from right to left. Keep a BST of the numbers already read, which are exactly the numbers to the right of the current one.
- For
ifrom the last index down to 0, inserta[i]into the BST with a normal walk: smaller goes left, otherwise right. - During the walk, every time you go left at a spot, that spot is bigger than
a[i]. Remember it assucc. A later left turn is always at a smaller spot, so the last one is the smallest bigger number. - The answer for
iissucc, or-1if you never turned left.
Read from the right, insert, and note the last left turn
01 / 08
Start at the right end. 5 is the first number in the tree. Nothing is to its right, so its answer is -1.
- least greater
- -1
- looking now
- seen
- done
- answer
- path
- just added
class Solution {
private int succ; // the last left turn of this insert
public int[] leastGreater(int[] a) {
int[] out = new int[a.length];
TreeNode root = null;
for (int i = a.length - 1; i >= 0; i--) { // read from the right
succ = -1;
root = insert(root, a[i]);
out[i] = succ;
}
return out;
}
private TreeNode insert(TreeNode node, int x) {
if (node == null) return new TreeNode(x);
if (x < node.val) {
succ = node.val; // bigger than x: a candidate
node.left = insert(node.left, x);
} else {
node.right = insert(node.right, x); // equal or smaller: not bigger
}
return node;
}
}How long it takes: each insert walks one road, O(h), so O(n h) in total. That is O(n log n) when the tree stays bushy, but a sorted input makes a stick and O(n^2). Extra room: the tree, O(n).
Another way: a balanced tree from the library
Java's TreeSet is a self balancing BST, so its height stays about log n whatever the input. higher(x) gives the smallest number strictly bigger than x. Since repeats do not matter for this question, a set is enough.
import java.util.*;
class Solution {
public int[] leastGreater(int[] a) {
int[] out = new int[a.length];
TreeSet<Integer> right = new TreeSet<>(); // numbers to the right of i
for (int i = a.length - 1; i >= 0; i--) {
Integer up = right.higher(a[i]); // smallest number bigger than a[i]
out[i] = (up == null) ? -1 : up;
right.add(a[i]);
}
return out;
}
}This is a guaranteed O(n log n) with O(n) room. In an interview, write this one, and explain that it is the same left turn idea inside a red black tree.
Watch out for
- Treating equal as bigger. Use strict
x < node.valfor a left turn, and send equal numbers right. - Reading from the left. The tree must hold only numbers to the right of
i, so read from the right end. - Forgetting to reset
succto-1before each insert.
What interviewers ask next
- Count how many numbers to the right are smaller (LeetCode 315). Same backwards insert, but each spot stores the size of its left side, and every right turn adds that size plus one.
- The largest smaller number to the right. Mirror it: the last right turn wins, or use
TreeSet.lower. - Why does a plain BST risk
O(n^2)? An already sorted input makes every insert walk the full stick. A balanced tree or shuffling avoids it. - Can it be done in
O(n log n)without a tree? Yes: sort the indices by value, then for each value find the next index to the right in sorted order using a stack, as in "next greater element" on the sorted list of indices.
Remember this
Grown-up words: the kids already inside are a BST of the suffix, the last left turn is the inorder successor of the new value, higher is the ceiling-above query of a balanced BST, and reading backwards is a right to left sweep.