// Sorts the sequence (A[p],...,A[r-1])
template<class T>
void merge_sort(array<T>& A,int p,int r)
{
if (p<r-1)
{
int q=?; // see discussion in the text
merge_sort(A,p,q);
merge_sort(A,q,r);
merge(A,p,q,r);
}
}
Let's say array is [4, 9, 13, 1, 5]. I understand the recursion, the first merge_sort method splits the array up until the point of [4], so the first and second merge sort method gets skipped.How does it know where the rest of the array is to merge it? Array A is now only [4], so if we call merge (A,p,q,r) it only gets the 4 and no other part of it to merge it with?
Array A is now only [4] ... it only gets the 4 and no other part of it to merge it with.
That's were the confusion is. The array doesn't get shorter! At all times the array is the complete array with all its original values.
The notion of a subarray is only reflected by the additional parameters, p and r. These mark the range of interest in that full array (where p is the first index that is in the range, and r is the first index after the range).
Look for instance, at this recursive call:
merge_sort(A,p,q);
The p and q indices mark where in the array A is the partition that we want to sort. That call will only work with that part of the array.
At a certain moment, we will have p==0, q==1 and r==2 and the above call will then look at one element only, A[0].
The next recursive call is:
merge_sort(A,q,r);
This call will also look at one element: A[1].
These two calls will just return (as obviously nothing had to be done as a subarray with just one value is always "sorted"), and then the merge can be done with this call:
merge(A,p,q,r);
Note that this merge call gets to work with two values: A[0] and A[1]. When this call returns we know that the values at indices 0 and 1 are now guaranteed to be in sorted order.
Related
If I have the array [9, 82, 10]. By using the merge sort, I should compare the left index to the right index and if l < r, I split it to two arrays, right? But in the video, it shows that it has been cut it to half between 82 and 10. But, 82 > 10. I am so confused.
I think you confuse this logic with maybe another algorithm (QuickSort maybe?), because the split happens irrespective of the values. Where the split happens is only influenced by the current length of the array. The idea is to split the array in half. In case the length of the array is odd, that is not exactly possible, so the middle element is then put in the left or right part (it does not matter).
In this case it could split the array in either [9] and [82, 10] or in [9, 82] and [10]. Apparently you have seen it happen like in the latter case.
Only after the split the actual values start to play a role. This is when the parts are merged back together. First the left and right part are sorted (recursively), and then the left and right part are merged.
During that merge, a value from the left part and a value from the right part are compared. Every time the smaller one is put in the result array, and the "pointer" of where it came from is moved one position ahead.
In short: merge sort has two phases: split and merge. Values are not compared during the split phase, only during the merge phase.
To further clarify, no sorting or merging takes place until recursion produces two sub-arrays of size 1, at which point merging begins, and then follows the stack path, merging in a depth first / left first pattern (the animation makes this appear to be in parallel).
The particular implementation used in the video works with first and last indexes. The middle index effectively is (first+last)/2, and since array[4] = 9, array[5] = 82, array[6] = 10, first = 4, last = 6, middle = (4+6)/2 = 5, so it splits the sub-array into array[4,5] and array[6,6]. Although this is common for quick sort, most merge sorts work with beginning and ending index, beginning index = first index, and ending index = 1 + last index. In this case, begin = 4, end = 7, middle = (4+7)/2 = 5, and the split would be array[4, 5) = array[4,4] and array[5,7} = array[5,6] (using ...} to indicate the ending index = 1 + last index).
It should also be noted that most library implementations of stable sorts are some variation of iterative bottom up merge sort, which skips the recursive process and instead considers an array of n elements as n sorted runs of size 1, and begins merging immediately, in breadth first (across the array) order, merging even and odd runs, which doubles the size of sorted runs until run size >= array size. Typical variations of merge sort are hybrids of insertion sort and merge sort, such as timsort.
https://en.wikipedia.org/wiki/Timsort
Let's say I have a function foo(x,y) that returns an int. My data set is an array of arrays like a = [[1], [2,3], [4]]. I want to perform foo but for each index of a, I only want the minimum value if the array has more than one element.
So I want to calculate foo(1, 1 foo(1,4) and the minimum between foo(1,2) foo(1,3).
So if foo(1,2) is lower my calculation for the first index of a is foo(1,1) foo(1,2) foo(1,4) and push that total to some array b. I want to do this for each element in a and every element in a could possibly have more than one element
b = a.map{|arr| arr.map{|value| foo(1, value) }.min }
For each inner array value it does foo(1,value) and then selects the minimum result. In the case of only one value, only one number is calculated, of course.
It's not clear what the "1" represents in your example (the first argument of foo)
This question already has answers here:
How to check whether two lists are circularly identical in Python
(18 answers)
Closed 7 years ago.
I'm looking for an efficient way to compare lists of numbers to see if they match at any rotation (comparing 2 circular lists).
When the lists don't have duplicates, picking smallest/largest value and rotating both lists before comparisons works.
But when there may be many duplicate large values, this isn't so simple.
For example, lists [9, 2, 0, 0, 9] and [0, 0, 9, 9, 2] are matches,where [9, 0, 2, 0, 9] won't (since the order is different).
Heres an example of an in-efficient function which works.
def min_list_rotation(ls):
return min((ls[i:] + ls[:i] for i in range(len(ls))))
# example use
ls_a = [9, 2, 0, 0, 9]
ls_b = [0, 0, 9, 9, 2]
print(min_list_rotation(ls_a) == min_list_rotation(ls_b))
This can be improved on for efficiency...
check sorted lists match before running exhaustive tests.
only test rotations that start with the minimum value(skipping matching values after that)effectively finding the minimum value with the furthest & smallest number after it (continually - in the case there are multiple matching next-biggest values).
compare rotations without creating the new lists each time..
However its still not a very efficient method since it relies on checking many possibilities.
Is there a more efficient way to perform this comparison?
Related question:
Compare rotated lists in python
If you are looking for duplicates in a large number of lists, you could rotate each list to its lexicographically minimal string representation, then sort the list of lists or use a hash table to find duplicates. This canonicalisation step means that you don't need to compare every list with every other list. There are clever O(n) algorithms for finding the minimal rotation described at https://en.wikipedia.org/wiki/Lexicographically_minimal_string_rotation.
You almost have it.
You can do some kind of "normalization" or "canonicalisation" of a list independently of the others, then you only need to compare item by item (or if you want, put them in a map, in a set to eliminate duplicates, ..."
1 take the minimum item, which is not preceded by itself (in a circular way)
In you example 92009, you should take the first 0 (not the second one)
2 If you have always the same item (say 00000), you just keep that: 00000
3 If you have the same item several times, take the next item, which is minimal, and keep going until you find one unique path with minimums.
Example: 90148301562 => you have 0148.. and 0156.. => you take 0148
4 If you can not separate the different paths (= if you have equality at infinite), you have a repeating pattern: then, no matters: you take any of them.
Example: 014376501437650143765 : you have the same pattern 0143765...
It is like AAA, where A = 0143765
5 When you have your list in this form, it is easy to compare two of them.
How to do that efficiently:
Iterate on your list to get the minimums Mx (not preceded by itself). If you find several, keep all of them.
Then, iterate from each minimum Mx, take the next item, and keep the minimums. If you do an entire cycle, you have a repeating pattern.
Except the case of repeating pattern, this must be the minimal way.
Hope it helps.
I would do this in expected O(N) time using a polynomial hash function to compute the hash of list A, and every cyclic shift of list B. Where a shift of list B has the same hash as list A, I'd compare the actual elements to see if they are equal.
The reason this is fast is that with polynomial hash functions (which are extremely common!), you can calculate the hash of each cyclic shift from the previous one in constant time, so you can calculate hashes for all of the cyclic shifts in O(N) time.
It works like this:
Let's say B has N elements, then the the hash of B using prime P is:
Hb=0;
for (i=0; i<N ; i++)
{
Hb = Hb*P + B[i];
}
This is an optimized way to evaluate a polynomial in P, and is equivalent to:
Hb=0;
for (i=0; i<N ; i++)
{
Hb += B[i] * P^(N-1-i); //^ is exponentiation, not XOR
}
Notice how every B[i] is multiplied by P^(N-1-i). If we shift B to the left by 1, then every every B[i] will be multiplied by an extra P, except the first one. Since multiplication distributes over addition, we can multiply all the components at once just by multiplying the whole hash, and then fix up the factor for the first element.
The hash of the left shift of B is just
Hb1 = Hb*P + B[0]*(1-(P^N))
The second left shift:
Hb2 = Hb1*P + B[1]*(1-(P^N))
and so on...
Assuming A is an array, and n is the number of elements in A,
recursive_insertion_sort(A, n)
IF n > 1 THEN
recursive_insertion_sort(A, n-1)
key = A[n]
i = n - 1
DOWHILE A[i] > key AND i > 0
A[i+1] = A[i]
i = i - 1
ENDDO
A[i+1] = temp
ENDIF
END
Can someone explain how recursion works in this case? There are a few things I don't understand:
I don't understand why we have to call the function again if n > 1.
Why do we input (n-1) when we call the function again? Is it so that we start the entire process from n = 2, the first 2 elements?
How does recursion in general work? Like, once we call the function again, do we ignore the code from line 4 onwards, and jump straight into the second call? Or do we run the 2nd call in conjunction with the first call?
Before discussing the implementation, let's explain what this function does: it does not sort the entire array A, but only its initial n elements. You can pass the length of the array for n to sort the whole thing, but the fact that you pass the length separately is essential to understanding the rest of the answer.
I don't understand why we have to call the function again if n > 1.
Perhaps a better way to explain the meaning of this condition would be that we do not call this function again when n is one or less. This is called the base case of recursive algorithm, i.e. the case when you don't have to do anything. In case of sorting it means that an array of only one element is already sorted.
Why do we input (n-1) when we call the function again?
Since n is the number of elements that we need to sort, We pass n-1 to sort the front of the array. Once the function returns, we know that the portion A[1..n-1] is already sorted. All we need to do is to move A[n] to its right place. We do that in the DOWHILE loop that follows: we go backward one element at a time, moving elements that are bigger than A[n] to the right. Once the loop is over, we place A[n] to its new place. Now the range A[1..n] is sorted.
How does recursion in general work?
The function has two cases - the trivial base case, when everything is done, and a reduction step, when you use recursive invocation to solve a simpler problem, and then use the results of the simpler solution to construct your final solution.
once we call the function again, do we ignore the code from line 4 onwards, and jump straight into the second call?
No, once the function returns, we continue where we left. In your case, the function waits for the A[1..n-1] range to be sorted before placing A[n] to the right place.
Small example to understand how this works :
recursive_insertion_sort([1, 7, 5, 2], 4)
| recursive_insertion_sort([1, 7, 5, 2], 3)
| | recursive_insertion_sort([1, 7, 5, 2], 2)
| | | recursive_insertion_sort([1, 7, 5, 2], 1)
| | puts 7 in the right position between it's ORDERED left values [1] -> [1,7]
| puts 5 in the right position between it's ORDERED left values [1,7] -> [1,5,7]
puts 2 in the right position between it's ORDERED left values [1,5,7] -> [1,2,5,7]
I had an interview, and did one of the questions described below:
Given two arrays, please calculate the result: get the union and then remove the intersection from the union. e.g.
int a[] = {1, 3, 4, 5, 7};
int b[] = {5, 3, 8, 10}; // didn't mention if has the same value.
result = {1,4,7,8,10}
This is my idea:
Sort a, b.
Check each item of b using 'dichotomy search' in a. If not found, pass. Otherwise, remove this item from both a, b
result = elements left in a + elements left in b
I know it is a lousy algorithm, but nonetheless it's better than nothing. Is there a better approach than this one?
There are many approaches to this problem. one approach is:
1. construct hash-map using distinct array elements of array a with elements as keys and 1 is a value.
2. for every element,e in array b
if e in hash-map
set value of that key to 0
else
add e to result array.
3.add all keys from hash-map whose values 1 to result array.
another approach may be:
join both lists
sort the joined list
walk through the joined list and completely remove any elements that occurs multiple times
this have one drawback: it does not work if input lists already have doublets. But since we are talking about sets and set theory i would also expect the inputs to be sets in the mathematical sense.
Another (in my opinion the best) approach:
you do not need a search through your both lists. you can just sequentially iterate through them:
sort a and b
declare an empty result set
take iterators to both lists and repeat the following steps:
if the iterators values are unequal: add the smaller number to the result set and increment the belonging iterator
if the iterators values are equal: increment both iterators without adding something to the result set
if one iterator reaches end: add all remaining elements of the other set to the result