Time Complexity Analysis : better explaination? - algorithm

I came across the following code to find triplets that satisfy Triangle sum property.
// Function to count all possible triangles with arr[]
// elements
static int findNumberOfTriangles(int arr[])
{
int n = arr.length;
// Sort the array elements in non-decreasing order
Arrays.sort(arr);
// Initialize count of triangles
int count = 0;
// Fix the first element. We need to run till n-3 as
// the other two elements are selected from arr[i+1...n-1]
for (int i = 0; i < n-2; ++i)
{
// Initialize index of the rightmost third element
int k = i + 2;
// Fix the second element
for (int j = i+1; j < n; ++j)
{
/* Find the rightmost element which is smaller
than the sum of two fixed elements
The important thing to note here is, we use
the previous value of k. If value of arr[i] +
arr[j-1] was greater than arr[k], then arr[i] +
arr[j] must be greater than k, because the
array is sorted. */
while (k < n && arr[i] + arr[j] > arr[k])
++k;
/* Total number of possible triangles that can be
formed with the two fixed elements is k - j - 1.
The two fixed elements are arr[i] and arr[j]. All
elements between arr[j+1] to arr[k-1] can form a
triangle with arr[i] and arr[j]. One is subtracted
from k because k is incremented one extra in above
while loop. k will always be greater than j. If j
becomes equal to k, then above loop will increment
k, because arr[k] + arr[i] is always/ greater than
arr[k] */
count += k - j - 1;
}
}
return count;
}
Can someone give a better explanation as to why the time complexity of this solution is O(n^2) and not O(n^3) ? My understanding is that for every i and j, k varies too.

The time complexity of the above solution is O(n^2) as you can see the value of k is initialised before the second for loop. In the second for
loop the value of k is increasing in while condition. Once the while condition terminates, for loop will run for the next value of j and the
value of k remains the same as it was terminated in the while loop before.
Once the value of k becomes equal to n then it will not run for any value of j after that.
So the second for loop is running only from k=i+2 to n. Hence the complexity is O(n^2).

The only statement that could get executed more than O(n^2) times is the most nested ++k statement.
But k never exceeds n, and is reset (to a non-negative number) n-2 times. That proves that the ++k statement is executed at most n(n-2) = O(n^2) times.

Related

Number of increasing subsequences in sequence of [0-9] elements

In the case a sequence of unknown range value elements, the question Number of all increasing subsequences in given sequence give us a solution in O(n^2).
I've heard of a solution in O(9*n) in the case of a sequence composed of elements in the interval [0,9] only. If you know this algorithm please let me know about that.
Here is an algorithm:
1)Let's call dp[i] = the number of increasing subsequences which have a last element i(0 <= i <= 9). Initially it is filled with zeros.
2)Now we can iterate over the sequence and compute dp in the following way:
Let's assume that the current element is d(0 <= d <= 9). Then dp can be updated like this:
for prev = 0...d - 1
dp[d] += dp[prev] //continue one of the previous sequences.
dp[d] += 1 //start a new sequence that consists of only one element.
3)After iterating over all the elements of the sequence, the answer is just the sum of dp[i] for 0 <= i <= 9.
Note that this algorithm has desired complexity only under assumption that arithmetic operations have O(1) time complexity(that might not be the case because the number of increasing subsequences can be very large).
Altering the algorithm linked to in the question, I believe this shall also fulfill the complexity requirement:
input[i] = input array
n = size of input
dp[i] = number of increasing subsequences with i as the last element (same size as input)
initialize dp[i] = 0 for all 0 <= i < n
//Every possible input can be appended on to one previously found
// increasing subsequence
for (j = 0; j <= 9; j++) {
//Running total of increasing subsequences that occur earlier in the input using
// smaller numbers than j
int smallerSum = 0;
for (i = 0; i < n; i++) {
//This spot in dp was tallied already, need to add it to the running total
if (input[i] < j) {
smallerSum += dp[i]
//Add 1 to the running total, this should be the final answer for this spot in dp
} else if (input[i] == j) {
dp[i] = smallerSum + 1;
}
}
}
return the sum of all elements of dp
Normally a nested for-loop is a dead giveaway of O(n^2) but in this case we're looping over a constant, making it O(n).
In the general case this algorithm would be O(n*k), where n is the size of the input array and k is the number of possible input values.

Maximize sum of list with no more than k consecutive elements from input

I have an array of N numbers and I want remove only those elements from the list which when removed will create a new list where there are no more K numbers adjacent to each other. There can be multiple lists that can be created with this restriction. So I just want that list in which the sum of the remaining numbers is maximum and as an output print that sum only.
The algorithm that I have come up with so far has a time complexity of O(n^2). Is it possible to get better algorithm for this problem?
Link to the question.
Here's my attempt:
int main()
{
//Total Number of elements in the list
int count = 6;
//Maximum number of elements that can be together
int maxTogether = 1;
//The list of numbers
int billboards[] = {4, 7, 2, 0, 8, 9};
int maxSum = 0;
for(int k = 0; k<=maxTogether ; k++){
int sum=0;
int size= k;
for (int i = 0; i< count; i++) {
if(size != maxTogether){
sum += billboards[i];
size++;
}else{
size = 0;
}
}
printf("%i\n", sum);
if(sum > maxSum)
{
maxSum = sum;
}
}
return 0;
}
The O(NK) dynamic programming solution is fairly easy:
Let A[i] be the best sum of the elements to the left subject to the not-k-consecutive constraint (assuming we're removing the i-th element as well).
Then we can calculate A[i] by looking back K elements:
A[i] = 0;
for j = 1 to k
A[i] = max(A[i], A[i-j])
A[i] += input[i]
And, at the end, just look through the last k elements from A, adding the elements to the right to each and picking the best one.
But this is too slow.
Let's do better.
So A[i] finds the best from A[i-1], A[i-2], ..., A[i-K+1], A[i-K].
So A[i+1] finds the best from A[i], A[i-1], A[i-2], ..., A[i-K+1].
There's a lot of redundancy there - we already know the best from indices i-1 through i-K because of A[i]'s calculation, but then we find the best of all of those except i-K (with i) again in A[i+1].
So we can just store all of them in an ordered data structure and then remove A[i-K] and insert A[i]. My choice - A binary search tree to find the minimum, along with a circular array of size K+1 of tree nodes, so we can easily find the one we need to remove.
I swapped the problem around to make it slightly simpler - instead of finding the maximum of remaining elements, I find the minimum of removed elements and then return total sum - removed sum.
High-level pseudo-code:
for each i in input
add (i + the smallest value in the BST) to the BST
add the above node to the circular array
if it wrapper around, remove the overridden element from the BST
// now the remaining nodes in the BST are the last k elements
return (the total sum - the smallest value in the BST)
Running time:
O(n log k)
Java code:
int getBestSum(int[] input, int K)
{
Node[] array = new Node[K+1];
TreeSet<Node> nodes = new TreeSet<Node>();
Node n = new Node(0);
nodes.add(n);
array[0] = n;
int arrPos = 0;
int sum = 0;
for (int i: input)
{
sum += i;
Node oldNode = nodes.first();
Node newNode = new Node(oldNode.value + i);
arrPos = (arrPos + 1) % array.length;
if (array[arrPos] != null)
nodes.remove(array[arrPos]);
array[arrPos] = newNode;
nodes.add(newNode);
}
return sum - nodes.first().value;
}
getBestSum(new int[]{1,2,3,1,6,10}, 2) prints 21, as required.
Let f[i] be the maximum total value you can get with the first i numbers, while you don't choose the last(i.e. the i-th) one. Then we have
f[i] = max{
f[i-1],
max{f[j] + sum(j + 1, i - 1) | (i - j) <= k}
}
you can use a heap-like data structure to maintain the options and get the maximum one in log(n) time, keep a global delta or whatever, and pay attention to the range i - j <= k.
The following algorithm is of O(N*K) complexity.
Examine the 1st K elements (0 to K-1) of the array. There can be at most 1 gap in this region.
Reason: If there were two gaps, then there would not be any reason to have the lower (earlier gap).
For each index i of these K gap options, following holds true:
1. Sum upto i-1 is the present score of each option.
2. If the next gap is after a distance of d, then the options for d are (K - i) to K
For every possible position of gap, calculate the best sum upto that position among the options.
The latter part of the array can be traversed similarly independently from the past gap history.
Traverse the array further till the end.

Calculate majority element in an array

Last week I appeared in an interview. I was given the following question:
Given an array of 2n elements, and out of this n elements are same, and the remaining are all different. Find the element that repeats n times.
There is no restriction on the range of the elements.
Can someone please give me an efficient algorithm to solve this?
"Array of 2n elements is given, and out of this n elements are same, and remaining are all different. Find the element that repeats n time."
This can be done in O(n) with the following algorithm:
1) Iterate over the array, checking to see if any elements [i] and [i+1] are the same.
2) Iterate over the array, checking to see if any elements [i] and [i+2] are the same.
3) If n = 2 (and thus length = 4), check if 0 and 3 are the same.
Explanation:
Call the matching elements m and the non-matching elements r.
For n = 2, we can construct mmrr, mrmr and mrrm - so we must check for gap size 0, 1 and the only place we can have gap size 2.
For n > 2, we cannot construct the array with no gaps of size 0 or 1. For example for n = 3, you have to start like this: mrrmr... but then you must place an m. Similarly for n = 4, mrrmrrmm - having no gaps of size 0 or 1 would require ms to be outnumbered by rs by more and more as n increases. Proving this is easy.
You just need to find two elements that are the same.
One idea would be:
Get one element from the 2n elements.
If it is not in the a Set, put it in.
Repeat until you find one that is in that set.
Well if complexity doesn't matter, one naive way would be to use two loops, which is for the worst case O(n^2).
for(int i = 0; i < array.size(); i++){
for(int j = i + 1; j < array.size(); j++){
if(array[i] == array[j]){
// element found
}
}
If the first four elements are all distinct then the array must contain a consecutive pair of the target element...
int find(int A[n])
{
// check first four elements (10 iterations = O(1))
for (int i = 0; i < 4; i++)
for (int j = i+1; j < 4; j++)
if (A[i] == A[j])
return A[i];
// find the consecutive pair (n-4 iterations = O(n))
for (int i = 3; i < n-1; i++)
if (A[i] == A[i+1])
return A[i];
// unreachable if input matches preconditions
throw invald_input;
}
This is optimally O(n) time with a single pass and O(1) space.
If you find one element twice, that is the element as the questions says : Array of 2n elements is given, and out of this n elements are same, and remaining are all different. Find the element that repeats n time.
You have array of 2n element and half are same and remaining are different so , consider following case,
ARRAY[2n] = {N,10,N,878,85778,N......};
or
Array[2n] = {10,N,10,N,44,N......};
And so on now simple case in for loop like,
if(ARRAY[i] == ARRAY[i+1])
{
//Your similar element :)
}
I think that the problem should be "find the element that appears at least n+1 times", if it appears only n times they can be two.
Assuming that there is such an element in the input the following algorithm can be used.
input array of 2*n elements;
int candidate = input[0];
int count = 1;
for (int i = 1; i < 2*n; ++i) {
if (input[i] == candidate) {
count++;
} else {
count --;
if (count == 0) candidate = input[i];
}
}
return candidate;
if the request is to find if there is an element present n+1 times another traversal is required to find if the element found at previous step appears n + 1 times.
Edit:
It has been suggested that the n elements with the same value are contiguous.
If this is the case just use the above algorithm and stop when count reaches n.

number of subarrays where sum of numbers is divisible by K

Given an array, find how many such subsequences (does not require to be contiguous) exist where sum of elements in that subarray is divisible by K.
I know an approach with complexity 2^n as given below. it is like finding all nCi where i=[0,n] and validating if sum is divisible by K.
Please provide Pseudo Code something like linear/quadratic or n^3.
static int numways = 0;
void findNumOfSubArrays(int [] arr,int index, int sum, int K) {
if(index==arr.length) {
if(sum%k==0) numways++;
}
else {
findNumOfSubArrays(arr, index+1, sum, K);
findNumOfSubArrays(arr, index+1, sum+arr[index], K);
}
}
Input - array A in length n, and natural number k.
The algorithm:
Construct array B: for each 1 <= i <= n: B[i] = (A[i] modulo K).
Now we can use dynamic programming:
We define D[i,j] = maximum number of sub-arrays of - B[i..n] that the sum of its elements modulo k equals to j.
1 <= i <= n.
0 <= j <= k-1.
D[n,0] = if (b[n] == 0), 2. Otherwise, 1.
if j > 0 :
D[n,j] = if (B[n] modulo k) == j, than 1. Otherwise, 0.
for i < n and 0 <= j <= k-1:
D[i,j] = max{D[i+1,j], 1 + D[i+1, D[i+1,(j-B[i]+k) modulo k)]}.
Construct D.
Return D[1,0].
Overall running time: O(n*k)
Acutally, I don't think this problem can likely be solved in O(n^3) or even polynomial time, if the range of K and the range of numbers in array is unknown. Here is what I think:
Consider the following case: the N numbers in arr is something like
[1,2,4,8,16,32,...,2^(N-1)]
,
in this way, the sums of 2^N "subarrays" (that does not require to be contiguous) of arr, is exactly all the integer numbers in [0,2^N)
and asking how many of them is divisible by K, is equivalent to asking how many of integers are divisible by K in [0, 2^N).
I know the answer can be calculated directly like (2^N-1)/K (or something) in the above case. But , if we just change a few ( maybe 3? 4? ) numbers in arr randomly, to "dig some random holes" in the perfect-contiguous-integer-range [0,2^N), that makes it looks impossible to calculate the answer without going through almost every number in [0,2^N).
ok just some stupid thoughts ... could be totally wrong.
Use an auxiliary array A
1) While taking input, store the current grand total in the corresponding index (this executes in O(n)):
int sum = 0;
for (int i = 0; i < n; i++)
{
cin >> arr[i];
sum += arr[i];
A[i] = sum;
}
2) now,
for (int i = 0; i < n; i++)
for (int j = i; j < n; j++)
check that (A[j] - A[i] + arr[i]) is divisible by k
There you go: O(n^2)...

Amortized Time Cost using Accounting Method

I written an algorithm to calculate the next lexicographic permutation of an array of integers (ex. 123, 132, 213, 231, 312,323). I dont think the code is necessary but I included it below.
I think I have appropriately determined worst case time cost of O(n) where n is the number of elements in the array. I understand however if you utilize "Amortized Cost" you would find that the time cost could be accurately shown as O(1) on average case.
Question:
I would like to learn the "ACCOUNTING METHOD" to show this as O(1) but am having difficulty understanding how to apply a cost to each operation. Accounting method: Link: Accounting_Method_Explained
Thoughts:
Ive thought to apply a cost of changing a value at a position, or applying the cost to a swap. But it really doesnt make much sense.
public static int[] getNext(int[] array) {
int temp;
int j = array.length - 1;
int k = array.length - 1;
// Find largest index j with a[j] < a[j+1]
// Finds the next adjacent pair of values not in descending order
do {
j--;
if(j < 0)
{
//Edge case, where you have the largest value, return reverse order
for(int x = 0, y = array.length-1; x<y; x++,y--)
{
temp = array[x];
array[x] = array[y];
array[y] = temp;
}
return array;
}
}while (array[j] > array[j+1]);
// Find index k such that a[k] is smallest integer
// greater than a[j] to the right of a[j]
for (;array[j] > array[k]; k--,count++);
//Swap the two elements found from j and k
temp = array[k];
array[k] = array[j];
array[j] = temp;
//Sort the elements to right of j+1 in ascending order
//This will make sure you get the next smallest order
//after swaping j and k
int r = array.length - 1;
int s = j + 1;
while (r > s) {
temp = array[s];
array[s++] = array[r];
array[r--] = temp;
}
return array;
} // end getNext
Measure running time in swaps, since the other work per iteration is worst-case O(#swaps).
The swap of array[j] and array[k] has virtual cost 2. The other swaps have virtual cost 0. Since at most one swap per iteration is costly, the running time per iteration is amortized constant (assuming that we don't go into debt).
To show that we don't go into debt, it suffices to show that, if the swap of array[j] and array[k] leaves a credit at position j, then every other swap involves a position with a credit available, which is consumed. Case analysis and induction reveal that, between iterations, if an item is larger than the one immediately following it, then it was put in its current position by a swap that left an as-yet unconsumed credit.
This problem is not a great candidate for the accounting method, given the comparatively simple potential function that can be used: number of indexes j such that array[j] > array[j + 1].
From the aggregate analysis, we see T(n) < n! · e < n! · 3, so we pay $3 for each operation, and its enough for the total n! operations. Therefore its an upper bound of actual cost. So the total amortized

Resources