Nested time complexity - performance

I hope this is a simple question, but Google did not give me any immediate results.
If I have a function with running time O(n log n) and inside that function is another function, also taking O(n log n), then what is the total running time of the function?
Say I have a list of lists.
it takes n log n time to the find the desired list and then n log n time again to find the desired item within that list.
something like
find list in n log n time
find element in list in n log n time
Is the running time still just n log n?
Thank you in advance.
What if the function looks like this:
for each element e1 in list // (O(N) time)
if e1 is the one we are looking for
for each element e2 in e1 // (O(N) time)
do something
It is O(N) inside O(N), but the second O(N) is only executed once in the first loop.

It depends how often you call the second function.
If you execute a function that finds a list within a list of lists in O(n log n) time
and then searches just that one list for desired element, which it finds in O(m log m) time,
then the total running time is O(n log n + m log m).
If m=n then the total time is just O(n log n).
If the outer loop performs O(n log n) "steps", and at each step you consider one list from the list of lists and call a function that takes O(m log m) time to find a desired item in that list, then the total running time is O(mn (log m)(log n)). I'm having difficulty imagining what application would use an algorithm like this, however.
If you execute a loop O(N) times, and during at most one of the iterations of the loop you execute an "inner" loop that runs in O(M) time, then the total running time of the outer loop is O(N + M). Note that the reason we say O(M + N) is that we don't have
any other information in this paragraph about which grows faster, M or N, and O(M + N)
covers us in either case. Again, if we knew that M=N, or even if we just knew that M is O(N) (doesn't grow faster than N), then we could just write the total time as O(N).

Well, mathematically speaking, you just multiply “what's inside” the big Os. You get O(n² log²(n)).

Your notation obscures the truth, there are no "functions inside another". (More precisely, there is no function which calls another several times.)
What is actually done is
find list in n log n time;
find element in list in n log n time
which has complexity of order n log n.
In the second example:
for each element e1 in list // (O(N) time)
if e1 is the one we are looking for
break // Found
for each element e2 in e1 // (O(N) time)
do something
for a total of O(N).
This is to be contrasted with true nesting:
for each element e1 in list // (O(N) passes)
for each element e2 in e1 // (O(N) time)
do something
for a total of O(N²).

For
find list in n log n time
find element in list in n log n time
we get
Time = n log n * n log n = 2 n log n ~ O(n log n)
And for
for each element e1 in list // (O(N) time)
if e1 is the one we are looking for
for each element e2 in e1 // (O(N) time)
do something
we get
Time = n + k n = (k+1) n
Best-Case: only 1 matching value exist
Time(Best-Case) = n + 1 * n = 2n ~ n
Worst-Case: all values are identical and matching
Time(Worst-Case) = n + n * n = n + n^2 ~ n^2

Related

Computing the union and intersection of two unsorted arrays in O(nlogm) time

need help to solve this problem with algorithm ...
Given are two sets A and B with m and n elements, respectively, from a linear order. These sets are not necessarily sorted. Also assume that m ≤ n. Show how to compute A∪B and A ∩ B in O(nlogm) time.
As vivek_23 said, you can do better using a hash table with high probability.
However, to achieve O(n log m), and assuming your sets are stored as arrays, you can sort A in O(m log m) time and then do n binary searches for each element of B to see if it is also in A. Each lookup takes O(log m) time, for a total of O(n log m) time.
So, for A∪B, you can copy A into a new set C in O(m) time. Then for each element of B, you do a lookup (binary search) on A. If it's not in A, you add it to C. This way, you'd have spent O(m + n log m) time to construct C and O(m log m)* to sort A. Since m < n, the total time is O(n log m) as you'd like.
For A ∩ B, you'll start with an empty set D. For each element of B, you do a lookup in A. If it is there, you'll add it to D. When you're done, you'll have done n lookups on A, for a total of (n log m).
If you were to insert all the elements of list A into a hash table rather than sorting them, you could do everything in O(m + n) time with high probability.

Time complexity of sort by x1, then x2?

The standard sort function literature will tell you that a sort can generally (Merge, Quick) be done in N log N.
So for instance if I have this list: [1,6,3,3,4,2]
It will be sorted in NlogN time to this: [1,2,3,3,4,6]
What if I have a list where I sort by the first property, then the second?
Like this: [(1,1),(6,3),(3,4),(3,1),(2,8)] to this: [(1,1),(2,8),(3,1),(3,4),(6,3)]
What's the time complexity of that?
What I've come up with is that if all the first indices are the same, you're just doing an N log N again, so the same. If there's a bunch of different first indices though, you are re-sorting a bunch of little sets.
Merge sort(or quick sort) performs O(N log N) comparisons. Its time complexity is O(N log N * time_to_compare_two_elements). The time complexity of comparing a pair of elements is constants(if the time to compare two element is constant). Thus, the time complexity of sorting an array of pairs is O(N log N), too.
Firstly you will be comparing the first elements of each pair and sort.Takes NlogN. Now will compare seconds elements if first elements are same. which takes NlogN. Total 2Nlogn which is nothing but NlogN
Hope this Helps!!
If you don't know anything more about your data, you can't guarantee better than O(N log(N)): all first elements could be the same, and then you'd be stuck sorting the second elements as normal.
That's what the O(N log(N)) bound means: if you have to generic-comparison-sort your data, you can't improve on this. Full stop.
If you do have further information, then (as you intuitively reasoned) you might be able to improve on this. As an example, say that for any x that occurs at least once as the first element in a pair, there are roughly log(N) pairs having x as their first element. In this case, your first pass can be more efficient:
d = {}
for x, y in L:
xL = d.setdefault(x, [])
xL.append(y)
xs_sorted = sorted(d.keys())
This is (roughly) O(N), since d.keys() has N / log(N) elements. Next, you can sort each of the N / log(N) sublists, which each have size log(N):
L_sorted = []
for x in xs_sorted:
ys_sorted = sorted(d[x])
for y in ys_sorted:
L_sorted.append((x, y))
This is O(N log(log(N))), which dominates the runtime - but is better than O(N log(N))!
There is a constant factor in Nlog(N) complexity which is dependent how a computer is going to make the comparison before deciding the order. For example if the sorting is on the characters the complexity of Nlog(N) will become
Nlog(N) * length of maximum string size

Running time of merge sort on linked lists?

i came across this piece of code to perform merge sort on a link list..
the author claims that it runs in time O(nlog n)..
here is the link for it...
http://www.geeksforgeeks.org/merge-sort-for-linked-list/
my claim is that it takes atleast O(n^2) time...and here is my argument...
look, you divide the list(be it array or linked list), log n times(refer to recursion tree), during each partition, given a list of size i=n, n/2, ..., n/2^k, we would take O(i) time to partition the original/already divided list..since sigma O(i)= O(n),we can say , we take O(n) time to partition for any given call of partition(sloppily), so given the time taken to perform a single partition, the question now arises as to how many partitions are going to happen all in all, we observe that the number of partitions at each level i is equal to 2^i , so summing 2^0+2^1+....+2^(lg n ) gives us [2(lg n)-1] as the sum which is nothing but (n-1) on simplification , implying that we call partition n-1, (let's approximate it to n), times so , the complexity is atleast big omega of n^2..
if i am wrong, please let me know where...thanks:)
and then after some retrospection , i applied master method to the recurrence relation where i replaced theta of 1 which is there for the conventional merge sort on arrays with theta of n for this type of merge sort (because the divide and combine operations take theta of n time each), the running time turned out to be theta of (n lg n)...
also i noticed that the cost at each level is n (because 2 power i * (n/(2pow i)))...is the time taken for each level...so its theta of n at each level* lg n levels..implying that its theta of (n lg n).....did i just solve my own question??pls help i am kinda confused myself..
The recursive complexity definition for an input list of size n is
T(n) = O(n) + 2 * T(n / 2)
Expanding this we get:
T(n) = O(n) + 2 * (O(n / 2) + 2 * T(n / 4))
= O(n) + O(n) + 4 * T(n / 4)
Expanding again we get:
T(n) = O(n) + O(n) + O(n) + 8 * T(n / 8)
Clearly there is a pattern here. Since we can repeat this expansion exactly O(log n) times, we have
T(n) = O(n) + O(n) + ... + O(n) (O(log n) terms)
= O(n log n)
You are performing a sum twice for some weird reason.
To split and merge a linked list of size n, it takes O(n) time. The depth of recursion is O(log n)
Your argument was that a splitting step takes O(i) time and sum of split steps become O(n) And then you call it the time taken to perform only one split.
Instead, lets consider this, a problem of size n forms two n/2 problems, four n/4 problems eight n/8 and so on until 2^log n n/2^logn subproblems are formed. Sum these up you get O(nlogn) to perform splits.
Another O(nlogn) to combine sub problems.

Can any one help me out in sorting the array by inserting k new elements

What is the time complexity (How to do) for inserting K new elements into the sorted array which contains N elements. This can be done in O(K Log K + N)
It depends on the data structure used. You mentioned "in an array", which makes it O(k * n) O(k + n). With other implementations it can be reduced to O(k log n), which is the (optimal?) complexity of finding the insertion position for each of the k elements.
However, an array doesn't allow for inserting. Inserting a new element on position i requires pushing all elements starting from the old i one space back. If i is close to the end that's okay, but near the start you'll end up moving nearly every element back, thus a worst case of O(n) per insertion. On top of that, an array has limited space. If your array wasn't made to fit n+k elements, you'll have to allocate new memory and copy from the old to the new.
[edit]
As mentioned by sashoalm, if all k new elements are known at the start, you can do better by first sorting the array of new elements (O(k log k)) and then merging them with the old array (O(n + k)). Provided k << n, this boils down to O(n + k) rather than O(k * n).
I have a new answer like as follows
Sort the K new elements with merge sort which takes O(K * Log K)
Now merge the two sorted arrays (Use Third Array, after finishing remove first and second arrays). The first array is already sorted which contains N elements and the second array contains K elements which in turn takes O(N + K) which is going to be O(N).
So, the total time complexity is going to be O(K * Log K) (First Step) + O(N) (Second Step)
Total time complexity is O( K * Log K + N)

Verifying complexity of sorting N integers, M at a time

I am asking complexity only of the first part(sort part) of a bigger problem called external sort.
N - number of integers (big enof to fit in memory)
M - number of integers that can be sorted in memory using merge sort.
Complexity of merge sort:
O (M log M)
But we need to sort total of N elements.
Thus, we need so sort N / M times totally.
thus
O ((N / M) * M log M)
thus finally deriving
O (N log M)
Is this correct complexity ? If not do correct my calculations.
Yes, this is the correct complexity for the first stage of sorting N integers M at a time. You could express the same number differently if you say that the number of M-sized sets is k. Then you could say it's
O(N*Log(N/k))
If you want to end up with all N elements being sorted this is insufficient. You split your total set of N numbers into N/M subsets of M numbers each. Then you sort each subset. As you correctly found out, this has a complexity of O(N log M), and if your goal is to end up with a couple of sorted subsets you are done and everything is fine. However, at this point you don't have the entire set of N sorted, you just have a couple of sorted subsets and still some work to do. What's still missing is the operation of merging those subsets together. Assuming your merge operations always merge two subsets into one, you still have Log2( N/M ) merge operations to do, each with a complexity O(N). So the final complexity is O(N log M + N Log(N/M) ) = O( N Log N ). As it's supposed to be.

Resources