Three Way Quicksort for arrays with many duplicates: Partition Placement - arrays

I'm working on a variation of the Quick Sort Algorithm designed to handle arrays where there may be many duplicate elements. The basic idea is to divide the array into 3 partitions first: All Elements below the Pivot Values (with the initial pivot value being chosen at random); All Elements Equal to the Pivot Value; and All Elements Greater than the Pivot Value.
I need some advice regarding the best way to arrange the Partition..
What is the best way to arrange the Partition in a Three Way Quick Sort?
The first way I might go about it is to just keep the Pivot Partition on the left, which would make it easy to define the boundaries when I return them to the larger Quick Sort function I plan to nest the Partition function within. But that makes subsequent recursive calls to sort the Above and Below Partitions a little tricky, since they would be all lumped together in one large partition above the Pivot Partition to start with (instead of being more neatly organized into an Above and Below Partition). I could call a For Loop to insert each of these elements above and below the Pivot Partition, but I suspect that would mitigate the efficiency of the algorithm. After doing this, I could make two recursive calls to Quick Sort: once on the Below Partition, and again on the Above Partition.
OR I could modify Partition to insert "Below" elements to the left of the Pivot Partition, and insert "Above" elements to the right. This reduces the need for linear scans over the array, but it means I would have to update the left and right bounds of the partition as the Partition function operates over the array.
I believe the second choice is the better one, but I want to see if anyone has any other ideas.
For reference, the initial array might look something like this:
array = [2, 2, 1, 9, 2]
Assuming the Pivot is randomly chosen as value of "2", then after Partition, it could look either like this:
array = [2, 2, 2, 9, 1]
Or like this if I insert above and below the partition during the Partition Function:
array = [1, 2, 2, 2, 9]
And the "shell code" I'm supposed to build this function around looks like this:
def randomized_quick_sort(a, l, r):
if l >= r:
return
k = random.randint(l, r)
a[l], a[k] = a[k], a[l]
left_part_bound, right_part_bound = partition3(a, l, r)
randomized_quick_sort(a, l, left_part_bound - 1)
randomized_quick_sort(a, right_part_bound + 1, r)
*The end result doesn't need to look like this (I just need to be able to output the right result and be able to resolve within a time limit to demonstrate minimal efficiency), but it shows why I think I may need to create Above and Below partitions as I'm creating the Pivot Partition.

Related

Array Moves Chunks Of Neighboring Elements To A New Index

The Question
The question is simple, Imagine I have a list or an array (not linkedList)
list = [1, 2, ... 999999]
Now I wanna move elements from index 3000 to 600000 to index 100. The result should be easy to imagine
[1, 2,... 99, 100, 3000, 3001, ... 6000000, 101, 102, ...2999, 600001, 600002, ... 999999]
How to do those operations efficiently?
Judge My Thinking
Disclaimer: You can say this operation is exactly same as moving 101 to 2999 to index 600000. Which seems a more efficient operation. But that's not the algorithm my question is about, my question is about how to move more efficiently, so let's do the original question.
I can think of several ways:
Just do delete and insert for element 3000 to 600000. (What's the time complexity?)
Save elements from [3000, 600000] to a temporary space, then use delete insert to move everything from [101 to 2999] down to [597192, 600000] to make space to transfer [3000, 600000] back into index 100. Temporary holding data from [3000, 600000] will cost some memory, but does the copying make the whole operation slower? (Time complexity?)
is an attempt to improve 2. Same idea, but the move operation is not done by delete insert, but by manually copy [101,2999] to [597192, 600000]. (Time complexity? does it improve speed compared to delete insert)?
is an attempt to improve 2 or 3. Same idea, but no delete insert, but using many copying. But not copying everything from [3000, 600000], but only hold 1 element at a time in temporary memory, and move / copy everything in a complicated way. (Is this faster than others? Is it possible to implement? Can you show me the code / pseudo-code)
Is there better ideas?
Thank you for reading and thinking.
The algorithm you are after is known as rotate. There are two common ways to implement it. Both are running in O(length) time and O(1) space.
One, which is attributed to Dijkstra, is ultimately efficient in a sense that every element is moved just once. It is kind of tricky to implement, and requires a non-obvious setup. Besides, it may behave in a very cache-unfriendly manner. For details, see METHOD 3 (A Juggling Algorithm).
Another is very simple, cache-friendly, but moves each element twice. To rotate a range [l, r) around a midpoint m do
reverse(l, m);
reverse(m, r);
reverse(l, r);
I split the list at the "breakpoints", and reassembled the pieces. Creating a slice should be O(n), with n being the length of the slice. The longest slice is up to len(a) elements, so the storing of the list pieces should be O(n), with n being len(a). Reassembling the list pieces is O(n) as well, so this should be O(n) in total. The memory requirement is 2*len(a), since we store the slices as well, which sum up to the same length as a.
def truck(a, start, end, to):
a = list(a)
diff = end - start
to_left = to < start
split_save = a[to:to+diff]
split_take = a[start:end]
if to_left:
split_first = a[:to]
split_second = a[to+diff:start]
split_third = a[end:]
res = split_first + split_take + split_second + split_save + split_third
else:
split_first = a[:start]
split_second = a[end:to]
split_third = a[to+diff:]
res = split_first + split_save + split_second + split_take + split_third
return res
print(truck(range(10), 5, 8, 2))
# [0, 1, 5, 6, 7, 2, 3, 4, 8, 9]
print(truck(range(10), 2, 5, 8))
# [0, 1, 8, 9, 5, 6, 7, 2, 3, 4]
Let [l, r] be the segment you want to move, and L = r-l+1, the length of segment. N is total count of elements.
Delete and Insert at arbitrary position in array takes O(N), and delete and insert occurs O(L). So total time complexity is O(NL).
Same as #1. It takes O(NL) because of delete and insert.
3, 4. Copy and move takes O(L). Simply we can say it takes O(N)
Now, some fancy data structure for better complexity. You can use tree data structure to store linear array.
Especially, self-balancing binary search tree(BBST). It takes O(logN) to insert and delete one element.
Time complexity of moving segment to arbitrary position could be O(L logN), delete and insert each element.
You can find this data strcuture std::map in C++.
O(L logN) does not seem to be better. But, it can be better with BBST, to amortized O(log N)
You can gather elements in [l, r] to one subtree. Then cut this subtree from BBST. It is delete.
Insert this subtree at position you want.
For example, gather [3000, 600000] to one subtree and cut it from its parent. (Delete segment at once)
Make this subtree right child of 100th element in inorder, or left child of 101th element in inorder. (Insert segment at once)
Then, tree contains elements in order what you want.
Splay Tree would be good choice.

Optimize algorithm arrange an array with items with even-indexs are on left side of array, items with odd-indexs are on right side of array

Requirement: using recursion, size of array is an even number.
For example:
0...1...2...3...4...5 (order of index)
a...b...c...d...e...f (array before arrange)
a...c...e...b...d...f (array after arrange)
0......1......2......3......4......5......6......7 (order of index)
a1....b1....a2....b2....a3....b3....a4....b4 (array before arrange)
a1....a2....a3....a4....b1....b2....b3....b4 (array after arrange)
The problem looks easy to solve if we dont care about optimization, we can use temp array or use recursion combine with a loop to shift items ... I think this way is not best solution ....I try to use recursion combine with swap operation, without using loop ... but I fail.
Hope someone suggests me an idea to resolve the problem, thanks any help
First, let me mention that the optimum solution depends on the size of the array (how many elements are occupied) assuming this in in memory which means the array size is constrained, you could get away with a quick loop for a complexity of O(n) like so. Let array be N, and the count of elements in N is x.
Get the starting index of all odd elements = x/2 (if x is even) or
(x + 1)/2
let that index be a
let even elements start at b. let b = 0
create output array called T
while start = 0 but less than x, INCR and BEGIN
if start is even, place element at N(start) into T(b++)
if start is odd, place element at N(start) into T(a++)
array insertions and lookups are accepted O(1).
Operations are
Determine current index, you cannot avoid checking every index in the array.
You cannot avoid generating output, but its more computationally expensive to delete an element, retrieve a value, and place it at the correct position. Easier to just insert in an already allocated space in memory.
You do have the option of running concurrent for loops which would speed things up a bit, but I imagine that is beyond what you are looking for.
Don't think the optimum solution would be with a recursion, but if it's part of the problem description, and if I had the guarantee that the size of the array is a power of 2, then I would apply divide and conquer.
The simple case would be when the size of the problem is 2. That problem is already solved. After solving 2 partitions of size 2, you have to combine them in a partition of size 4. eg.
0......1......2......3......4......5......6......7 (order of index)
a1....b1....a2....b2....a3....b3....a4....b4 (array before arrange)
a1....a2....b1....b2....a3....a4....b3....b4 (array after combining partitions of size 2)
After you finish combining partitions of size 2, you combine the partitions of size 4.
a1....a2....a3....a4....b1....b2....b3....b4 (array after combining partitions of size 2)
Combining 2 partitions of size N, means swapping the N/2 items in the right of the left partition, with N/2 items on the left of the right partition. That can be done with a simple loop.
Hope this thoughts help you with your task.

Checking if two substring overlaps in O(n) time

If I have a string S of length n, and a list of tuples (a,b), where a specifies the staring position of the substring of S and b is the length of the substring. To check if any substring overlaps, we can, for example, mark the position in S whenever it's touched. However, I think this will take O(n^2) time if the list of tuples has a size of n (looping the tuple list, then looping S).
Is it possible to check if any substring actually overlaps with the other in O(n) time?
Edit:
For example, S = "abcde". Tuples = [(1,2),(3,3),(4,2)], representing "ab","cde" and "de". I want to the know an overlap is discovered when (4,2) is read.
I was thinking it is O(n^2) because you get a tuple every time, then you need to loop through the substring in S to see if any character is marked dirty.
Edit 2:
I cannot exit once a collide is detected. Imagine I need to report all the subsequent tuples that collide, so i have to loop through the whole tuple list.
Edit 3:
A high level view of the algorithm:
for each tuple (a,b)
for (int i=a; i <= a+b; i++)
if S[i] is dirty
then report tuple and break //break inner loop only
Your basic approach is correct, but you could optimize your stopping condition, in a way that guarantees bounded complexity in the worst case. Think about it this way - how many positions in S would you have to traverse and mark in the worst case?
If there is no collision, then at worst you'll visit length(S) positions (and run out of tuples by then, since any additional tuple would have to collide). If there is a collision - you can stop at the first marked object, so again you're bounded by the max number of unmarked elements, which is length(S)
EDIT: since you added a requirement to report all colliding tuples, let's calculate this again (extending my comment) -
Once you marked all elements, you can detect collision for every further tuple with a single step (O(1)), and therefore you would need O(n+n) = O(n).
This time, each step would either mark an unmarked element (overall n in the worst case), or identify a colliding tuple (worst O(tuples) which we assume is also n).
The actual steps may be interleaved, since the tuples may be organized in any way without colliding first, but once they do (after at most n tuples which cover all n elements before colliding for the first time), you have to collide every time on the first step. other arrangements may collide earlier even before marking all elements, but again - you're just rearranging the same number of steps.
Worst case example: one tuple covering the entire array, then n-1 tuples (doesn't matter which) -
[(1,n), (n,1), (n-1,1), ...(1,1)]
First tuple would take n steps to mark all elements, the rest would take O(1) each to finish. overall O(2n)=O(n). Now convince yourself that the following example takes the same number of steps -
[(1,n/2-1), (1,1), (2,1), (3,1), (n/2,n/2), (4,1), (5,1) ...(n,1)]
According to your description and comment, the overlap problem may be not about string algorithm, it can be regarded as "segment overlap" problem.
Just use your example, it can be translated to 3 segments: [1, 2], [3, 5], [4, 5]. The question is to check whether the 3 segments have overlap.
Suppose we have m segments each have format [start, end] which means segment start position and end position, one efficient algorithm to detect overlap is to sort them by start position in ascending order, it takes O(m * lgm). Then iterate the sorted m segments, for each segment, try to find whether its end position, here you only need to check:
if(start[i] <= max(end[j], 1 <= j <= i-1) {
segment i is overlap;
}
maxEnd[i] = max(maxEnd[i-1], end[i]); // update max end position of 1 to i
Which each check operation takes O(1). Then the total time complexity is O(m*lgm + m), which can be regarded as O(m*lgm). While for each output, time complexity is related to each tuple's length, which is also related to n.
This is a segment overlap problem and the solution should be possible in O(n) itself if the list of tuples has been sorted in ascending order wrt the first field. Consider the following approach:
Transform the intervals from (start, number of characters) to (start, inclusive_end). Hence the above example becomes: [(1,2),(3,3),(4,2)] ==> [(1, 2), (3, 5), (4, 5)]
The tuples are valid if transformed consecutive tuples (a, b) and (c, d) always follow b < c. Else there is an overlap in the tuples mentioned above.
Each of 1 and 2 can be done in O(n) if the array is sorted in the form mentioned above.

Optimize queries performed on a subarray

I recently interviewed at Google. Because of this question my process didn't move forward after 2 rounds.
Suppose you are given an array of numbers. You can be given queries
to:
Find the sum of the values between indexes i and j.
Update value at index i to a new given value.
Find the maximum of the values between indexes i and j.
Check whether the subarray between indexes i and j, both inclusive, is in ascending or descending order.
I gave him a solution but it was to check the subarray between the indexes i and j. He asked me to optimize it. I thought of using a hashtable so that if the starting index is same and the ending index is more than the previous found, we store the maximum and whether its in ascending or descending and check only the remaining subarray. But that also didn't optimize it as much as required.
I'd love to know how I can optimize the solution so as to make it acceptable.
Constraints:
Everything from [1,10^5]
Thanks :)
All this queries can be answered in O(log N) time per query in the worst case(with O(N) time for preprocessing). You can just build a segment tree and maintain the sum, the maximum and two boolean flags(they indicate whether the range which corresponds to this node is sorted in ascending/descending order or not) for each node. All this values can be recomputed efficiently for an update query because only O(log N) nodes can change(they lie on the path from the root to a leaf which corresponds to the changing element). All other range queries(sum, max, sorted or not) are decomposed into O(log N) nodes(due to the properties of a segment tree), and it is easy to combine the value of two nodes in O(1)(for example, for sum the result of combining 2 nodes is just the sum of values for these nodes).
Here is some pseudo code. It shows what data should be stored in a node and how to combine values of 2 nodes:
class Node {
bool is_ascending
bool is_descending
int sum
int max
int leftPos
int rightPos
}
Node merge(Node left, Node right) {
res = Node()
res.leftPos = left.leftPos
res.rightPos = right.rightPos
res.sum = left.sum + right.sum
res.max = max(left.max, right.max)
res.is_ascending = left.is_ascending and right.is_ascending
and array[left.rightPos] < array[right.leftPos]
res.is_descending = left.is_descending and right.is_descending
and array[left.rightPos] > array[right.leftPos]
return res
}
As andy pointed out in the comments: The queries are quite different in nature, so the "best" solution will probably depend on which query type is executed most frequently.
However, the task
Find the sum of the values between indexes i and j.
can efficiently be solved by performing a scan/prefix sum computation of the array. Imagine an array of int values
index: 0 1 2 3 4 5 6
array: [ 3, 8, 10, -5, 2, 12, 7 ]
Then you compute the Prefix Sum:
index: 0 1 2 3 4 5 6, 7
prefix: [ 0, 3, 11, 21, 16, 18, 30, 37 ]
Note that this can be computed particularly efficient in parallel. In fact, this is one important building block of many parallel algorithms, as described in the thesis "Vector Models for Data-Parallel Computing" by Guy E. Blelloch (thesis as PDF File).
Additionally, it can be computed in two ways: Either starting with the value from array[0], or starting with 0. This will, of course, affect how the resulting prefix array has to be accessed. Here, I started with 0, and made the resulting array one element longer than the input array. This may also be implemented differently, but in this case, it makes it easier to obey the array limits (although one would still have to clarify in which cases indices should be considered as inclusive or exclusive).
However, given this prefix sum array, one can compute the sum of elements between indices i and j in constant time, by simply subtracting the corresponding values of the prefix sum array:
sum(n=i..j)(array[n]) = (prefix[j+1] - prefix[i])
For example:
sum(n=2..5)(array[n]) = 10 + (-5) + 2 + 12 = 19
prefix[5+1] - prefix[2] = 30 - 11 = 19
For task 2,
Update value at index i to a new given value.
this would mean that the prefix sums would have to be updated. This could be done brute-force, in linear time, by just adding the difference of the old value and the new value to all prefix sums that appear after the modified element (but for this, also see the notes in the last section of this answer)
The tasks 3 and 4
Find the maximum of the values between indexes i and j.
Check whether the subarray between indexes i and j, both inclusive, is in ascending or descending order.
I could imagine that the maximum value could simply be tracked while building the prefix sums, as well as checking whether the values are only ascending or descending. However, when values are updated, this information would have to be re-computed.
In any case, there are some data structures that deal with prefix sums in particular. I think that a Fenwick tree might allow to implement some of the O(n) operations mentioned above in O(logn), but I have not yet looked at this in detail.

Find the most frequent triplet in an array

We have an array of N numbers. All the numbers are between 1-k.
The problem is how to find the best way of finding the most frequent triplet.
My approach to the problem is:
Say if the input is like { 1, 2, 3, 4, 1, 2, 3, 4}
First search for the count of triplet ( 1, 2, 3) start from the second element in the array till the end of the array. Now we will have the count as 1.
Now start with { 2, 3, 4) and search the array.
for each triplet we scan the array and find the count. Like this we run the array for n-1 times.
This way my algorithm runs in the order of n*n time complexity. Is there any better way for
this problem.?
You can do it in O(n * log n) worst-case space and time complexity: Just insert all triples into a balanced binary search tree and find the maximum afterwards.
Alternatively, you can use a hash table to get O(n) expected time (which is typically faster than the search tree approach in reality, if you choose a good hash function).
Are there any memory boundaries i.e. does it run on a device with memory limitations?
If not, maybe this could be good solution: iterate over array and for each tripple build and representation object (or struct if implemented in c#) which goes into map as a key and the tripple counter as a value.
If you implement hash and equals functions appropriately, you will be able to find the "most popular" tripple where numbers order matters or not e.g. 1,2,3 != 2,1,3 or 1,2,3 == 2,1,3
After iterating entire array you would have to find the largest value and its key would be your "most popular" tripple. With that approach you could find X most popular tripples too. Also you would scan array only once and aggregate all the trippels (no extra scanning for each tripple).

Resources