Given N flowers. One flower blooms per day. Array flowers[i]
gives the flower position (1-based) blooms at i + 1
th day. Find the first day where there are k not bloomed flowers between two flowers already bloomed.
[Optimal]Two Pointers/Sliding Window Solution
Put in another word, we need to find two flowers such that all the k flowers in between bloom in a later day.
The idea is to build another array days
where days[i] represents which day (1-based) a flower at position i + 1 bloom. Therefore, we can initialize a window [0, k + 1]. While we iterating the days, as long as days[i] is greater than days[left] and days[right], meaning it blooms after two boundaries and can be in between, we can continue. However, if days[i] < days[left] or days[i] < days[right], meaning we found a middle flower that blooms earlier. It cannot in the window. So we shift the window such that left = i and right = left + k + 1. If all days between left and right are later, we found a solution.
Time: O(n)
Space: O(n), extra space to store days each flower blooms.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Solution { | |
/** | |
* @param flowers: | |
* the place where the flower will open in that day | |
* @param k: | |
* an integer | |
* @return: in which day meet the requirements | |
*/ | |
public int kEmptySlots(int[] flowers, int k) { | |
int n = flowers.length; | |
int[] pos = new int[n]; | |
for (int i = 0; i < n; i++) { | |
int day = i + 1; | |
int idx = flowers[i] – 1; | |
pos[idx] = day; | |
} | |
int l = 0, r = k + 1; | |
int result = n + 1; | |
while (r < n) { | |
int i = l + 1; | |
while (i < r && pos[i] > pos[l] && pos[i] > pos[r]) { | |
i++; | |
} | |
if (i == r) // !!! Do not return here. This is *one* solution, but may not be the first day !!! | |
result = Math.min(result, Math.max(pos[l], pos[r])); | |
l = i; | |
r = i + k + 1; | |
} | |
return result == n + 1 ? -1 : result; | |
} | |
} |
TreeSet Solution
The idea was to walk through the flowers array (positions) and insert positions into BST (simulate day-after-day). Meanwhile, for each in coming position pos
, check before (pos – k – 1) and after (pos + k + 1) whether those flowers already inserted. Leverage higher()/lower() methods from TreeSet. If set, meaning there’s a solution, return the current day. Because positions are inserted while the checking happens, so it implies the flowers in between haven’t bloom yet. Otherwise, insert current position into BST and continue.
Consider using TreeSet when need efficient ceiling/floor kind of operations.
Time: O(nlogn), higher/lower operations O(logn), in a loop perform at most n times;
Space: O(n), store all flower positions in BST.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.TreeSet; | |
class Solution { | |
public int kEmptySlots(int[] flowers, int k) { | |
// Store flower position in a tree set | |
// so we can quickly get the flower k distance apart | |
// (either before or after) very quickly | |
TreeSet<Integer> ts = new TreeSet<>(); | |
for (int i = 0; i < flowers.length; i++) { | |
int pos = flowers[i]; | |
// Check if right (i + k + 1) is set and nothing in between | |
// higher() already imply that nothing is in between, | |
// otherwise there will be something | |
Integer after = ts.higher(pos); | |
if (after != null && after == pos + k + 1) { | |
return i + 1; | |
} | |
// Check if left (i – k – 1) is set and nothing in between | |
// lower() already imply that nothing is in between | |
// otherwise there will be something | |
Integer before = ts.lower(pos); | |
if (before != null && before == pos – k – 1) { | |
return i + 1; | |
} | |
ts.add(pos); | |
} | |
// Cannot find a solution | |
return -1; | |
} | |
} |
Binary Index Tree Solution
This the the binary index tree solution. Let’s first brush up on BIT.
Binary Index Tree
Let’s say you have an array of integers, you can create a separate BIT to quickly find the sum between the beginning and any given index in O(logn) time. BIT index is 1-based. A node removes its last 1-bit gives its parent. 0110 –> 0100. Each node stores sum in segment (parent, child].
BIT has two methods:
- update(int val, int index)
- Insert an element to the BIT at given index.
- It also updates all nodes “covers” current node.
- Time: O(logn).
- index += index & (-index) to get next node to update. Keep shifting last 1 bit to the left, and clear other 1 bits along the way.
- read(int index)
- Read the prefix sum at given index;
- Time: O(logn).
- index -= index & (-index). Keep trace back through parents and add the sum.
The idea to use BIT in this problem is we going through day by day and insert value 1 and flower position into BIT. This forms a BIT for an arrays of 1s. (1 means bloom and 0 means not bloom). Meanwhile, after each insert at position i, first read prefix sum at i
and i + k + 1
from BIT. If diff is 1, then we found a solution, which is to its right. Otherwise, read prefix sum at i - k - 1
and i
. If diff is 1, then we found a solution to its left.
Time: O(nlogn), worst case insert each element in BIT.
Space: O(n), binary index tree.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Solution { | |
// Binary index tree is 1 indexed | |
private int[] bitree; | |
public int kEmptySlots(int[] flowers, int k) { | |
int len = flowers.length; | |
bitree = new int[len + 1]; | |
for (int i = 0; i < len; i++) { | |
update(1, flowers[i]); | |
// Compare the flower k distance apart to its right | |
if (flowers[i] + k + 1 <= len) { | |
if (read(flowers[i] + k + 1) – read(flowers[i]) == 1) { | |
return i + 1; | |
} | |
} | |
// Compare the flower k distance apart to its left | |
if (flowers[i] – k – 1 >= 1) { | |
if (read(flowers[i]) – read(flowers[i] – k – 1) == 1) { | |
return i + 1; | |
} | |
} | |
} | |
return -1; | |
} | |
/* | |
* Binary index tree update method. | |
* It adds given value to the given node and all | |
* the nodes that "cover" it. | |
*/ | |
private void update(int val, int index) { | |
while (index < bitree.length) { | |
bitree[index] += val; | |
index += index & (-index); | |
} | |
} | |
/* | |
* Binary index tree read method. | |
* It returns the prefix sum of a given index. | |
*/ | |
private int read(int index) { | |
int sum = 0; | |
while (index > 0) { | |
sum += bitree[index]; | |
index -= index & (-index); | |
} | |
return sum; | |
} | |
} |
K Consecutive Slots
This is a follow up question. Instead of finding two flowers with k empty slots in between, this problem is regarding find the last day where exact k consecutive flowers bloom.
The solution is very similar to the first problem using two pointers and sliding window. The idea is to maintain a sliding window [l, r] where [l + 1, r – 1] all bloom in earlier days than [l] and [r]. Therefore, need to build an array “days” where days[i] means at position i, which position flower bloom. Note that “sliding window” needs to work on physical positions, thus need to convert from temporal array to spatial array.
Because the consecutive blooms can at the edge. Add one padding on each end of days array can simplify the processing. Initialize with Integer.MAX_VALUE pretend never bloom.
Time: O(n), sliding window move one direction.
Space: O(n), extra space for days array.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Solution { | |
public int kEmptySlots(int[] flowers, int k) { | |
// Build a days array to store which day a position flower bloom | |
// Create an extra slot at the beginning and end make it easy to | |
// process | |
int[] days = new int[flowers.length + 2]; | |
days[0] = Integer.MAX_VALUE; | |
days[days.length – 1] = Integer.MAX_VALUE; | |
for (int i = 0; i < flowers.length; i++) { | |
int day = i + 1; | |
int pos = flowers[i]; | |
days[pos] = day; | |
} | |
// Two pointers of the window | |
// Our goal is to find [l + 1, r – 1] that all bloom | |
// but l and r do not not bloom. | |
// a.k.a [l + 1, r – 1] must have smaller days than l and r. | |
int l = 0; | |
int r = k + 1; | |
for (int i = 1; i < days.length; i++) { | |
if (i == r || i == days.length – 1) { | |
// Found a solution, go through the days in the window | |
// and find the largest day | |
int max = Integer.MIN_VALUE; | |
for (int j = l + 1; j < i; j++) { | |
max = Math.max(max, days[j]); | |
} | |
return max; | |
} else { | |
if (days[i] >= days[l] || days[i] >= days[r]) { | |
// Found flower i in the window that bloom later than two ends | |
// thus flower i cannot be in the window | |
// Advance left pointer to i | |
l = i; | |
r = l + k + 1; | |
if (r >= days.length) { | |
// Cannot find a solution | |
break; | |
} | |
} | |
} | |
} | |
return -1; | |
} | |
} |