Create 3BinaryInsertionSort.md

This commit is contained in:
Aakash Panchal 2020-04-13 14:26:07 +05:30 committed by GitHub
parent 174c4e6ddc
commit 46f3b756fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -0,0 +1,147 @@
## Binary Insertion Sort
As we know the main idea of Insertion sort is to take an element and insert it at the correct position. Now, we are going to use binary search to find the correct position, rather than a simple loop.
After finding the correct index, we right-shift elements and insert the element at the correct index.
For an array `[1 4 5 9 10 7 14]`, let's start binary insertion sort from element 7,
![enter image description here](https://lh3.googleusercontent.com/gYDe1Dsz1eezOcERB8Y5f1rRBEaEkIIRBiOOtKyAt5h70VYxCiBDPNS69DQv8UmLKpJeUqzzXzkn)
![enter image description here](https://lh3.googleusercontent.com/Rrs9w_oXDgYnuaG4R7hX7qre5p2GhgcydJr_bd_eDRoFaryac2yszu5OUAKvs3wvFMMbcWX2coFJ)
![enter image description here](https://lh3.googleusercontent.com/t2mXAlL2PSJSh3n-lnzAkuRy1qK9fRFq0Q7JCu5A4pl1OOspOkDYbrclAiqEwpvn3gp50pgD8DYQ)
![enter image description here](https://lh3.googleusercontent.com/FxEgcwtRKHaeVrjQr68BZH_ZsEvBBzstFfgfWnos3N5NuMm5Hiy1lnFuZlDL6x499m6WNEJiRJZ8)
![enter image description here](https://lh3.googleusercontent.com/tOY16Ca5Te94D2LdU9N85d9LSodAwQDT35NaiM3Mw7NWib0pkGe6oZvVOiKorsL54apXh_owwBB6)
![enter image description here](https://lh3.googleusercontent.com/Iks1metkG937y-OAataP7HVsn-dW8nqi3ZVcySdFyjTl6BM3fRxeJpinel2unbfa5CwLvnP4tS9S)
Similarly, for $14$, but it is already at its right position.
```cpp
// @param start: Position of next element to be inserted
// @param low: Lower index of the range
// @param high: Upper index of the range
void binaryInsertionsort(vector<int>& data, int start, int low, int high)
{
if (start == low)
start++;
// Iterate from the start index to high-1
for (; start < high; start++) {
int ele = data[start];
// Now find a correct position using binary search
int left = low, right = start;
while (left < right) {
int mid = (left + right) >> 1;
if (data[mid] > ele)
right = mid;
else
left = mid + 1;
}
int n = start - left;
// Bring it at the right position
if (n > 0) {
int j = start;
while (j != left) {
swap(data[j], data[j - 1]);
j--;
}
}
}
}
```
**Time complexity:** $\mathcal{O}(N^2)$, due to swap operations after finding index using binary search. But the constant factor is lesser than ordinary insertion sort.
Now, we have understood how to find runs. Next, we are going to see how to merge them?
## Merging
While we merging runs, we take too much care to maintain stability. In order to maintain **stability** we always merge consecutive runs, because otherwise, it may result in instability.
**For example**, [2 3 4], [1 2 5], [2 4 5] are three consecutive runs, so if we merge first and third run first, then 2 of the third run will end up before 2 of the second run in the later merge operation.
Now, to maintain information about runs, we are going to use an array, **which is used as a stack**, so whenever a new run comes, we insert it at the top. Now, let's discuss some criteria about merging these runs.
Merging lists of similar sizes tend to perform better than the other case, due to relatively simple logic. It is called **"balanced merge"**.
Now, let's discuss some criteria we use to merge runs efficiently.
![enter image description here](https://lh3.googleusercontent.com/oNnCn_wv0fTCSN-myagncWWjagqzEoVfnGOz0n2srsmWu8HZKWr88tPIVH-Cr9_LJQZ8cxH4jPfb)
**Note:** $|X|$ is a size of a run X.
**Criterion 1:** If the stack-size is greater than or equal to 3 and $|Z| <= |Y| + |X|$ is true,
Then if $|Z|<|X|$, then merge $|Z|$ and $|Y|$ otherwise merge $|X|$ and $|Y|$.
**Criterion 2:** $|Y|<=|X|$ then merge them.
Whenever we push a new run into the stack, we check for these criteria and merge runs accordingly until none of these criteria satisfy. The function does this work is called `mergecollapse`.
Below is a stack of runs, for the shake of simplicity only length of runs are shown.
![enter image description here](https://lh3.googleusercontent.com/JlBHBgdvLdISBatKmFg1Iq5G8MWQakZ930D3Am7uBFeosJTuYUpgAAL3JDxVRyev1t6mQaaomGL1)
Merge until none of these criteria satisfy. Criteria 1 is satisfying so merge,
![enter image description here](https://lh3.googleusercontent.com/7b9_Ogdf_Pg-gLDoedoMelqCapTcyVCIyZR_oi1JCn_NFIr00CQA8NuG2fToH5r0fnSXY4WZxETv)
Again, criteria 1 is satisfying so again merge,
![enter image description here](https://lh3.googleusercontent.com/IEoTH8KmnZbxYBW1EldpAjXKmBUnDozgLnj977d9E_Vw0y-URZMLjE1Y6iq0DDD70BWRLIvxbhTt)
No criteria are satisfying now, so stop.
**Note:** <code>mergeAt()</code> function is used to merge two runs, will be discussed.
```cpp
// This method is called each time a new run is pushed onto the stack
void mergecollapse(vector<int>& data) {
while (stackSize > 1) {
int n = (int) stackSize - 2;
if (n > 0 && stack_of_runs[n - 1].len <= stack_of_runs[n].len + stack_of_runs[n + 1].len) {
if (stack_of_runs[n - 1].len < stack_of_runs[n + 1].len)
n--;
// Procedure to merge runs at id n and n+1
mergeAt(data, n);
}
else if (stack_of_runs[n].len <= stack_of_runs[n + 1].len)
// Procedure to merge runs at id n and n+1
mergeAt(data, n);
else
break;
}
}
```
**There are two things to take note:**
1. In order to have a balanced merge, we are delaying the merge by waiting for the next run.
2. If we want to take advantage of cache memory, that is fresh runs are already in the cache, therefore, merging them have less memory overhead, then we should merge the fresh runs as soon as possible.
So, by taking care of both the things, criteria are decided.
Each time a run new is pushed on the stack we call `mergeCollapse` procedure, but it runs until criteria satisfy. Therefore, at the end of this procedure, it is possible that we have more than $1$ runs in the stack.
**Note:** At the end of the merging procedure, `stackSize = 1` means that this last run is the whole sorted array.
So, at the end of the merging procedure(after we have found the last run of the array), if the stack size is more than 1, then we will forcefully merge all the remaining runs. The function does this work is called `mergeForceCollapse()`.
```cpp
// Merges all runs on the stack until only one remains. This method is
// called once, to complete the sort at last.
void mergeForceCollapse(vector<int>& data)
{
while (stackSize > 1) {
int n = stackSize - 2;
if (n > 0 && stack_of_runs[n - 1].len < stack_of_runs[n + 1].len)
n--;
// Procedure to merge runs at id n and n+1
mergeAt(data, n);
}
}
```
**Note:** It will be more clear when we will discuss the final "Tim sort function".