Is there any way to optimize this Mergesort implementation? - sorting

I apologize in advance for any unconventional writing patterns, or any other apparent mistakes. Any advice or tips would be appreciated!
public class MergeSort {
private static final int INSERTION_THRESHOLD = 8;
public static void mergeSort(int[] x) {
mergeSort(x, 0, x.length);
}
private static void mergeSort(int[] x, int start, int end) {
int length=end-start;
if(length<INSERTION_THRESHOLD) {
for (int i=start; i<end; i++)
for (int j=i; j>start && x[j-1]>x[j]; j--)
swap(x, j, j-1);
return;
}
int mid=(start+end)>>>1;
mergeSort(x, 0, mid);
mergeSort(x, mid, end);
int[] space=Arrays.copyOfRange(x, 0, mid);
int i=0, j=mid, k=0;
while(i<mid&&j<end)
x[k++]=(x[j]<space[i])?x[j++]:space[i++];
while(i<mid)
x[k++]=space[i++];
}
private static void swap(int[] x, int n, int m) {
int temp = x[n];
x[n] = x[m];
x[m] = temp;
}
Here are a few test cases to show that it works.
public static void main(String[] args) {
int[] test1={ 4, 3, 2, 1, 6, 7, 4, 2 }; // 1 2 2 3 4 4 6 7
int[] test2={ 3, 2, 3, 5, 4, 2, 1, 7 }; // 1 2 2 3 3 4 5 7
int[] test3={ 1, 5, 4, 6, 7, 3, 2, 8 }; // 1 2 3 4 5 6 7 8
int[] test4={ 9, 8, 7, 6, 5, 4, 3, 2 }; // 2 3 4 5 6 7 8 9
int[] test5={-8, 7,-3, 4, 5, 1, 0,-3 }; //-8 -3 -3 0 1 4 5 7
mergeSort(test1);
mergeSort(test2);
mergeSort(test3);
mergeSort(test4);
mergeSort(test5);
System.out.println(Arrays.toString(test1));
System.out.println(Arrays.toString(test2));
System.out.println(Arrays.toString(test3));
System.out.println(Arrays.toString(test4));
System.out.println(Arrays.toString(test5));
}
}
What I am looking for here, is really any advice or ideas that might make this a better implementation. Am I getting it right, or is there some horribly hilarious mess-up here? Are there any spots that might be unnecessary in the code? Is there something that could be added to make this run more quickly/smoothly/more along the lines of a good Merge Sort?

Here you can take a look at different merge sort implementation in different languages, and find out the best solution for you, maybe improve your existing code. Here is the example of implementation in PHP from that website:
function mergesort($arr){
if(count($arr) == 1 ) return $arr;
$mid = count($arr) / 2;
$left = array_slice($arr, 0, $mid);
$right = array_slice($arr, $mid);
$left = mergesort($left);
$right = mergesort($right);
return merge($left, $right);
}
function merge($left, $right){
$res = array();
while (count($left) > 0 && count($right) > 0){
if($left[0] > $right[0]){
$res[] = $right[0];
$right = array_slice($right , 1);
}else{
$res[] = $left[0];
$left = array_slice($left, 1);
}
}
while (count($left) > 0){
$res[] = $left[0];
$left = array_slice($left, 1);
}
while (count($right) > 0){
$res[] = $right[0];
$right = array_slice($right, 1);
}
return $res;
}
$arr = array( 1, 5, 2, 7, 3, 9, 4, 6, 8);
$arr = mergesort($arr);
echo implode(',',$arr);
Hope it will help. Here is the website: http://rosettacode.org/wiki/Sorting_algorithms/Merge_sort

Related

How do I change merge sort for calculate distance of an array from sorted array?

NOTE: this problem is different from the count of inversions problem.
the distance of an array from sorted array defined as:
dist(A)=Sumation(j-i) for any i<j that A[i]>A[j]
. Simply it can calculate in O(n^2). My question is How to change merge sort to calculate distance in O(n log n)?
for example:
input: 5 2 3 4 1
output: 16
I don't want to count of inversions!
this function is different from inversions.
input: 5 2 3 4 1
dist(A): 16
inversions(A): 7
If you know how to calculate the number of inversion of an array through merge sort, you will solve this by changing several lines of code.
pay attention to the details.
// struct to store the values
//
struct pa {
int value, index;
};
// The merge step
// A is the left part, while B is the right part
// a_cnt is the number of elements in A
void merge(pa A[], pa B[], int a_cnt, int b_cnt) {
int st = 0;
int tmp_sum = 0;
for (int i = 0; i < a_cnt; ++i) {
while (st < b_cnt && B[st].value < A[i].value) {
tmp_sum += B[st].index;
st++;
}
ans += (tmp_sum - st * A[i].index)
}
// origional merge code here
}
You can solve this by inserting the numbers one by one in a sorted sequence and maintaining a suffix sum array. Here's how it works:
For each element in the array, have a pair. One contains the number itself and the other, its index. Now create another array and try to insert it in the sorted sequence.
Eg: [(5, 1), (2, 2), (3, 3), (4, 4), (1, 5)]
Insert 5
[(5, 1)]
Insert 2
[(2, 2), (5, 1)] => contributes (1*2 - 1) = > 1
Insert 3
[(2, 2), (3, 3), (5, 1)] => contributes (1*3 - 1) => 2
Insert 4
[(2, 2), (3, 3), (4, 4), (5, 1)] => contributes (1*4 - 1) => 3
Insert 1
[(1, 5), (2, 2), (3, 3), (4, 4), (5, 1)] => contributes (5 * 4 - (10) ) => 10
Sum all the contributions and you get 16
Time complexity: O(N * log N)
This can be easily done with the help of binary search tree.
You need to maintain the sum of indices of nodes present in right sub tree and number of nodes present in right sub tree. So whenever you are inserting a new node and it goes towards left of any node then distance will get updated by
`(no of nodes in right subtree* index of val which is to be inserted) - sum of indices of nodes present in right subtree)`
Lets do a walk through for input
5, 2, 3, 4, 1
first node will be with val 5 and distance till now is 0;
Situation after inserting 2
sizeOfRightSubTree : 1
index: 1
sumOfIndicesOnRight: 0
inserted: 2, distance: 1
After inserting 3
sizeOfRightSubTree : 1
index: 2
sumOfIndicesOnRight: 0
inserted: 3, distance: 3
After inserting 4
sizeOfRightSubTree : 1
index: 3
sumOfIndicesOnRight: 0
inserted: 4, distance: 6
After inserting 1. Notice it has to traverse left twice to reach its final position hence distance is updated twice.
sizeOfRightSubTree : 1
index: 4
sumOfIndicesOnRight: 0
sizeOfRightSubTree : 3
index: 4
sumOfIndicesOnRight: 6
inserted: 1, distance: 16
Following is the java code
public class DistanceFromSortedArray
{
class Node {
int val;
Node left;
Node right;
int index;
int sumOfIndicesOnRight;
int sizeOfRightSubTree;
Node(int num, int index)
{
this.val = num;
this.index = index;
sizeOfRightSubTree = 1;
sumOfIndicesOnRight = index;
}
void addIndexToRight(int index)
{
sizeOfRightSubTree++;
sumOfIndicesOnRight += index;
}
int distance(int index)
{
return sizeOfRightSubTree*index - sumOfIndicesOnRight;
}
}
private Node head;
private int distance;
public int calculate(int[] nums){
head = null;
distance = 0;
for(int i=0; i<nums.length; i++){
insert(nums[i], i);
}
return distance;
}
private void insert(int num, int index)
{
Node toInsert = new Node(num, index);
if(head == null){
head = toInsert;
return;
}
Node current = head;
Node previous = null;
while (current != null){
previous = current;
if(current.val > num){
distance += current.distance(index);
current = current.left;
}
else {
current.addIndexToRight(index);
current = current.right;
}
}
if(previous.val > num){
previous.left = toInsert;
}
else {
previous.right = toInsert;
}
}
}
Here are few test cases
#Test
public void calculate()
{
int[] nums = {5, 2, 3, 4, 1};
assertEquals(16, new DistanceFromSortedArray().calculate(nums));
}
#Test
public void reverseCalculate()
{
int[] nums = {5, 4, 3, 2, 1};
assertEquals(20, new DistanceFromSortedArray().calculate(nums));
}
#Test
public void SizeTwoCalculate()
{
int[] nums = {4, 5};
assertEquals(0, new DistanceFromSortedArray().calculate(nums));
int [] nums2 = {5, 4};
assertEquals(1, new DistanceFromSortedArray().calculate(nums2));
}
#Test
public void twistedCalculate()
{
int[] nums = {8, 3, 6, 5, 7, 1};
assertEquals(26, new DistanceFromSortedArray().calculate(nums));
}
#Test
public void AllSameCalculate()
{
int[] nums = {1, 1, 1, 1, 1, 1};
assertEquals(0, new DistanceFromSortedArray().calculate(nums));
}

All variations of a number

Im trying to find all the possible variations of a number in the form of:
'1_2_3_4' where _ is a number between 0 to 9.
I was wondering what is the best approach to this problem.
This seems like the simplest method:
static void printPerms()
{
int n = 1020304;
for (int i = 0; i <= 9; i++, n += 90000)
for (int j = 0; j <= 9; j++, n += 900)
for (int k = 0; k <= 9; k++, n += 10)
System.out.println(n);
}
Or even this, which has a lovely symmetry:
static void printPerms()
{
int n = 1020304;
for (int ni = n + 900000; n <= ni; n += 90000)
for (int nj = n + 9000; n <= nj; n += 900)
for (int nk = n + 90; n <= nk; n += 10)
System.out.println(n);
}
import java.util.*;
public class Solution {
public static void main(String[] args){
int[] fillable = {1,-1,2,-1,3,-1,4};
for(int i=0;i<=9;++i){
for(int j=0;j<=9;++j){
for(int k=0;k<=9;++k){
fillable[1] = i;
fillable[3] = j;
fillable[5] = k;
System.out.println(Arrays.toString(fillable));
}
}
}
}
}
OUTPUT:
[1, 0, 2, 0, 3, 0, 4]
[1, 0, 2, 0, 3, 1, 4]
[1, 0, 2, 0, 3, 2, 4]
[1, 0, 2, 0, 3, 3, 4]
[1, 0, 2, 0, 3, 4, 4]
[1, 0, 2, 0, 3, 5, 4]
[1, 0, 2, 0, 3, 6, 4]
.
.
.
.
Time Complexity: O(10^n) where n is no. of places to fill in. If 3 empty places if fixed, then it is O(1).
Space Complexity: O(1)
Note: There is no better way to do this. You have to go through each and every combination.
Python style, assuming ASCII code representation:
n= "1020304"
while True:
n[5]+= 1
if n[5] == ':':
n[5]= '0'
n[3]+= 1
if n[3] == ':':
n[3]= '0'
n[1]+= 1
if n[1] == ':'=
break

Algorithms: distribute elements far away from each other

I have an array of sorted integers. Given an integer N i need to place N largest elements further away from each other so that they have maximum space between each other. The remaining elements should be placed between these big items. For example, array of 10 with N=3 would result in [0, 5, 8, 2, 6, 9, 3, 7, 10, 4].
public static void main(String[] args) {
int[] start = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
int[] end = new int[10];
int N = 4;
int step = Math.round(start.length / N );
int y = 0;
int count = 0;
for (int i = 0; i < step; i++) {
for (int j = i; j<start.length; j = j + step) {
//System.out.println(j + " " + i);
if (count < start.length && start[count] != 0) {
end[j] = start[count];
count++;
}
}
}
System.out.println(end.toString());
}
You have an array of K elements. You have N max numbers you need to distribute. Then:
Step := K/N (removing the remainder)
Take any number from N maximum and insert it at Step/2 position.
Take other maximum numbers and insert it after the previous inserted maximum number at Step distance.
Giving [1,2,3,4,5,6,7,8,9,10]. So K = 10, N = 3. Then Step = 3. So the first maximum is placed at 3/2 position
[1,10,2,3,4,5,6,7,8,9]
Then other 2 are put at 3 distance from each other:
[1,10,2,3,9,4,5,8,6,7]
The code:
std::vector<int> Distribute(std::vector<int> aSource, int aNumber)
{
auto step = aSource.size() / aNumber; // Note integer dividing.
for (int i = 0; i < aNumber; ++i)
{
auto place = aSource.end() - i * step - step / 2;
aSource.insert(place, aSource.front());
aSource.erase(aSource.begin());
}
return aSource;
}
int main()
{
std::vector<int> vec{10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10};
auto res = Distribute(vec, 4);
for (auto e : res)
{
std::cout << e << ", ";
}
std::cout << std::endl;
}
Output:
6, 5, 4, 7, 3, 2, 1, 0, 8, -1, -2, -3, -4, 9, -5, -6, -7, -8, 10, -9, -10,

Algorithm puzzle: minimum cost for allow all persons standing on a line to communicate with each other

I have a algorithm design puzzle that I could not solve.
The puzzle is formulated like this: There are N persons standing on a number line, each of them maybe standing on any integer number on that line. Multiple persons may stand on the same number. For any two persons to be able to communicate with each other, the distance between them should be less than K. The goal is to move them so that each pair of two persons can communicate each other (possibly via other people). In other words, we need to move them so that the distance between any neighboring two persons is smaller than K.
Question: What is the minimum number of total moves? It feels like this falls into greedy algorithm family or dynamic programming. Any hints are appreciated!
We can do the following in O(n):
Calculate the cost of moving all people to the right of person i towards person i at an acceptable distance:
costRight(A[i]) = costRight(A[i+1]) + (A[i+1] - A[i] - k + 1) * count of people to the right
K = 3; A = { 0, 3, 11, 17, 21}
costRight = {32, 28, 10, 2, 0}
Calculate the cost of moving all people to the left of person i towards person i at an acceptable distance:
costLeft(A[i]) = costLeft(A[i-1]) + (A[i] - A[i-1] - k + 1) * count of people to the left
K = 3; A = { 0, 3, 11, 17, 21}
costLeft = { 0, 1, 13, 25, 33}
costRight = {32, 28, 10, 2, 0}
Now that we have cost from both directions we can do this in O(n):
minCost = min(costRight + costLeft) for all A[i]
minCost = min(32 + 0, 28 + 1, 13 + 10, 25 + 2, 33 + 0) = 23
But sometimes that's no enough:
K = 3; A = { 0, 0, 1, 8, 8}
carry: -2 -4 3
costLeft = { 0, 0, 0, 11, 11}
carry: -3 5 -2
costRight = { 8, 8, 8, 0, 0}
The optimum is neither 11 nor 8. Test the current best by moving towards the greatest saving:
move 1 to 2, cost = 1
K = 3; A = { 0, 0, 2, 8, 8}
carry: -2 -2 -10
costLeft = { 0, 0, 0, 10, 10}
carry: -2 -2
costRight = { 6, 6, 6, 0, 0}
minCost = 1 + min(0 + 6, 0 + 6, 0 + 6, 10 + 0, 10 + 0) = 1 + 6 = 7
Not quite sure how to formularize this efficiently.
Here is a greedy algorithm written in Java, but I don't know if it gives the optimal solution in every case. Also it is more a proof of concept, there is some room for optimizations.
It is based on the fact that two neighbouring persons must not be more than K apart, the next neighbour must not be more than 2K away and so on. In each step we move the person that "violates these constraints most". The details of this calculation are in method calcForce.
package so;
import java.util.Arrays;
public class Main {
public static void main(String args[]) {
int[] position = new int[] {0, 0, 5, 11, 17, 23};
int k = 5;
solve(position, k);
}
private static void solve(int[] position, int k) {
if (!sorted(position)) {
throw new IllegalArgumentException("positions must be sorted");
}
int[] force = new int[position.length];
int steps = 0;
while (calcForce(position, k, force)) {
int mp = -1;
int mv = -1;
for (int i = 0; i < force.length; i++) {
if (mv < Math.abs(force[i])) {
mv = Math.abs(force[i]);
mp = i;
}
}
System.out.printf("move %d to the %s%n", mp, force[mp] > 0 ? "right" : "left");
if (force[mp] > 0) {
position[mp]++;
} else {
position[mp]--;
}
steps++;
}
System.out.printf("total: %d steps%n", steps);
}
private static boolean calcForce(int[] position, int k, int[] force) {
boolean commProblem = false;
Arrays.fill(force, 0);
for (int i = 0; i < position.length - 1; i++) {
for (int j = i + 1; j < position.length; j++) {
int f = position[j] - position[i] - (j - i) * k;
if (f > 0) {
force[i] += f;
force[j] -= f;
commProblem = true;
}
}
}
return commProblem;
}
private static boolean sorted(int[] position) {
for (int i = 0; i < position.length - 1; i++) {
if (position[i] > position[i+1]) {
return false;
}
}
return true;
}
}

Find longest increasing sequence

You are given a sequence of numbers and you need to find a longest increasing subsequence from the given input(not necessary continuous).
I found the link to this(Longest increasing subsequence on Wikipedia) but need more explanation.
If anyone could help me understand the O(n log n) implementation, that will be really helpful. If you could explain the algo with an example, that will be really appreciated.
I saw the other posts as well and what I did not understand is:
L = 0
for i = 1, 2, ... n:
binary search for the largest positive j ≤ L such that X[M[j]] < X[i] (or set j = 0 if no such value exists)
above statement, from where to start binary search? how to initialize M[], X[]?
A simpler problem is to find the length of the longest increasing subsequence. You can focus on understanding that problem first. The only difference in the algorithm is that it doesn't use the P array.
x is the input of a sequence, so it can be initialized as:
x = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15]
m keeps track of the best subsequence of each length found so far. The best is the one with the smallest ending value (allowing a wider range of values to be added after it). The length and ending value is the only data needed to be stored for each subsequence.
Each element of m represents a subsequence. For m[j],
j is the length of the subsequence.
m[j] is the index (in x) of the last element of the subsequence.
so, x[m[j]] is the value of the last element of the subsequence.
L is the length of the longest subsequence found so far. The first L values of m are valid, the rest are uninitialized. m can start with the first element being 0, the rest uninitialized. L increases as the algorithm runs, and so does the number of initialized values of m.
Here's an example run. x[i], and m at the end of each iteration is given (but values of the sequence are used instead of indexes).
The search in each iteration is looking for where to place x[i]. It should be as far to the right as possible (to get the longest sequence), and be greater than the value to its left (so it's an increasing sequence).
0: m = [0, 0] - ([0] is a subsequence of length 1.)
8: m = [0, 0, 8] - (8 can be added after [0] to get a sequence of length 2.)
4: m = [0, 0, 4] - (4 is better than 8. This can be added after [0] instead.)
12: m = [0, 0, 4, 12] - (12 can be added after [...4])
2: m = [0, 0, 2, 12] - (2 can be added after [0] instead of 4.)
10: m = [0, 0, 2, 10]
6: m = [0, 0, 2, 6]
14: m = [0, 0, 2, 6, 14]
1: m = [0, 0, 1, 6, 14]
9: m = [0, 0, 1, 6, 9]
5: m = [0, 0, 1, 5, 9]
13: m = [0, 0, 1, 5, 9, 13]
3: m = [0, 0, 1, 3, 9, 13]
11: m = [0, 0, 1, 3, 9, 11]
7: m = [0, 0, 1, 3, 7, 11]
15: m = [0, 0, 1, 3, 7, 11, 15]
Now we know there is a subsequence of length 6, ending in 15. The actual values in the subsequence can be found by storing them in the P array during the loop.
Retrieving the best sub-sequence:
P stores the previous element in the longest subsequence (as an index of x), for each number, and is updated as the algorithm advances. For example, when we process 8, we know it comes after 0, so store the fact that 8 is after 0 in P. You can work backwards from the last number like a linked-list to get the whole sequence.
So for each number we know the number that came before it. To find the subsequence ending in 7, we look at P and see that:
7 is after 3
3 is after 1
1 is after 0
So we have the subsequence [0, 1, 3, 7].
The subsequences ending in 7 or 15 share some numbers:
15 is after 11
11 is after 9
9 is after 6
6 is after 2
2 is after 0
So we have the subsequences [0, 2, 6, 9, 11], and [0, 2, 6, 9, 11, 15] (the longest increasing subsequence)
One of the best explanation to this problem is given by MIT site.
http://people.csail.mit.edu/bdean/6.046/dp/
I hope it will clear all your doubts.
based on FJB's answer, java implementation:
public class Lis {
private static int[] findLis(int[] arr) {
int[] is = new int[arr.length];
int index = 0;
is[0] = index;
for (int i = 1; i < arr.length; i++) {
if (arr[i] < arr[is[index]]) {
for (int j = 0; j <= index; j++) {
if (arr[i] < arr[is[j]]) {
is[j] = i;
break;
}
}
} else if (arr[i] == arr[is[index]]) {
} else {
is[++index] = i;
}
}
int[] lis = new int[index + 1];
lis[index] = arr[is[index]];
for (int i = index - 1; i >= 0; i--) {
if (is[i] < is[i + 1]) {
lis[i] = arr[is[i]];
} else {
for (int j = is[i + 1] - 1; j >= 0; j--) {
if (arr[j] > arr[is[i]] && arr[j] < arr[is[i + 1]]) {
lis[i] = arr[j];
is[i] = j;
break;
}
}
}
}
return lis;
}
public static void main(String[] args) {
int[] arr = new int[] { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11,
7, 15 };
for (int i : findLis(arr)) {
System.out.print(i + "-");
}
System.out.println();
arr = new int[] { 1, 9, 3, 8, 11, 4, 5, 6, 4, 19, 7, 1, 7 };
for (int i : findLis(arr)) {
System.out.print(i + "-");
}
System.out.println();
}
}
Below is the O(NLogN) longest increasing subsequence implementation:
// search for the index which can be replaced by the X. as the index can't be
//0 or end (because if 0 then replace in the findLIS() and if it's greater than the
//current maximum the just append)of the array "result" so most of the boundary
//conditions are not required.
public static int search(int[] result, int p, int r, int x)
{
if(p > r) return -1;
int q = (p+r)/2;
if(result[q] < x && result[q+1]>x)
{
return q+1;
}
else if(result[q] > x)
{
return search(result, p, q, x);
}
else
{
return search(result, q+1, r, x);
}
}
public static int findLIS(int[] a)
{
int[] result = new int[a.length];
result[0] = a[0];
int index = 0;
for(int i=1; i<a.length; i++)
{
int no = a[i];
if(no < result[0]) // replacing the min number
{
result[0] = no;
}
else if(no > result[index])//if the number is bigger then the current big then append
{
result[++index] = no;
}
else
{
int c = search(result, 0, index, no);
result[c] = no;
}
}
return index+1;
}
Late to the party, but here's a JavaScript implementation to go along with the others.. :)
var findLongestSubsequence = function(array) {
var longestPartialSubsequences = [];
var longestSubsequenceOverAll = [];
for (var i = 0; i < array.length; i++) {
var valueAtI = array[i];
var subsequenceEndingAtI = [];
for (var j = 0; j < i; j++) {
var subsequenceEndingAtJ = longestPartialSubsequences[j];
var valueAtJ = array[j];
if (valueAtJ < valueAtI && subsequenceEndingAtJ.length > subsequenceEndingAtI.length) {
subsequenceEndingAtI = subsequenceEndingAtJ;
}
}
longestPartialSubsequences[i] = subsequenceEndingAtI.concat();
longestPartialSubsequences[i].push(valueAtI);
if (longestPartialSubsequences[i].length > longestSubsequenceOverAll.length) {
longestSubsequenceOverAll = longestPartialSubsequences[i];
}
}
return longestSubsequenceOverAll;
};
Based on #fgb 's answer, I implemented the algorithm using c++ to find the longest strictly increasing sub-sequence. Hope this will be somewhat helpful.
M[i] is the index of the last element of the sequence whose length is i, P[i] is the index of the previous element of i in the sequence, which is used to print the whole sequence.
main() is used to run the simple test case: {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15}.
#include <vector>
using std::vector;
int LIS(const vector<int> &v) {
int size = v.size(), max_len = 1;
// M[i] is the index of the last element of the sequence whose length is i
int *M = new int[size];
// P[i] is the index of the previous element of i in the sequence, which is used to print the whole sequence
int *P = new int[size];
M[0] = 0; P[0] = -1;
for (int i = 1; i < size; ++i) {
if (v[i] > v[M[max_len - 1]]) {
M[max_len] = i;
P[i] = M[max_len - 1];
++max_len;
continue;
}
// Find the position to insert i using binary search
int lo = 0, hi = max_len - 1;
while (lo <= hi) {
int mid = lo + ((hi - lo) >> 1);
if (v[i] < v[M[mid]]) {
hi = mid - 1;
} else if (v[i] > v[M[mid]]) {
lo = mid + 1;
} else {
lo = mid;
break;
}
}
P[i] = P[M[lo]]; // Modify the previous pointer
M[lo] = i;
}
// Print the whole subsequence
int i = M[max_len - 1];
while (i >= 0) {
printf("%d ", v[i]);
i = P[i];
}
printf("\n");
delete[] M, delete[] P;
return max_len;
}
int main(int argc, char* argv[]) {
int data[] = {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15};
vector<int> v;
v.insert(v.end(), data, data + sizeof(data) / sizeof(int));
LIS(v);
return 0;
}
The O(N lg N) solution comes from patience sorting of playing card. I found this from my code comment and hence sharing here. I believe it would be really easier to understand for everyone how it works. Also you can find all possible longest increasing sub-sequence list if you understand well.
https://www.cs.princeton.edu/courses/archive/spring13/cos423/lectures/LongestIncreasingSubsequence.pdf
Code:
vector<int> lisNlgN(vector<int> v) {
int n = v.size();
vector<int> piles = vector<int>(n, INT_MAX);
int maxLen = 0;
for(int i = 0; i < n; i++) {
int pos = lower_bound(piles.begin(), piles.end(), v[i]) - piles.begin();
piles[pos] = v[i];
maxLen = max(maxLen, pos+1); // Plus 1 because of 0-based index.
}
// // Print piles for debug purpose
// for (auto x : piles) cout << x << " ";
// cout << endl;
//
// // Print position for debug purpose
// for (auto x : position) cout << x << " ";
// cout << endl;
vector<int> ret = vector<int>(piles.begin(), piles.begin() + maxLen);
return ret;
}
Code:
vector<vector<int>> allPossibleLIS(vector<int> v) {
struct Card {
int val;
Card* parent = NULL;
Card(int val) {
this->val = val;
}
};
auto comp = [](Card* a, Card* b) {
return a->val < b->val;
};
int n = v.size();
// Convert integers into card node
vector<Card*> cards = vector<Card*>(n);
for (int i = 0; i < n; i++) cards[i] = new Card(v[i]);
vector<Card*> piles = vector<Card*>(n, new Card(INT_MAX));
vector<Card*> lastPileCards;
int maxLen = 0;
for(int i = 0; i < n; i++) {
int pos = lower_bound(piles.begin(), piles.end(), new Card(v[i]), comp) - piles.begin();
piles[pos] = cards[i];
// Link to top card of left pile
if (pos == 0) cards[i]->parent = NULL;
else cards[i]->parent = piles[pos-1];
// Plus 1 because of 0-based index.
if (pos+1 == maxLen) {
lastPileCards.push_back(cards[i]);
} else if (pos+1 > maxLen) {
lastPileCards.clear();
lastPileCards.push_back(cards[i]);
maxLen = pos + 1;
}
}
// Print for debug purpose
// printf("maxLen = %d\n", maxLen);
// printf("Total unique lis list = %d\n", lastPileCards.size());
vector<vector<int>> ret;
for (auto card : lastPileCards) {
vector<int> lis;
Card* c = card;
while (c != NULL) {
lis.push_back(c->val);
c = c->parent;
}
reverse(lis.begin(), lis.end());
ret.push_back(lis);
}
return ret;
}

Resources