Take every k-th element from the (1 .. n) natural numbers series - algorithm

For example, we have series 1, 2, 3, 4, 5. We take every 3 element =>
3, 1, 5, 2, 4 (chosen element shouldn't remain, we can take while series is not empty). Naive implementation by circle doubly linked list is not good idea cause of performance. Can you give me an advice which data structures and algorithms are more applicable?

Build a complete binary tree containing the numbers 1 to n, e.g. for n=15 that would be:
In each branch, store the number of nodes to the left of it; this will allow us to quickly find the i-th node. (You'll see that this tree has a very predictable structure and values, and generating it is much more efficient than building a same-sized binary tree with randomly-ordered values. It's also an ideal candidate for a tree-in-an-array.)
Then, to find the i-th number, start at the root node, and at every node, if i is one greater than the number of nodes to the left, you've found the i-th number, else go left (if i is not greater than the number of nodes to the left) or right (if i is more than 1 greater than the number of nodes to the left).
Whenever you go left, decrement the count of nodes to the left of this node (because we'll be removing one).
Whenever you go right, decrease the number you're looking for by the number of nodes to the left of the node, plus 1 (or plus 0 if the value in the node has been erased).
When you've found the i-th node, read its value (to add to the removal order list) and then set its value to 0. Thereafter, if the i-th node we're looking for has had its value erased, we'll go right and then take the leftmost node.
We start with a value i = k, and then every time we've erased the number in the i-th node, we'll decrement the total number of nodes and set i = (i + k - 1) % total (or if that is zero: i = total).
This gives a log2N lookup time and a total complexity of N×LogN.
Example walk-through: with n=15 (as in the image above) and k=6, the first steps are 6, 12, 3, 10, 2. At that point the situation is:
We've just removed the second number, and now i = 2 + 6 - 1 = 7. We start at the root node, which has 4 nodes to the left of it and still has its value, so we go right and subtract 5 from the 7 we're looking for and get 2. We arrive at node 12 (which has been erased) and find there are 2 nodes to the left of it, so we decrement the number of nodes to the left of it and then go left. We come to node 10 (which has been erased) and find that it has 1 node to the left of it, and 1 = 2 - 1 so this is the node we're looking for; however, since its value has been erased, we go right and subtract 1 from the 2 we're looking for and get 1. We arrive at node 11, which has 0 nodes to the left of it (because it's a leaf), and 0 = 1 - 1, so this is the node we're looking for.
We then decrement the total number of nodes from 10 to 9, and update i from 7 to (7 + 6 - 1) % 9 = 3 and go on to find the third node (which is now the one with value 5).
Here's a simple implementation in JavaScript. It solves series of 100,000 numbers in less than a second, and it could probably be made faster and more space-efficient by using a tree-in-an-array structure.
(Unlike in the explanation above, the indexes of the numbers are zero-based, to simplify the code; so index 0 is the first number in the tree, and we look for the node with a number of left-connected children that equals the target index.)
function Tree(size) { // CONSTRUCTOR
var height = Math.floor(Math.log(size) / Math.log(2));
this.root = addNode(height, 1 << height, size);
this.size = size;
function addNode(height, value, max) { // RECURSIVE TREE-BUILDER
var node = {value: value > max ? 0 : value, lower: (1 << height) - 1};
if (height--) {
node.left = addNode(height, value - (1 << height), max);
if (value < max) { // DON'T ADD UNNECESSARY RIGHT NODES
node.right = addNode(height, value + (1 << height), max);
}
}
return node;
}
}
Tree.prototype.cut = function(step) { // SEE ANSWER FOR DETAILS
var sequence = [], index = (step - 1) % this.size;
while (this.size) {
var node = this.root, target = index;
while (node.lower != target || node.value == 0) {
if (target < node.lower) {
--node.lower;
node = node.left;
} else {
target -= node.lower + (node.value ? 1 : 0);
node = node.right;
}
}
sequence.push(node.value);
node.value = 0;
index = (index + step - 1) % --this.size;
}
return sequence;
}
var tree = new Tree(15);
var sequence = tree.cut(6);
document.write("15/6→" + sequence + "<BR>");
tree = new Tree(100000);
sequence = tree.cut(123456);
document.write("100000/123456→" + sequence);
NOTE:
If you look at the tree for n=10, you'll see that the node to the right of the root has an incomplete tree with 2 nodes to its left, but the algorithm as implemented in the code example above gives it an incorrect left-node count of 3 instead of 2:
However, nodes with an incomplete tree to their left never hold a value themselves, and never have nodes to their right. So you always go left there anyway, and the fact that their left-node count is too high is of no consequence.

If you just need the last number, it's known as Josephus problem and there're well-known formulas for computing the answer in O(N) time.
I don't know if one can adapt it to run a full simulation, so I'll describe a straightforward O(N log N) solution here:
Let's keep all numbers in a treap with implicit keys. We need to find the k-th element and delete it at each step (in fact, there can be a shift, so it's more like (cur_shift + k) % cur_size, but it doesn't really matter). A treap can do it. We just need to split it into 3 parts [0, k - 1], [k, k] and [k + 1, cur_size - 1], print the number in the node that corresponds to the second part and merge the first and last part back together. It requires O(log N) time per step, so it should be good enough for the given constraints.

Here is an implementation with an array representation of the binary tree, only storing the size of the left sub-tree as node value. The input array is not actually stored, but silently assumed to be the leaves at the bottom level, below the binary tree:
function josephusPermutation(size, step) {
var len = 1 << 32 - Math.clz32(size-1), // Smallest power of 2 >= size
tree = Array(len).fill(0), // Create tree in array representation
current = 0,
skip = step - 1,
result = Array(size).fill(0),
goRight, leftSize, order, i, j;
// Initialise tree with sizes of left subtrees as node values
(function init(i) {
if (i >= len) return +(i - len < size); // Only count when within size
var left = tree[i] = init(i*2); // recursive, only store left-size
return left + (left ? init(i*2+1) : 0); // return sum of left and right
})(1);
for (j = 0; j < result.length; j++, size--) {
current = (current + skip) % size; // keep within range
order = current;
for (i = 1; i < len; i = i*2+goRight) {
leftSize = tree[i];
goRight = order >= leftSize;
if (goRight) {
order -= leftSize; // Moving rightward, counting what is at left side.
} else {
tree[i]--; // we will remove value at left side
}
}
result[j] = 1 + i - len;
}
return result;
}
var sequence = josephusPermutation(100000, 123456);
console.log(sequence.join(','));

Below is an implementation of Lei Wang and Xiaodong Wang's (2013) 1 O(n log k) algorithm (very similar to, if not based on, the algorithm by Errol Lloyd, published in 1983). The idea is to divide the original sequence into n/m binary trees of height log k. The algorithm is actually designed for the "feline" Josephus problem, where the participants can have more than one life (listed in the array variable below, global.l).
I also like the O(1) space algorithms by Knuth, Ahrens, and Kaplansky, (outlined in a master's thesis by Gregory Wilson, California State University, Hayward, 19792), which take a longer time to process, although can be quite fast depending on the parameters.
Knuth’s algorithm for J(n,d,t) (t is the ith hit), a descending sequence:
Let x1 = d * t and for k = 2,3,...,
let x_k = ⌊(d * x_(k−1) − d * n − 1) / (d − 1)⌋
Then J(n,d,t) = x_p where x_p is the first term in the sequence <= n.
Ahrens’ algorithm for J(n,d,t), an ascending sequence:
Let a1 = 1 and for k = 2,3,...
let a_k = ⌈(n − t + a_(k−1)) * d / (d − 1)⌉
If a_r is the first term in the sequence such that a_r + 1 ≥ d * t + 1
then J(n,d,t) = d * t + 1 − a_r.
Kaplansky’s algorithm for J(n,d,t):
Let Z+ be the set of positive integers and for k =1,2,...,t
define a mapping P_k : Z+ → Z+ by P_k(m) = (m+d−1)−(n−k+1)(m−k+d−1)/(n−k+1)
Then, J(n,d,t) = P1 ◦ P2 ◦···◦Pt(t).
JavaScript code:
var global = {
n: 100000,
k: 123456,
l: new Array(5).fill(1),
m: null,
b: null,
a: [],
next: [],
prev: [],
i: 0,
limit: 5,
r: null,
t: null
}
function init(params){
global.m = Math.pow(2, Math.ceil(Math.log2(params.k)));
params.b = Math.ceil(params.n / global.m);
for (let i=0; i<params.b; i++){
let s = i * global.m,
t = (i + 1) * global.m,
u = [];
for (let j=0; j<global.m; j++)
u[j] = 0;
for (let j=s; j<=Math.min(t-1,params.n-1); j++)
u[j-s] = -(j + 1);
global.a[i] = [];
build(u, global.a[i]);
t = (i + 1) % params.b;
params.next[i] = t;
params.prev[t] = i;
}
}
function build(u,v){
function count(_v, i){
if (global.m < i + 2){
if (_v[i] < 0)
return 1;
else
return 0;
} else {
_v[i] = count(_v, 2*i + 1);
_v[i] = _v[i] + count(_v, 2*i + 2);
return _v[i];
}
}
for (let i=0; i<global.m; i++)
v[global.m + i - 1] = u[i];
count(v, 0);
}
function algorithmL(n, b){
global.r = 0;
global.t = b - 1;
while (global.i < global.limit){
tree(global, global);
let j = leaf(global, global);
hit(global.i,j,global,global);
global.i = global.i + 1;
}
}
function tree(params_r,params_t){
if (params_t.t === global.next[params_t.t] && params_r.r < global.k){
params_r.r = global.k + global.a[params_t.t][0] - 1 - (global.k - params_r.r - 1) % global.a[params_t.t][0];
} else {
while (params_r.r < global.k){
params_t.t = global.next[params_t.t];
params_r.r = params_r.r + global.a[params_t.t][0];
}
}
}
function size(t,j){
if (global.a[t][j] < 0)
return 1
return global.a[t][j];
}
function leaf(params_r,params_t){
let j = 0,
nxt = params_r.r - global.k;
while (j + 1 < global.m){
let rs = size(params_t.t, 2*j + 2);
if (params_r.r - rs < global.k){
j = 2*j + 2;
} else {
j = 2*j + 1;
params_r.r = params_r.r - rs;
}
}
params_r.r = nxt;
return j;
}
function hit(i,j,params_r,params_t){
let h = -global.a[params_t.t][j];
console.log(h);
if (global.l[h-1] > 1)
global.l[h-1] = global.l[h-1] - 1;
else
kill(i,j,params_r,params_t);
}
function kill(i,j,params_r,params_t){
global.a[params_t.t][j] = 0;
while (j > 0){
j = Math.floor((j - 1) / 2);
global.a[params_t.t][j] = global.a[params_t.t][j] - 1;
}
if (params_t.t !== global.next[params_t.t]){
if (global.a[params_t.t][0] + global.a[global.next[params_t.t]][0] === global.m){
params_r.r = params_r.r + global.a[global.next[params_t.t]][0];
combine(params_t);
} else if (global.a[params_t.t][0] + global.a[global.prev[params_t.t]][0] === global.m){
t = global.prev[params_t.t];
combine(params_t);
}
}
}
function combine(params_t){
let x = global.next[params_t.t],
i = 0,
u = [];
for (let j=0; j<global.m; j++)
if (global.a[params_t.t][global.m + j - 1] < 0){
u[i] = global.a[params_t.t][global.m + j - 1];
i = i + 1;
}
for (let j=0; j<global.m; j++)
if (global.a[x][global.m + j - 1] < 0){
u[i] = global.a[x][global.m + j - 1];
i = i + 1;
}
build(u,global.a[params_t.t]);
global.next[params_t.t] = global.next[global.next[params_t.t]];
global.prev[global.next[params_t.t]] = params_t.t;
}
init(global);
algorithmL(global.n, global.b);
(1) L. Wang and X. Wang. A Comparative Study on the Algorithms for a Generalized Josephus Problem. Applied Mathematics & Information Sciences, 7, No. 4, 1451-1457 (2013).
(2) References from Wilson (1979):
Knuth, D. E., The Art of Computer Programming, Addison-Wesley, Reading Mass., Vol I Fundamental Algorithms, 1968, Ex. 22, p158; Vol. III, Sorting and Searching, Ex. 2, pp. 18-19; Vol. I, 2-nd ed., p.181.
Ahrens, W., Mathematische Unterhaltungen und Spiele, Teubner: Leipzig, 1901, Chapter 15, 286-301.
Kaplansky, I. and Herstein I.N., Matters Mathematical, Chelsea, New York, 1978, pp. 121-128.

Related

Max Product of a string that requires K multiplication operators to be inserted

Maximum Product.
The input to the problem is a string Z = z1,z2.....zn where each zi is any number between 1...9 and an integer k where 0 <= k < n.
An example string is Z = 8473817, which is of length n = 7. We want to insert k multiplication operators X into the string so that the mathematical result of the expression
is the largest possible. There are n - 1 possible locations for the operators,
namely, after the ith character where i = 1,....., n - 1.
For example, for input Z = 21322 and k = 2, then one possible way to insert the X operators
is: 2 X 1 X 322 = 644, another possibility is 21 X 3 X 22 = 1386.
Design a dynamic programming to output the maximum product
obtainable from inserting exactly k multiplication operators X into the string.
You can assume that all the multiplication operations in your algorithm take
O(1) time.
I am approaching this using the Matrix Chain Multiplication method where you compute smaller subproblem along the upper diagonal.
This works when K=1 i.e. one multiplication operator is inserted.
In the picture below, I have used 8473817 as an example and shown that 8473 X 817 yields the highest product.
How do I scale this solution for K > 1 and K < N.
Update: adding a pseudo code.
let A(i,j) store the max product for the strings A(i...j) 1 < i < j < n
for i = 1 -> n:
A(i,i) = Z(i)
for s = 1 -> n-1:
for i = 1 -> n-s:
j = i + s
A(i,j) = 0
for l = i -> j-1:
A(i,j) = max (A(i,j), A(i,l) * A(l+1,j)
return A(1,n)
The above code works when k = 1. How do I scale this up when k > 1 and less than n
Update
Based on #trincot solution, I revamped the soln to not use memoization
Sub problem Defn
Let T(i) store the start offset where inserting the X operator in Z yields max value for i : 1 < i < k.
Pseudo code
`
T(0) = 0
for i = 1 -> k:
max = 0
for j = T(i-1) + 1 -> n:
result = Z[1..j] * Z[j+1..n]
if result > max
max = result
T(i) = j
val = 1
for i = 1 -> k:
val = val * Z[T(i-1)+1...T(i)]
val = val * Z[T(k)+1..n]
Your pseudo code is a dynamic programming solution where you use memoization for every possible slice of z (2 dimensions, starting and ending offset). However, you would only need to memoize the best result for any suffix of z, so you would only need one (starting) offset. A second dimension in your memoization would then be used for the value of k (the number of remaining multiplications).
So you would still need a 2-dimensional table for memoization, but one index would be for k and the other for an offset in z.
Here is an implementation in JavaScript:
function solve(z, k) {
// Initialise a kxl array (where l is the length of z), filled with zeroes.
const memo = Array.from({length: k + 1}, () => Array(z.length + 1).fill(0));
function recur(z, k) {
if (k == 0) return z;
let result = memo[k][z.length];
if (result == 0) {
for (let i = 1; i <= z.length - k; i++) {
result = Math.max(result, +z.slice(0, i) * recur(z.slice(i), k - 1));
}
memo[k][z.length] = result;
}
return result;
}
return recur(z, k);
}
// A few example runs:
console.log(solve('8473817', 1)); // 6922441
console.log(solve('21322', 2)); // 1368
console.log(solve('191111', 2)); // 10101
Bottom up
The same can be done in an iterative algorithm -- bottom-up instead of top-down. Here we can save one dimension of the memoization array, as the same array can be re-used for the next value of k as it increases from 0 to its final value:
function solve(z, k) {
const memo = Array(z.length);
// Initialise for k=0:
// the best product in a suffix is the suffix itself
for (let i = 0; i < z.length; i++) {
memo[i] = +z.slice(i);
}
for (let kk = 1; kk <= k; kk++) {
for (let i = 0; i < z.length - kk; i++) {
// find best position for multiplication
let result = 0;
for (let j = i + 1; j < z.length - kk + 1; j++) {
result = Math.max(result, +z.slice(i, j) * memo[j]);
}
memo[i] = result;
}
}
return memo[0];
}
// A few example runs:
console.log(solve('8473817', 1)); // 6922441
console.log(solve('21322', 2)); // 1368
console.log(solve('191111', 2)); // 10101
(Code not supplied because this is homework.)
You have found that you can use the method once and get a solution for k=1.
Can you do it and find the best solution ending at every position in the string?
Now can you use the output of that second generalization and a similar method to get a complete solution for k=2?
Now can you write this a loop to solve for arbitrary k?
If you can do all that, then finishing is easy.
You have n-1 positions and k operators to insert. To me that looks like a binary number with n-1 bits including k 1's and the other positions set to 0.
Systematically generate all permutations of [0..01..1], insert multiplication operators at the 1 positions and calculate the result for each permutation.

Minimize total area using K rectangles in less than O(N^4)

Given an increasing sequence of N numbers (up to T), we can use at most K rectangles (placed starting at position 0) such as for the i-th value v in the sequence, exists a rectangle in positions [v, T) with height at least i + 1.
Total area of rectangles should be the minimum that satisfies what mentioned above.
Example: given the sequence [0, 3, 4], T = 5 and K = 2 we can use:
a rectangle from 0 to 2 with height 1 (thus having an area of 3)
a rectangle from 3 to 4 with height 3 (thus having an area of 6).
Using at most 2 rectangles, we cannot get a total area smaller than 9.
This problem can be solved using DP.
int dp[MAXK+1][MAXN][MAXN];
int sequence[MAXN];
int filldp(int cur_idx, int cur_value, int cur_K) {
int res = dp[cur_K][cur_idx][cur_value];
if (res != -1) return res;
res = INF;
if (cur_idx == N - 1 && cur_value >= N)
res = min(res, (T - seq[cur_idx]) * cur_value);
else {
if (cur_idx < N - 1 && cur_value >= cur_idx + 1) {
int cur_cost = (seq[cur_idx + 1] - seq[cur_idx]) * cur_value;
res = min(res, cur_cost + filldp(cur_idx + 1, cur_value, cur_K);
}
// Try every possible height for a rectangle
if (cur_K < K)
for (int new_value = cur_value + 1; cur_value <= N; new_value++)
res = min(res, filldp(cur_idx, new_value, cur_K + 1));
}
dp[cur_K][cur_idx][cur_value] = res;
return res;
}
Unsurprisingly, this DP approach is not really fast probably due to the for cycle. However, as far as I can understand, this code should not do more than MAXK * MAXN * MAXN significative calls (i.e., not more that every cell in dp). MAXK and MAXN are both 200, so dp has 8 millions of cells, which is not too much.
Am I missing anything?
UPDATE: As pointed out by Saeed Amiri (thank you!), the code makes N^2*K significative calls, but each one is O(N). The whole algorithm is then O(N^3*K) = O(N^4).
Can we do better?

Water collected between towers

I recently came across an interview question asked by Amazon and I am not able to find an optimized algorithm to solve this question:
You are given an input array whose each element represents the height of a line towers. The width of every tower is 1. It starts raining. How much water is collected between the towers?
Example
Input: [1,5,3,7,2] , Output: 2 units
Explanation: 2 units of water collected between towers of height 5 and 7
*
*
*w*
*w*
***
****
*****
Another Example
Input: [5,3,7,2,6,4,5,9,1,2] , Output: 14 units
Explanation= 2 units of water collected between towers of height 5 and 7 +
4 units of water collected between towers of height 7 and 6 +
1 units of water collected between towers of height 6 and 5 +
2 units of water collected between towers of height 6 and 9 +
4 units of water collected between towers of height 7 and 9 +
1 units of water collected between towers of height 9 and 2.
At first I thought this could be solved by Stock-Span Problem (http://www.geeksforgeeks.org/the-stock-span-problem/) but I was wrong so it would be great if anyone can think of a time-optimized algorithm for this question.
Once the water's done falling, each position will fill to a level equal to the smaller of the highest tower to the left and the highest tower to the right.
Find, by a rightward scan, the highest tower to the left of each position. Then find, by a leftward scan, the highest tower to the right of each position. Then take the minimum at each position and add them all up.
Something like this ought to work:
int tow[N]; // nonnegative tower heights
int hl[N] = {0}, hr[N] = {0}; // highest-left and highest-right
for (int i = 0; i < n; i++) hl[i] = max(tow[i], (i!=0)?hl[i-1]:0);
for (int i = n-1; i >= 0; i--) hr[i] = max(tow[i],i<(n-1) ? hr[i+1]:0);
int ans = 0;
for (int i = 0; i < n; i++) ans += min(hl[i], hr[i]) - tow[i];
Here's an efficient solution in Haskell
rainfall :: [Int] -> Int
rainfall xs = sum (zipWith (-) mins xs)
where mins = zipWith min maxl maxr
maxl = scanl1 max xs
maxr = scanr1 max xs
it uses the same two-pass scan algorithm mentioned in the other answers.
Refer this website for code, its really plain and simple
http://learningarsenal.info/index.php/2015/08/21/amount-of-rain-water-collected-between-towers/
Input: [5,3,7,2,6,4,5,9,1,2] , Output: 14 units
Explanation
Each tower can hold water upto a level of smallest height between heighest tower to left, and highest tower to the right.
Thus we need to calculate highest tower to left on each and every tower, and likewise for the right side.
Here we will be needing two extra arrays for holding height of highest tower to left on any tower say, int leftMax[] and likewise for right side say int rightMax[].
STEP-1
We make a left pass of the given array(i.e int tower[]),and will be maintaining a temporary maximum(say int tempMax) such that on each iteration height of each tower will be compared to tempMax, and if height of current tower is less than tempMax then tempMax will be set as highest tower to left of it, otherwise height of current tower will be assigned as the heighest tower to left and tempMax will be updated with current tower height,
STEP-2
We will be following above procedure only as discussed in STEP-1 to calculate highest tower to right BUT this times making a pass through array from right side.
STEP-3
The amount of water which each tower can hold is-
(minimum height between highest right tower and highest left tower) – (height of tower)
You can do this by scanning the array twice.
The first time you scan from top to bottom and store the value of the tallest tower you have yet to encounter when reaching each row.
You then repeat the process, but in reverse. You start from the bottom and work towards the top of the array. You keep track of the tallest tower you have seen so far and compare the height of it to the value for that tower in the other result set.
Take the difference between the lesser of these two values (the shortest of the tallest two towers surrounding the current tower, subtract the height of the tower and add that amount to the total amount of water.
int maxValue = 0;
int total = 0;
int[n] lookAhead
for(i=0;i<n;i++)
{
if(input[i] > maxValue) maxValue = input[i];
lookahead[i] = maxValue;
}
maxValue = 0;
for(i=n-1;i>=0;i--)
{
// If the input is greater than or equal to the max, all water escapes.
if(input[i] >= maxValue)
{
maxValue = input[i];
}
else
{
if(maxValue > lookAhead[i])
{
// Make sure we don't run off the other side.
if(lookAhead[i] > input[i])
{
total += lookAhead[i] - input[i];
}
}
else
{
total += maxValue - input[i];
}
}
}
Readable Python Solution:
def water_collected(heights):
water_collected = 0
left_height = []
right_height = []
temp_max = heights[0]
for height in heights:
if (height > temp_max):
temp_max = height
left_height.append(temp_max)
temp_max = heights[-1]
for height in reversed(heights):
if (height > temp_max):
temp_max = height
right_height.insert(0, temp_max)
for i, height in enumerate(heights):
water_collected += min(left_height[i], right_height[i]) - height
return water_collected
O(n) solution in Java, single pass
Another implementation in Java, finding the water collected in a single pass through the list. I scanned the other answers but didn't see any that were obviously using my solution.
Find the first "peak" by looping through the list until the tower height stops increasing. All water before this will not be collected (drain off to the left).
For all subsequent towers:
If the height of the subsequent tower decreases or stays the same, add water to a "potential collection" bucket, equal to the difference between the tower height and the previous max tower height.
If the height of the subsequent tower increases, we collect water from the previous bucket (subtract from the "potential collection" bucket and add to the collected bucket) and also add water to the potential bucket equal to the difference between the tower height and the previous max tower height.
If we find a new max tower, then all the "potential water" is moved into the collected bucket and this becomes the new max tower height.
In the example above, with input: [5,3,7,2,6,4,5,9,1,2], the solution works as follows:
5: Finds 5 as the first peak
3: Adds 2 to the potential bucket (5-3) collected = 0, potential = 2
7: New max, moves all potential water to the collected bucket collected = 2, potential = 0
2: Adds 5 to the potential bucket (7-2) collected = 2, potential = 5
6: Moves 4 to the collected bucket and adds 1 to the potential bucket (6-2, 7-6) collected = 6, potential = 2
4: Adds 2 to the potential bucket (6-4) collected = 6, potential = 4
5: Moves 1 to the collected bucket and adds 2 to the potential bucket (5-4, 7-5) collected = 7, potential = 6
9: New max, moves all potential water to the collected bucket collected = 13, potential = 0
1: Adds 8 to the potential bucket (9-1) collected = 13, potential = 8
2: Moves 1 to the collected bucket and adds 7 to the potential bucket (2-1, 9-2) collected = 14, potential = 15
After running through the list once, collected water has been measured.
public static int answer(int[] list) {
int maxHeight = 0;
int previousHeight = 0;
int previousHeightIndex = 0;
int coll = 0;
int temp = 0;
// find the first peak (all water before will not be collected)
while(list[previousHeightIndex] > maxHeight) {
maxHeight = list[previousHeightIndex];
previousHeightIndex++;
if(previousHeightIndex==list.length) // in case of stairs (no water collected)
return coll;
else
previousHeight = list[previousHeightIndex];
}
for(int i = previousHeightIndex; i<list.length; i++) {
if(list[i] >= maxHeight) { // collect all temp water
coll += temp;
temp = 0;
maxHeight = list[i]; // new max height
}
else {
temp += maxHeight - list[i];
if(list[i] > previousHeight) { // we went up... collect some water
int collWater = (i-previousHeightIndex)*(list[i]-previousHeight);
coll += collWater;
temp -= collWater;
}
}
// previousHeight only changes if consecutive towers are not same height
if(list[i] != previousHeight) {
previousHeight = list[i];
previousHeightIndex = i;
}
}
return coll;
}
None of the 17 answers already posted are really time-optimal.
For a single processor, a 2 sweep (left->right, followed by a right->left summation) is optimal, as many people have pointed out, but using many processors, it is possible to complete this task in O(log n) time. There are many ways to do this, so I'll explain one that is fairly close to the sequential algorithm.
Max-cached tree O(log n)
1: Create a binary tree of all towers such that each node contains the height of the highest tower in any of its children. Since the two leaves of any node can be computed independently, this can be done in O(log n) time with n cpu's. (Each value is handled by its own cpu, and they build the tree by repeatedly merging two existing values. All parallel branches can be executed in parallel. Thus, it's O(log2(n)) for a 2-way merge function (max, in this case)).
2a: Then, for each node in the tree, starting at the root, let the right leaf have the value max(left, self, right). This will create the left-to-right monotonic sweep in O(log n) time, using n cpu's.
2b: To compute the right-to-left sweep, we do the same procedure as before. Starting with root of the max-cached tree, let the left leaf have the value max(left, self, right). These left-to-right (2a) and right-to-left (2b) sweeps can be done in parallel if you'd like to. They both use the max-cached tree as input, and generate one new tree each (or sets their own fields in original tree, if you prefer that).
3: Then, for each tower, the amount of water on it is min(ltr, rtl) - towerHeight, where ltr is the value for that tower in the left-to-right monotonic sweep we did before, i.e. the maximum height of any tower to the left of us (including ourselves1), and rtl is the same for the right-to-left sweep.
4: Simply sum this up using a tree in O(log n) time using n cpu's, and we're done.
1 If the current tower is taller than all towers to the left of us, or taller than all towers to the the right of us, min(ltr, rtl) - towerHeight is zero.
Here's two other ways to do it.
Here is a solution in Groovy in two passes.
assert waterCollected([1, 5, 3, 7, 2]) == 2
assert waterCollected([5, 3, 7, 2, 6, 4, 5, 9, 1, 2]) == 14
assert waterCollected([5, 5, 5, 5]) == 0
assert waterCollected([5, 6, 7, 8]) == 0
assert waterCollected([8, 7, 7, 6]) == 0
assert waterCollected([6, 7, 10, 7, 6]) == 0
def waterCollected(towers) {
int size = towers.size()
if (size < 3) return 0
int left = towers[0]
int right = towers[towers.size() - 1]
def highestToTheLeft = []
def highestToTheRight = [null] * size
for (int i = 1; i < size; i++) {
// Track highest tower to the left
if (towers[i] < left) {
highestToTheLeft[i] = left
} else {
left = towers[i]
}
// Track highest tower to the right
if (towers[size - 1 - i] < right) {
highestToTheRight[size - 1 - i] = right
} else {
right = towers[size - 1 - i]
}
}
int water = 0
for (int i = 0; i < size; i++) {
if (highestToTheLeft[i] && highestToTheRight[i]) {
int minHighest = highestToTheLeft[i] < highestToTheRight[i] ? highestToTheLeft[i] : highestToTheRight[i]
water += minHighest - towers[i]
}
}
return water
}
Here same snippet with an online compiler:
https://groovy-playground.appspot.com/#?load=3b1d964bfd66dc623c89
You can traverse first from left to right, and calculate the water accumulated for the cases where there is a smaller building on the left and a larger one on the right. You would have to subtract the area of the buildings that are in between these two buildings and are smaller than the left one.
Similar would be the case for right to left.
Here is the code for left to right. I have uploaded this problem on leetcode online judge using this approach.
I find this approach much more intuitive than the standard solution which is present everywhere (calculating the largest building on the right and the left for each i ).
int sum=0, finalAns=0;
idx=0;
while(a[idx]==0 && idx < n)
idx++;
for(int i=idx+1;i<n;i++){
while(a[i] < a[idx] && i<n){
sum += a[i];
i++;
}
if(i==n)
break;
jdx=i;
int area = a[idx] * (jdx-idx-1);
area -= sum;
finalAns += area;
idx=jdx;
sum=0;
}
The time complexity of this approach is O(n), as you are traversing the array two time linearly.
Space complexity would be O(1).
The first and the last bars in the list cannot trap water. For the remaining towers, they can trap water when there are max heights to the left and to the right.
water accumulation is:
max( min(max_left, max_right) - current_height, 0 )
Iterating from the left, if we know that there is a max_right that is greater, min(max_left, max_right) will become just max_left. Therefore water accumulation is simplified as:
max(max_left - current_height, 0) Same pattern when considering from the right side.
From the info above, we can write a O(N) time and O(1) space algorithm as followings(in Python):
def trap_water(A):
water = 0
left, right = 1, len(A)-1
max_left, max_right = A[0], A[len(A)-1]
while left <= right:
if A[left] <= A[right]:
max_left = max(A[left], max_left)
water += max(max_left - A[left], 0)
left += 1
else:
max_right = max(A[right], max_right)
water += max(max_right - A[right], 0)
right -= 1
return water
/**
* #param {number[]} height
* #return {number}
*/
var trap = function(height) {
let maxLeftArray = [], maxRightArray = [];
let maxLeft = 0, maxRight = 0;
const ln = height.length;
let trappedWater = 0;
for(let i = 0;i < height.length; i ++) {
maxLeftArray[i] = Math.max(height[i], maxLeft);
maxLeft = maxLeftArray[i];
maxRightArray[ln - i - 1] = Math.max(height[ln - i - 1], maxRight);
maxRight = maxRightArray[ln - i - 1];
}
for(let i = 0;i < height.length; i ++) {
trappedWater += Math.min(maxLeftArray[i], maxRightArray[i]) - height[i];
}
return trappedWater;
};
var arr = [5,3,7,2,6,4,5,9,1,2];
console.log(trap(arr));
You could read the detailed explanation in my blogpost: trapping-rain-water
Here is one more solution written on Scala
def find(a: Array[Int]): Int = {
var count, left, right = 0
while (left < a.length - 1) {
right = a.length - 1
for (j <- a.length - 1 until left by -1) {
if (a(j) > a(right)) right = j
}
if (right - left > 1) {
for (k <- left + 1 until right) count += math.min(a(left), a(right)) - a(k)
left = right
} else left += 1
}
count
}
An alternative algorithm in the style of Euclid, which I consider more elegant than all this scanning is:
Set the two tallest towers as the left and right tower. The amount of
water contained between these towers is obvious.
Take the next tallest tower and add it. It must be either between the
end towers, or not. If it is between the end towers it displaces an
amount of water equal to the towers volume (thanks to Archimedes for
this hint). If it outside the end towers it becomes a new end tower
and the amount of additional water contained is obvious.
Repeat for the next tallest tower until all towers are added.
I've posted code to achieve this (in a modern Euclidean idiom) here: http://www.rosettacode.org/wiki/Water_collected_between_towers#F.23
I have a solution that only requires a single traversal from left to right.
def standing_water(heights):
if len(heights) < 3:
return 0
i = 0 # index used to iterate from left to right
w = 0 # accumulator for the total amount of water
while i < len(heights) - 1:
target = i + 1
for j in range(i + 1, len(heights)):
if heights[j] >= heights[i]:
target = j
break
if heights[j] > heights[target]:
target = j
if target == i:
return w
surface = min(heights[i], heights[target])
i += 1
while i < target:
w += surface - heights[i]
i += 1
return w
An intuitive solution for this problem is one in which you bound the problem and fill water based on the height of the left and right bounds.
My solution:
Begin at the left, setting both bounds to be the 0th index.
Check and see if there is some kind of a trajectory (If you were to
walk on top of these towers, would you ever go down and then back up
again?) If that is the case, then you have found a right bound.
Now back track and fill the water accordingly (I simply added the
water to the array values themselves as it makes the code a little
cleaner, but this is obviously not required).
The punch line: If the left bounding tower height is greater than the
right bounding tower height than you need to increment the right
bound. The reason is because you might run into a higher tower and need to fill some more water.
However, if the right tower is higher than the left tower then no
more water can be added in your current sub-problem. Thus, you move
your left bound to the right bound and continue.
Here is an implementation in C#:
int[] towers = {1,5,3,7,2};
int currentMinimum = towers[0];
bool rightBoundFound = false;
int i = 0;
int leftBoundIndex = 0;
int rightBoundIndex = 0;
int waterAdded = 0;
while(i < towers.Length - 1)
{
currentMinimum = towers[i];
if(towers[i] < currentMinimum)
{
currentMinimum = towers[i];
}
if(towers[i + 1] > towers[i])
{
rightBoundFound = true;
rightBoundIndex = i + 1;
}
if (rightBoundFound)
{
for(int j = leftBoundIndex + 1; j < rightBoundIndex; j++)
{
int difference = 0;
if(towers[leftBoundIndex] < towers[rightBoundIndex])
{
difference = towers[leftBoundIndex] - towers[j];
}
else if(towers[leftBoundIndex] > towers[rightBoundIndex])
{
difference = towers[rightBoundIndex] - towers[j];
}
else
{
difference = towers[rightBoundIndex] - towers[j];
}
towers[j] += difference;
waterAdded += difference;
}
if (towers[leftBoundIndex] > towers[rightBoundIndex])
{
i = leftBoundIndex - 1;
}
else if (towers[rightBoundIndex] > towers[leftBoundIndex])
{
leftBoundIndex = rightBoundIndex;
i = rightBoundIndex - 1;
}
else
{
leftBoundIndex = rightBoundIndex;
i = rightBoundIndex - 1;
}
rightBoundFound = false;
}
i++;
}
I have no doubt that there are more optimal solutions. I am currently working on a single-pass optimization. There is also a very neat stack implementation of this problem, and it uses a similar idea of bounding.
Here is my solution, it passes this level and pretty fast, easy to understand
The idea is very simple: first, you figure out the maximum of the heights (it could be multiple maximum), then you chop the landscape into 3 parts, from the beginning to the left most maximum heights, between the left most max to the right most max, and from the right most max to the end.
In the middle part, it's easy to collect the rains, one for loop does that. Then for the first part, you keep on updating the current max height that is less than the max height of the landscape. one loop does that. Then for the third part, you reverse what you have done to the first part
def answer(heights):
sumL = 0
sumM = 0
sumR = 0
L = len(heights)
MV = max(heights)
FI = heights.index(MV)
LI = L - heights[::-1].index(MV) - 1
if LI-FI>1:
for i in range(FI+1,LI):
sumM = sumM + MV-heights[i]
if FI>0:
TM = heights[0]
for i in range(1,FI):
if heights[i]<= TM:
sumL = sumL + TM-heights[i]
else:
TM = heights[i]
if LI<(L-1):
TM = heights[-1]
for i in range(L-1,LI,-1):
if heights[i]<= TM:
sumL = sumL + TM-heights[i]
else:
TM = heights[i]
return(sumL+sumM+sumR)
Here is a solution in JAVA that traverses the list of numbers once. So the worst case time is O(n). (At least that's how I understand it).
For a given reference number keep looking for a number which is greater or equal to the reference number. Keep a count of numbers that was traversed in doing so and store all those numbers in a list.
The idea is this. If there are 5 numbers between 6 and 9, and all the five numbers are 0's, it means that a total of 30 units of water can be stored between 6 and 9. For a real situation where the numbers in between aren't 0's, we just deduct the total sum of the numbers in between from the total amount if those numbers were 0. (In this case, we deduct from 30). And that will give the count of water stored in between these two towers. We then save this amount in a variable called totalWaterRetained and then start from the next tower after 9 and keep doing the same till the last element.
Adding all the instances of totalWaterRetained will give us the final answer.
JAVA Solution: (Tested on a few inputs. Might be not 100% correct)
private static int solveLineTowerProblem(int[] inputArray) {
int totalWaterContained = 0;
int index;
int currentIndex = 0;
int countInBetween = 0;
List<Integer> integerList = new ArrayList<Integer>();
if (inputArray.length < 3) {
return totalWaterContained;
} else {
for (index = 1; index < inputArray.length - 1;) {
countInBetween = 0;
integerList.clear();
int tempIndex = index;
boolean flag = false;
while (inputArray[currentIndex] > inputArray[tempIndex] && tempIndex < inputArray.length - 1) {
integerList.add(inputArray[tempIndex]);
tempIndex++;
countInBetween++;
flag = true;
}
if (flag) {
integerList.add(inputArray[index + countInBetween]);
integerList.add(inputArray[index - 1]);
int differnceBetweenHighest = min(integerList.get(integerList.size() - 2),
integerList.get(integerList.size() - 1));
int totalCapacity = differnceBetweenHighest * countInBetween;
totalWaterContained += totalCapacity - sum(integerList);
}
index += countInBetween + 1;
currentIndex = index - 1;
}
}
return totalWaterContained;
}
Here is my take to the problem,
I use a loop to see if the previous towers is bigger than the actual one.
If it is then I create another loop to check if the towers coming after the actual one are bigger or equal to the previous tower.
If that's the case then I just add all the differences in height between the previous tower and all other towers.
If not and if my loop reaches my last object then I simply reverse the array so that the previous tower becomes my last tower and call my method recursively on it.
That way I'm certain to find a tower bigger than my new previous tower and will find the correct amount of water collected.
public class towers {
public static int waterLevel(int[] i) {
int totalLevel = 0;
for (int j = 1; j < i.length - 1; j++) {
if (i[j - 1] > i[j]) {
for (int k = j; k < i.length; k++) {
if (i[k] >= i[j - 1]) {
for (int l = j; l < k; l++) {
totalLevel += (i[j - 1] - i[l]);
}
j = k;
break;
}
if (k == i.length - 1) {
int[] copy = Arrays.copyOfRange(i, j - 1, k + 1);
int[] revcopy = reverse(copy);
totalLevel += waterLevel(revcopy);
}
}
}
}
return totalLevel;
}
public static int[] reverse(int[] i) {
for (int j = 0; j < i.length / 2; j++) {
int temp = i[j];
i[j] = i[i.length - j - 1];
i[i.length - j - 1] = temp;
}
return i;
}
public static void main(String[] args) {
System.out.println(waterLevel(new int[] {1, 6, 3, 2, 2, 6}));
}
}
Tested all the Java solution provided, but none of them passes even half of the test-cases I've come up with, so there is one more Java O(n) solution, with all possible cases covered. The algorithm is really simple:
1) Traverse the input from the beginning, searching for tower that is equal or higher that the given tower, while summing up possible amount of water for lower towers into temporary var.
2) Once the tower found - add that temporary var into main result var and shorten the input list.
3) If no more tower found then reverse the remaining input and calculate again.
public int calculate(List<Integer> input) {
int result = doCalculation(input);
Collections.reverse(input);
result += doCalculation(input);
return result;
}
private static int doCalculation(List<Integer> input) {
List<Integer> copy = new ArrayList<>(input);
int result = 0;
for (ListIterator<Integer> iterator = input.listIterator(); iterator.hasNext(); ) {
final int firstHill = iterator.next();
int tempResult = 0;
int lowerHillsSize = 0;
while (iterator.hasNext()) {
final int nextHill = iterator.next();
if (nextHill >= firstHill) {
iterator.previous();
result += tempResult;
copy = copy.subList(lowerHillsSize + 1, copy.size());
break;
} else {
tempResult += firstHill - nextHill;
lowerHillsSize++;
}
}
}
input.clear();
input.addAll(copy);
return result;
}
For the test cases, please, take a look at this test class.
Feel free to create a pull request if you find uncovered test cases)
This is a funny problem, I just got that question in an interview. LOL I broke my mind on that stupid problem, and found a solution which need one pass (but clearly non-continuous). (and in fact you even not loop over the entire data, as you bypass the boundary...)
So the idea is. You start from the side with the lowest tower (which is now the reference). You directly add the content of the towers, and if you reach a tower which is highest than the reference, you call the function recursively (with side to be reset). Not trivial to explain with words, the code speak for himself.
#include <iostream>
using namespace std;
int compute_water(int * array, int index_min, int index_max)
{
int water = 0;
int dir;
int start,end;
int steps = std::abs(index_max-index_min)-1;
int i,count;
if(steps>=1)
{
if(array[index_min]<array[index_max])
{
dir=1;
start = index_min;
end = index_max;
}
else
{
dir = -1;
start = index_max;
end = index_min;
}
for(i=start+dir,count=0;count<steps;i+=dir,count++)
{
if(array[i]<=array[start])water += array[start] - array[i];
else
{
if(i<end)water += compute_water(array, i, end);
else water += compute_water(array, end, i);
break;
}
}
}
return water;
}
int main(int argc,char ** argv)
{
int size = 0;
int * towers;
if(argc==1)
{
cout<< "Usage: "<<argv[0]<< "a list of tower height separated by spaces" <<endl;
}
else
{
size = argc - 1;
towers = (int*)malloc(size*sizeof(int));
for(int i = 0; i<size;i++)towers[i] = atoi(argv[i+1]);
cout<< "water collected: "<< compute_water(towers, 0, size-1)<<endl;
free(towers);
}
}
I wrote this relying on some of the ideas above in this thread:
def get_collected_rain(towers):
length = len(towers)
acummulated_water=[0]*length
left_max=[0]*length
right_max=[0]*length
for n in range(0,length):
#first left item
if n!=0:
left_max[n]=max(towers[:n])
#first right item
if n!=length-1:
right_max[n]=max(towers[n+1:length])
acummulated_water[n]=max(min(left_max[n], right_max[n]) - towers[n], 0)
return sum(acummulated_water)
Well ...
> print(get_collected_rain([9,8,7,8,9,5,6]))
> 5
Here's my attempt in jQuery. It only scans to the right.
Working fiddle (with helpful logging)
var a = [1, 5, 3, 7, 2];
var water = 0;
$.each(a, function (key, i) {
if (i > a[key + 1]) { //if next tower to right is bigger
for (j = 1; j <= a.length - key; j++) { //number of remaining towers to the right
if (a[key+1 + j] >= i) { //if any tower to the right is bigger
for (k = 1; k < 1+j; k++) {
//add to water: the difference of the first tower and each tower between the first tower and its bigger tower
water += a[key] - a[key+k];
}
}
}
}
});
console.log("Water: "+water);
Here's my go at it in Python. Pretty sure it works but haven't tested it.
Two passes through the list (but deleting the list as it finds 'water'):
def answer(heights):
def accWater(lst,sumwater=0):
x,takewater = 1,[]
while x < len(lst):
a,b = lst[x-1],lst[x]
if takewater:
if b < takewater[0]:
takewater.append(b)
x += 1
else:
sumwater += sum(takewater[0]- z for z in takewater)
del lst[:x]
x = 1
takewater = []
else:
if b < a:
takewater.extend([a,b])
x += 1
else:
x += 1
return [lst,sumwater]
heights, swater = accWater(heights)
x, allwater = accWater(heights[::-1],sumwater=swater)
return allwater
private static int soln1(int[] a)
{
int ret=0;
int l=a.length;
int st,en=0;
int h,i,j,k=0;
int sm;
for(h=0;h<l;h++)
{
for(i=1;i<l;i++)
{
if(a[i]<a[i-1])
{
st=i;
for(j=i;j<l-1;j++)
{
if(a[j]<=a[i] && a[j+1]>a[i])
{
en=j;
h=en;
break;
}
}
if(st<=en)
{
sm=a[st-1];
if(sm>a[en+1])
sm=a[en+1];
for(k=st;k<=en;k++)
{
ret+=sm-a[k];
a[k]=sm;
}
}
}
}
}
return ret;
}
/*** Theta(n) Time COmplexity ***/
static int trappingRainWater(int ar[],int n)
{
int res=0;
int lmaxArray[]=new int[n];
int rmaxArray[]=new int[n];
lmaxArray[0]=ar[0];
for(int j=1;j<n;j++)
{
lmaxArray[j]=Math.max(lmaxArray[j-1], ar[j]);
}
rmaxArray[n-1]=ar[n-1];
for(int j=n-2;j>=0;j--)
{
rmaxArray[j]=Math.max(rmaxArray[j+1], ar[j]);
}
for(int i=1;i<n-1;i++)
{
res=res+(Math.min(lmaxArray[i], rmaxArray[i])-ar[i]);
}
return res;
}

Codility Peaks Complexity

I've just done the following Codility Peaks problem. The problem is as follows:
A non-empty zero-indexed array A consisting of N integers is given.
A peak is an array element which is larger than its neighbors. More precisely, it is an index P such that 0 < P < N − 1, A[P − 1] < A[P] and A[P] > A[P + 1].
For example, the following array A:
A[0] = 1
A[1] = 2
A[2] = 3
A[3] = 4
A[4] = 3
A[5] = 4
A[6] = 1
A[7] = 2
A[8] = 3
A[9] = 4
A[10] = 6
A[11] = 2
has exactly three peaks: 3, 5, 10.
We want to divide this array into blocks containing the same number of elements. More precisely, we want to choose a number K that will yield the following blocks:
A[0], A[1], ..., A[K − 1],
A[K], A[K + 1], ..., A[2K − 1],
...
A[N − K], A[N − K + 1], ..., A[N − 1].
What's more, every block should contain at least one peak. Notice that extreme elements of the blocks (for example A[K − 1] or A[K]) can also be peaks, but only if they have both neighbors (including one in an adjacent blocks).
The goal is to find the maximum number of blocks into which the array A can be divided.
Array A can be divided into blocks as follows:
one block (1, 2, 3, 4, 3, 4, 1, 2, 3, 4, 6, 2). This block contains three peaks.
two blocks (1, 2, 3, 4, 3, 4) and (1, 2, 3, 4, 6, 2). Every block has a peak.
three blocks (1, 2, 3, 4), (3, 4, 1, 2), (3, 4, 6, 2). Every block has a peak.
Notice in particular that the first block (1, 2, 3, 4) has a peak at A[3], because A[2] < A[3] > A[4], even though A[4] is in the adjacent block.
However, array A cannot be divided into four blocks, (1, 2, 3), (4, 3, 4), (1, 2, 3) and (4, 6, 2), because the (1, 2, 3) blocks do not contain a peak. Notice in particular that the (4, 3, 4) block contains two peaks: A[3] and A[5].
The maximum number of blocks that array A can be divided into is three.
Write a function:
class Solution { public int solution(int[] A); }
that, given a non-empty zero-indexed array A consisting of N integers, returns the maximum number of blocks into which A can be divided.
If A cannot be divided into some number of blocks, the function should return 0.
For example, given:
A[0] = 1
A[1] = 2
A[2] = 3
A[3] = 4
A[4] = 3
A[5] = 4
A[6] = 1
A[7] = 2
A[8] = 3
A[9] = 4
A[10] = 6
A[11] = 2
the function should return 3, as explained above.
Assume that:
N is an integer within the range [1..100,000];
each element of array A is an integer within the range [0..1,000,000,000].
Complexity:
expected worst-case time complexity is O(N*log(log(N)))
expected worst-case space complexity is O(N), beyond input storage (not counting the storage required for input arguments).
Elements of input arrays can be modified.
My Question
So I solve this with what to me appears to be the brute force solution – go through every group size from 1..N, and check whether every group has at least one peak. The first 15 minutes I was trying to solve this I was trying to figure out some more optimal way, since the required complexity is O(N*log(log(N))).
This is my "brute-force" code that passes all the tests, including the large ones, for a score of 100/100:
public int solution(int[] A) {
int N = A.length;
ArrayList<Integer> peaks = new ArrayList<Integer>();
for(int i = 1; i < N-1; i++){
if(A[i] > A[i-1] && A[i] > A[i+1]) peaks.add(i);
}
for(int size = 1; size <= N; size++){
if(N % size != 0) continue;
int find = 0;
int groups = N/size;
boolean ok = true;
for(int peakIdx : peaks){
if(peakIdx/size > find){
ok = false;
break;
}
if(peakIdx/size == find) find++;
}
if(find != groups) ok = false;
if(ok) return groups;
}
return 0;
}
My question is how do I deduce that this is in fact O(N*log(log(N))), as it's not at all obvious to me, and I was surprised I pass the test cases. I'm looking for even the simplest complexity proof sketch that would convince me of this runtime. I would assume that a log(log(N)) factor means some kind of reduction of a problem by a square root on each iteration, but I have no idea how this applies to my problem. Thanks a lot for any help
You're completely right: to get the log log performance the problem needs to be reduced.
A n.log(log(n)) solution in python [below]. Codility no longer test 'performance' on this problem (!) but the python solution scores 100% for accuracy.
As you've already surmised:
Outer loop will be O(n) since it is testing whether each size of block is a clean divisor
Inner loop must be O(log(log(n))) to give O(n log(log(n))) overall.
We can get good inner loop performance because we only need to perform d(n), the number of divisors of n. We can store a prefix sum of peaks-so-far, which uses the O(n) space allowed by the problem specification. Checking whether a peak has occurred in each 'group' is then an O(1) lookup operation using the group start and end indices.
Following this logic, when the candidate block size is 3 the loop needs to perform n / 3 peak checks. The complexity becomes a sum: n/a + n/b + ... + n/n where the denominators (a, b, ...) are the factors of n.
Short story: The complexity of n.d(n) operations is O(n.log(log(n))).
Longer version:
If you've been doing the Codility Lessons you'll remember from the Lesson 8: Prime and composite numbers that the sum of harmonic number operations will give O(log(n)) complexity. We've got a reduced set, because we're only looking at factor denominators. Lesson 9: Sieve of Eratosthenes shows how the sum of reciprocals of primes is O(log(log(n))) and claims that 'the proof is non-trivial'. In this case Wikipedia tells us that the sum of divisors sigma(n) has an upper bound (see Robin's inequality, about half way down the page).
Does that completely answer your question? Suggestions on how to improve my python code are also very welcome!
def solution(data):
length = len(data)
# array ends can't be peaks, len < 3 must return 0
if len < 3:
return 0
peaks = [0] * length
# compute a list of 'peaks to the left' in O(n) time
for index in range(2, length):
peaks[index] = peaks[index - 1]
# check if there was a peak to the left, add it to the count
if data[index - 1] > data[index - 2] and data[index - 1] > data[index]:
peaks[index] += 1
# candidate is the block size we're going to test
for candidate in range(3, length + 1):
# skip if not a factor
if length % candidate != 0:
continue
# test at each point n / block
valid = True
index = candidate
while index != length:
# if no peak in this block, break
if peaks[index] == peaks[index - candidate]:
valid = False
break
index += candidate
# one additional check since peaks[length] is outside of array
if index == length and peaks[index - 1] == peaks[index - candidate]:
valid = False
if valid:
return length / candidate
return 0
Credits:
Major kudos to #tmyklebu for his SO answer which helped me a lot.
I'm don't think that the time complexity of your algorithm is O(Nlog(logN)).
However, it is certainly much lesser than O(N^2). This is because your inner loop is entered only k times where k is the number of factors of N. The number of factors of an integer can be seen in this link: http://www.cut-the-knot.org/blue/NumberOfFactors.shtml
I may be inaccurate but from the link it seems,
k ~ logN * logN * logN ...
Also, the inner loop has a complexity of O(N) since the number of peaks can be N/2 in the worst case.
Hence, in my opinion, the complexity of your algorithm is O(NlogN) at best but it must be sufficient to clear all test cases.
#radicality
There's at least one point where you can optimize the number of passes in the second loop to O(sqrt(N)) -- collect divisors of N and iterate through them only.
That will make your algo a little less "brute force".
Problem definition allows for O(N) space complexity. You can store divisors without violating this condition.
This is my solution based on prefix sums. Hope it helps:
class Solution {
public int solution(int[] A) {
int n = A.length;
int result = 1;
if (n < 3)
return 0;
int[] prefixSums = new int[n];
for (int i = 1; i < n-1; i++)
if (A[i] > A[i-1] && A[i] > A[i+1])
prefixSums[i] = prefixSums[i-1] + 1;
else
prefixSums[i] = prefixSums[i-1];
prefixSums[n-1] = prefixSums[n-2];
if (prefixSums[n-1] <= 1)
return prefixSums[n-1];
for (int i = 2; i <= prefixSums[n-2]; i++) {
if (n % i != 0)
continue;
int prev = 0;
boolean containsPeak = true;
for (int j = n/i - 1; j < n; j += n/i) {
if (prefixSums[j] == prev) {
containsPeak = false;
break;
}
prev = prefixSums[j];
}
if (containsPeak)
result = i;
}
return result;
}
}
def solution(A):
length = len(A)
if length <= 2:
return 0
peek_indexes = []
for index in range(1, length-1):
if A[index] > A[index - 1] and A[index] > A[index + 1]:
peek_indexes.append(index)
for block in range(3, int((length/2)+1)):
if length % block == 0:
index_to_check = 0
temp_blocks = 0
for peek_index in peek_indexes:
if peek_index >= index_to_check and peek_index < index_to_check + block:
temp_blocks += 1
index_to_check = index_to_check + block
if length/block == temp_blocks:
return temp_blocks
if len(peek_indexes) > 0:
return 1
else:
return 0
print(solution([1, 2, 3, 4, 3, 4, 1, 2, 3, 4, 6, 2, 1, 2, 5, 2]))
I just found the factors at first,
then just iterated in A and tested all number of blocks to see which is the greatest block division.
This is the code that got 100 (in java)
https://app.codility.com/demo/results/training9593YB-39H/
A javascript solution with complexity of O(N * log(log(N))).
function solution(A) {
let N = A.length;
if (N < 3) return 0;
let peaks = 0;
let peaksTillNow = [ 0 ];
let dividers = [];
for (let i = 1; i < N - 1; i++) {
if (A[i - 1] < A[i] && A[i] > A[i + 1]) peaks++;
peaksTillNow.push(peaks);
if (N % i === 0) dividers.push(i);
}
peaksTillNow.push(peaks);
if (peaks === 0) return 0;
let blocks;
let result = 1;
for (blocks of dividers) {
let K = N / blocks;
let prevPeaks = 0;
let OK = true;
for (let i = 1; i <= blocks; i++) {
if (peaksTillNow[i * K - 1] > prevPeaks) {
prevPeaks = peaksTillNow[i * K - 1];
} else {
OK = false;
break;
}
}
if (OK) result = blocks;
}
return result;
}
Solution with C# code
public int GetPeaks(int[] InputArray)
{
List<int> lstPeaks = new List<int>();
lstPeaks.Add(0);
for (int Index = 1; Index < (InputArray.Length - 1); Index++)
{
if (InputArray[Index - 1] < InputArray[Index] && InputArray[Index] > InputArray[Index + 1])
{
lstPeaks.Add(1);
}
else
{
lstPeaks.Add(0);
}
}
lstPeaks.Add(0);
int totalEqBlocksWithPeaks = 0;
for (int factor = 1; factor <= InputArray.Length; factor++)
{
if (InputArray.Length % factor == 0)
{
int BlockLength = InputArray.Length / factor;
int BlockCount = factor;
bool isAllBlocksHasPeak = true;
for (int CountIndex = 1; CountIndex <= BlockCount; CountIndex++)
{
int BlockStartIndex = CountIndex == 1 ? 0 : (CountIndex - 1) * BlockLength;
int BlockEndIndex = (CountIndex * BlockLength) - 1;
if (!(lstPeaks.GetRange(BlockStartIndex, BlockLength).Sum() > 0))
{
isAllBlocksHasPeak = false;
}
}
if (isAllBlocksHasPeak)
totalEqBlocksWithPeaks++;
}
}
return totalEqBlocksWithPeaks;
}
There is actually an O(n) runtime complexity solution for this task, so this is a humble attempt to share that.
The trick to go from the proposed O(n * loglogn) solutions to O(n) is to calculate the maximum gap between any two peaks (or a leading or trailing peak to the corresponding endpoint).
This can be done while building the peak hash in the first O(n) loop.
Then, if the gap is 'g' between two consecutive peaks, then the minimum group size must be 'g/2'. It will simply be 'g' between start and first peak, or last peak and end. Also, there will be at least one peak in any group from group size 'g', so the range to check for is: g/2, 1+g/2, 2+g/2, ... g.
Therefore, the runtime is the sum over d = g/2, g/2+1, ... g) * n/d where 'd' is the divisor'.
(sum over d = g/2, 1 + g/2, ... g) * n/d = n/(g/2) + n/(1 + g/2) + ... + (n/g)
if g = 5, this n/5 + n/6 + n/7 + n/8 + n/9 + n/10 = n(1/5+1/6+1/7+1/8+1/9+1/10)
If you replace each item with the largest element, then you get sum <= n * (1/5 + 1/5 + 1/5 + 1/5 + 1/5) = n
Now, generalising this, every element is replaced with n / (g/2).
The number of items from g/2 to g is 1 + g/2 since there are (g - g/2 + 1) items.
So, the whole sum is: n/(g/2) * (g/2 + 1) = n + 2n/g < 3n.
Therefore, the bound on the total number of operations is O(n).
The code, implementing this in C++, is here:
int solution(vector<int> &A)
{
int sizeA = A.size();
vector<bool> hash(sizeA, false);
int min_group_size = 2;
int pi = 0;
for (int i = 1, pi = 0; i < sizeA - 1; ++i) {
const int e = A[i];
if (e > A[i - 1] && e > A[i + 1]) {
hash[i] = true;
int diff = i - pi;
if (pi) diff /= 2;
if (diff > min_group_size) min_group_size = diff;
pi = i;
}
}
min_group_size = min(min_group_size, sizeA - pi);
vector<int> hash_next(sizeA, 0);
for (int i = sizeA - 2; i >= 0; --i) {
hash_next[i] = hash[i] ? i : hash_next[i + 1];
}
for (int group_size = min_group_size; group_size <= sizeA; ++group_size) {
if (sizeA % group_size != 0) continue;
int number_of_groups = sizeA / group_size;
int group_index = 0;
for (int peak_index = 0; peak_index < sizeA; peak_index = group_index * group_size) {
peak_index = hash_next[peak_index];
if (!peak_index) break;
int lower_range = group_index * group_size;
int upper_range = lower_range + group_size - 1;
if (peak_index > upper_range) {
break;
}
++group_index;
}
if (number_of_groups == group_index) return number_of_groups;
}
return 0;
}
var prev, curr, total = 0;
for (var i=1; i<A.length; i++) {
if (curr == 0) {
curr = A[i];
} else {
if(A[i] != curr) {
if (prev != 0) {
if ((prev < curr && A[i] < curr) || (prev > curr && A[i] > curr)) {
total += 1;
}
} else {
prev = curr;
total += 1;
}
prev = curr;
curr = A[i];
}
}
}
if(prev != curr) {
total += 1;
}
return total;
I agree with GnomeDePlume answer... the piece on looking for the divisors on the proposed solution is O(N), and that could be decreased to O(sqrt(N)) by using the algorithm provided on the lesson text.
So just adding, here is my solution using Java that solves the problem on the required complexity.
Be aware, it has way more code then yours - some cleanup (debug sysouts and comments) would always be possible :-)
public int solution(int[] A) {
int result = 0;
int N = A.length;
// mark accumulated peaks
int[] peaks = new int[N];
int count = 0;
for (int i = 1; i < N -1; i++) {
if (A[i-1] < A[i] && A[i+1] < A[i])
count++;
peaks[i] = count;
}
// set peaks count on last elem as it will be needed during div checks
peaks[N-1] = count;
// check count
if (count > 0) {
// if only one peak, will need the whole array
if (count == 1)
result = 1;
else {
// at this point (peaks > 1) we know at least the single group will satisfy the criteria
// so set result to 1, then check for bigger numbers of groups
result = 1;
// for each divisor of N, check if that number of groups work
Integer[] divisors = getDivisors(N);
// result will be at least 1 at this point
boolean candidate;
int divisor, startIdx, endIdx;
// check from top value to bottom - stop when one is found
// for div 1 we know num groups is 1, and we already know that is the minimum. No need to check.
// for div = N we know it's impossible, as all elements would have to be peaks (impossible by definition)
for (int i = divisors.length-2; i > 0; i--) {
candidate = true;
divisor = divisors[i];
for (int j = 0; j < N; j+= N/divisor) {
startIdx = (j == 0 ? j : j-1);
endIdx = j + N/divisor-1;
if (peaks[startIdx] == peaks[endIdx]) {
candidate = false;
break;
}
}
// if all groups had at least 1 peak, this is the result!
if (candidate) {
result = divisor;
break;
}
}
}
}
return result;
}
// returns ordered array of all divisors of N
private Integer[] getDivisors(int N) {
Set<Integer> set = new TreeSet<Integer>();
double sqrt = Math.sqrt(N);
int i = 1;
for (; i < sqrt; i++) {
if (N % i == 0) {
set.add(i);
set.add(N/i);
}
}
if (i * i == N)
set.add(i);
return set.toArray(new Integer[]{});
}
Thanks,
Davi

Determine if a sorted array contains a contiguous sequence that sums up to N

I'm trying to write an algorithm that will return True/False whether a contiguous sequence in a sorted array that contains only positive integers, can sum up to N.
For example:
Array = { 1, 2, 3, 4 };
6 is good! 1 + 2 + 3 = 6
8 is not good! 1 + 3 + 4 = 8, but it's not contiguous, since we skipped the 2.
This is what I have tried to do:
int[] arr = ...;
int headIndex = 1, tailIndex = 0, sum = arr[0];
while (sum != n)
{
if (sum < n)
{
sum += arr[headIndex++];
}
if (sum > n)
{
sum -= arr[tailIndex++];
}
}
return sum == n;
Obviously the above does not work (In some cases it might get stuck in an infinite loop). Any suggestions?
One thing I haven't mentioned earlier, and is very important- the algorithm's complexity must be low as possible.
This is just a sketch:
Loop from left to right, find the largest k that n1 + ... + nk <= target, set sum = n1 + ... + nk. If the array sum is smaller than target, then return false.
If sum == target, we are done. If not, then any subarray S that sum to target will have S.length < k, and will begin from an element that is larger than the first one. So we kick out the first from the sum: sum -= n1, leftEnd++. Now we can go back to step 1, but no need to compute k from scratch.
Since the left end moves at most N times, and the right end moves at most N times, this algorithm has time complexity O(N), and constant space requirement.
var i = 0;
while(i != arr.Length)
{
var remembre = i;
var tmp = 0;
for(; tmp < N && i < arr.Length; ++i)
tmp += arr[i];
if(N == tmp)
return true;
i = remembre + 1;
}
return false;
I believe this should work.
Simply:
for i = 1 to n - 1:
j = 0
while (j < i):
sm = 0
for k = j to i:
sm = sm + array[k]
if sm == N:
return True
j = j + 1
return False
This works in O(n^3) time.
Here is a solution in code. It's been heavily influenced by #Ziyao Wei's sketch, which simplified my original approach (in particular, there is no need to backtrack and add the small numbers back on, only to take them off as I first thought).
public static bool HasConsecutiveSum(IList<int> list, int requiredSum)
{
int start = 0;
int sum = 0;
for (int end = 0; end < list.Count; end++)
{
sum += list[end];
while (sum > requiredSum)
{
sum -= list[start++];
if (start > end)
{
return false;
}
}
if (sum == requiredSum)
{
return true;
}
}
return false;
}
I think the most optimal algorithm works with a window that moves over the list. The value of the window (WV) is the sum of the elements that fall within the window. If WV is less then N, move the head and add the new value that fits within the window to WV, if the value is bigger then N, move the tail one up and subtract the value that falls of the window from WV. The algorithm stops when WV equals N, or the tail moves beyond the head, or the head is at the end of the list, and WV is still to low.
This would run in linear time: every element in the list is once added and once subtracted at most.
Written down in some code to illustrate the idea (python alike), but not tested
WV = list[0]
L = len(list)
tail = 0
head = 0
while WV != N
if WV < N
head += 1
if head < L
WV += list[head]
else
return false // beyond end of list
elif WV > N
if tail < head
WV -= list[tail]
tail += 1
else
return false // single element bigger then N, and list is sorted
return true

Resources