12

I had previously posted a question, Given an array, find out the next smaller element for each element now, i was trying to know , if there is any way to find out "given an array, for each element, find out the total number of elements lesser than it, which appear to the right of it" for example, the array [4 2 1 5 3] should yield [3 1 0 1 0]??

[EDIT] I have worked out a solution, please have a look at it, and let me know if there is any mistake.

1 Make a balanced BST inserting elements traversing the array from right to left

2 The BST is made in such a way that each element holds the size of the tree rooted at that element

3 Now while you search for the right position to insert any element, take account of the total size of the subtree rooted at left sibling + 1(for parent) if you move right Now since, the count is being calculated at the time of insertion of an element, and that we are moving from right to left, we get the exact count of elements lesser than the given element appearing after it.

Community
  • 1
  • 1
brut3f0rc3
  • 1,173
  • 4
  • 13
  • 19
  • we can also solve this problem optimally in O(nlogn) time using modified mergesort (divide and conquer paradigm). example is [here](https://stackoverflow.com/questions/9495843/given-an-array-for-each-element-find-out-the-total-number-of-elements-lesser-t/48096211#48096211) – Amit Upadhyay Jan 04 '18 at 13:32

10 Answers10

11

It can be solved in O(n log n).

If in a BST you store the number of elements of the subtree rooted at that node when you search the node (reaching that from the root) you can count number of elements larger/smaller than that in the path:

int count_larger(node *T, int key, int current_larger){
    if (*T == nil)
        return -1;
    if (T->key == key)
        return current_larger + (T->right_child->size);
    if (T->key > key)
        return count_larger(T->left_child, key, current_larger + (T->right_child->size) + 1);
    return count_larger(T->right_child, key, current_larger)
}

** for example if this is our tree and we're searching for key 3, count_larger will be called for:

-> (node 2, 3, 0)
--> (node 4, 3, 0)
---> (node 3, 3, 2)

and the final answer would be 2 as expected.

a-z
  • 1,634
  • 4
  • 16
  • 35
  • No, this won't work you are first constructing the tree, now suppose the control goes to if (T->key > key) return count_larger(T->left_child, key, current_larger + (T->right_child->size) + 1); while searching for any element.. the problem is that (T->right_child->size) + 1); will include elements that have been inserted prior to the element being searched.. – brut3f0rc3 Feb 29 '12 at 11:04
  • @RamanBhatia It would work. For each element starting from the right, (1) increment the count for that element and update the tree, and (2) look up the cumulative count. When you do a lookup, the tree contains only the items to the right of the current element and the element itself. – tom Feb 29 '12 at 11:52
  • yeah.. that's what i have posted (have edited the question, and posted my solution there) and i mistook your " when you search the node (reaching that from the root)" as doing a search after constructing the entire tree, for each element.. My bad.. – brut3f0rc3 Feb 29 '12 at 12:26
  • @RamanBhatia :+1 to question . what does the size mean in "(T->right_child->size)" is it a special field in node or something else.. what does he mean when a-z says " you store the number of elements of the subtree rooted at that node when you search the node (reaching that from the root) " . pls explain with a small input data . thanks in advance – Imposter Oct 24 '12 at 12:02
  • @Imposter: "T->right_child" is a pointer to right child of node *T in the tree. We store the size of the subtree rooted at a node (e.g. *T) in a variable named "size"; so "T->right_child->size" means size of subtree rooted at right child of *T. The algorithm is just a searching for a key in the BST, we just count the number of elements that are larger than our key and are outside the subtree we're going next(left or right). – a-z Nov 02 '12 at 04:29
4

Suppose the Array is 6,-1,5,10,12,4,1,3,7,50

Steps

1.We start building a BST from right end of the array.Since we are concerned with all the elements to right for any element.

2.Suppose we have formed the partial solution tree upto the 10.

enter image description here

3.Now when inserting 5 we do a tree traversal and insert to the right of 4. Notice that each time we traverse to the right of any node we increment by 1 and add the no. of elements in left subtree of that node. eg:
for 50 it is 0
for 7 it is 0
for 12 it is 1 right traversel + leftsubtree size of 7 = 1+3 =4
for 10 same as above.
for 4 it is 1+1 =2

While building bst we can easily maintain the left subtree size for each node by simply maintaining a variable corresponding to it and incrementing it by 1 each time a node traverses to the left by it.
Hence the Solution Average case O(nlogn).

We can use other optimizations such as predetermining whether array is sorted in decreasing order find groups of element in decreasing order treat them as single.

utkarsh dubey
  • 875
  • 7
  • 19
  • Just note that although using a BST will work, but the worst case complexity will be O(n^2) input array is already sorted. (because the BST will be fully skewed) – totsubo Sep 26 '19 at 12:11
2

You can also use binary Index tree

int tree[1000005];
void update(int idx,int val)
{
   while(idx<=1000000)
   {
       tree[idx]+=val;
       idx+=(idx & -idx);
   }
}

int sum(int idx)
{
    int sm=0;
    while(idx>0)
    {
       sm+=tree[idx];
       idx-=(idx & -idx);
    }
    return sm;
}

int main()
{
    int a[]={4,2,1,5,3};
    int s=0,sz=6;
    int b[10];
    b[sz-1]=0;
    for(int i=sz-2;i>=0;i--)
    {
        if(a[i]!=0)
        {
            update(a[i],1);
            b[i]=sum(a[i]-1)+s;
        }
        else s++;
    }
    for(int i=0;i<sz-1;i++)
    {
       cout<<b[i]<<" ";
    }
   return 0;
}
  • This program will break if a[i]<0. Nice idea if all the elements in the array are going to be positive, doesnt work for general case. One solution for arrays with negative element that I can think of is find the min element and add absolute value of the min element to all the elements, so the min element is now 0 and the solution wont change. – Shankhoneer Chakrovarty Jul 19 '16 at 07:30
2

I think is it possible to do it in O(nlog(n))with a modified version of quicksort. Basically each time you add an element to less, you check if this element rank in the original array was superior to the rank of the current pivot. It may look like

oldrank -> original positions 
count -> what you want
function quicksort('array')
  if length('array') ≤ 1
      return 'array'  // an array of zero or one elements is already sorted
  select and remove a pivot value 'pivot' from 'array'
  create empty lists 'less' and 'greater'
  for each 'x' in 'array'
      if 'x' ≤ 'pivot' 
         append 'x' to 'less'
         if oldrank(x) > = oldrank(pivot)  increment count(pivot)
      else 
         append 'x' to 'greater'
         if oldrank(x) <  oldrank(pivot)  increment count(x) //This was missing
  return concatenate(quicksort('less'), 'pivot', quicksort('greater')) // two recursive calls

EDIT:

Actually it can be done using any comparison based sorting algorithm . Every time you compare two elements such that the relative ordering between the two will change, you increment the counter of the bigger element.

Original pseudo-code in wikipedia.

UmNyobe
  • 22,539
  • 9
  • 61
  • 90
  • 2
    No, it won't work. The pivot in the second recursive call needs to be aware of the 'other half', but it isn't. Nice idea though. – tom Feb 29 '12 at 10:19
  • 1
    I am afraid it still doesn't work. The elements in `greater` need to be aware of all the elements in `less`, not just the pivot. – tom Feb 29 '12 at 11:31
1

Another approach without using the tree.

  1. Construct another sorted array . For example for input array {12, 1, 2, 3, 0, 11, 4} it will be {0, 1, 2, 3, 4, 11, 12}
  2. Now compare position of each element from input array with sorted array.For example 12 in first array is at 0 index while sorted array it’s as 6
  3. Once comparison is done, remove element from both array
M Sach
  • 33,416
  • 76
  • 221
  • 314
1

Other than using BST, we can also solve this problem optimally by doing some modification in merge sort algorithm (in O(n*logn) time).

If you observe this problem more carefully, you can say that in the problem we need to count the number of inversions required for each element to make the array sorted in ascending order, right?

So this problem can be solved using Divide and Conquer paradigm. Here you need to maintain an auxiliary array for storing the count of inversions required (i.e. elements smaller than it on the right side of it).

Below is a python program:

def mergeList(arr, pos, res, start, mid, end):
    temp = [0]*len(arr)
    for i in range(start, end+1):
        temp[i] = pos[i]

    cur = start
    leftcur = start
    rightcur = mid + 1

    while leftcur <= mid and rightcur <= end:

        if arr[temp[leftcur]] <= arr[temp[rightcur]]:
            pos[cur] = temp[leftcur]
            res[pos[cur]] += rightcur - mid - 1
            leftcur += 1
            cur += 1
        else:
            pos[cur] = temp[rightcur]
            cur += 1
            rightcur += 1

    while leftcur <= mid:
        pos[cur] = temp[leftcur]
        res[pos[cur]] += end - mid
        cur += 1
        leftcur += 1

    while rightcur <= end:
        pos[cur] = temp[rightcur]
        cur += 1
        rightcur += 1


def mergeSort(arr, pos, res, start, end):
    if start < end:
        mid = (start + end)/2
        mergeSort(arr, pos, res, start, mid)
        mergeSort(arr, pos, res, mid+1, end)
        mergeList(arr, pos, res, start, mid, end)


def printResult(arr, res):
    print
    for i in range(0, len(arr)):
        print arr[i], '->', res[i]


if __name__ == '__main__':
    inp = input('enter elements separated by ,\n')
    inp = list(inp)
    res = [0]*len(inp)
    pos = [ind for ind, v in enumerate(inp)]
    mergeSort(inp, pos, res, 0, len(inp)-1)
    printResult(inp, res)

Time : O(n*logn)

Space: O(n)

Amit Upadhyay
  • 7,179
  • 4
  • 43
  • 57
1
//some array called newarray
for(int x=0; x <=array.length;x++)
{
for(int y=x;y<array.length;y++)
{
if(array[y] < array[x])
{
newarray[x] = newarray[x]+1;
}
}
}

something like this,where array is your input array and newarray your output array make sure to initialize everything correctly(0 for the newarrays values)

Volker Mauel
  • 514
  • 3
  • 21
0

You can also use an array instead of a binary search tree.

def count_next_smaller_elements(xs):
    # prepare list "ys" containing item's numeric order
    ys = sorted((x,i) for i,x in enumerate(xs))
    zs = [0] * len(ys)

    for i in range(1, len(ys)):
        zs[ys[i][1]] = zs[ys[i-1][1]]
        if ys[i][0] != ys[i-1][0]: zs[ys[i][1]] += 1

    # use list "ts" as binary search tree, every element keeps count of
    # number of children with value less than the current element's value
    ts = [0] * (zs[ys[-1][1]]+1)
    us = [0] * len(xs)

    for i in range(len(xs)-1,-1,-1):
        x = zs[i]+1
        while True:
            us[i] += ts[x-1]
            x -= (x & (-x))
            if x <= 0: break

        x = zs[i]+1
        while True:
            x += (x & (-x))
            if x > len(ts): break
            ts[x-1] += 1

    return us

print count_next_smaller_elements([40, 20, 10, 50, 20, 40, 30])
# outputs: [4, 1, 0, 2, 0, 1, 0]
Jatin Sanghvi
  • 1,928
  • 1
  • 22
  • 33
0

Instead of BST, you can use stl map.

Start inserting from right. After inserting an element, find its iterator:

auto i = m.find(element);

Then subtract it from m.end(). That gives you the number of elements in map which are greater than current element.

map<int, bool> m;
for (int i = array.size() - 1; i >= 0; --i) {
  m[array[i]] = true;
  auto iter = m.find(array[i])
  greaterThan[i] = m.end() - iter;
}

Hope it helped.

mb1994
  • 241
  • 3
  • 13
  • you will get a compilation array at this line "greaterThan[i] = m.end() - iter;"you cannot subtract map iterator. – akashchandrakar Feb 20 '16 at 09:42
  • @mb1994 you do know that the STL map uses self balanced BST (RedBlack Tree) internally, so essentially if you are not building your own BST, still you are using BST internally and algorithmic complexities remain O(logn) assuming self balanced BST being used else O(n) if skewed BST. – mahee96 Jul 27 '21 at 09:20
0

Modified Merge sort: (Already tested code)

Takes O(nlogn) time.

public class MergeSort {
    static HashMap<Integer, Integer> valueToLowerCount = new HashMap<Integer, Integer>();

    public static void main(String[] args) {
        int []                 arr = new int[] {50, 33, 37, 26, 58, 36, 59};
        int [] lowerValuesOnRight  = new int[] {4,   1,  2,  0,  1,  0,  0};

        HashMap<Integer, Integer> expectedLowerCounts = new HashMap<Integer, Integer>();
        idx = 0;
        for (int x: arr) {
            expectedLowerCounts.put(x, lowerValuesOnRight[idx++]);
        }
        
        for (int x : arr) valueToLowerCount.put(x, 0);
        
        mergeSort(arr, 0, arr.length-1);
        
        //Testing       
        Assert.assertEquals("Count lower values on right side", expectedLowerCounts, valueToLowerCount);
    }
    public static void mergeSort(int []arr, int l, int r) {
        if (r <= l) return;
        int mid = (l+r)/2;
        mergeSort(arr, l, mid);
        mergeSort(arr, mid+1, r);
        mergeDecreasingOrder(arr, l, mid, r);
    }
    public static void mergeDecreasingOrder(int []arr, int l, int lr, int r) {
        int []leftArr = Arrays.copyOfRange(arr, l, lr+1);
        int []rightArr = Arrays.copyOfRange(arr, lr+1, r+1);
        int indexArr = l;
        int i = 0, j = 0;
        while (i < leftArr.length && j < rightArr.length) {
            if (leftArr[i] > rightArr[j]) {
                valueToLowerCount.put(leftArr[i], valueToLowerCount.get(leftArr[i]) + rightArr.length - j);
                arr[indexArr++] = leftArr[i++];
            }else {
                arr[indexArr++] = rightArr[j++];
            }
        }
        while (i < leftArr.length)  { 
            arr[indexArr++] = leftArr[i++]; 
        }
        while (j < rightArr.length) {
            arr[indexArr++] = rightArr[j++];
        }
    }   
}

To find the total number of values on right-side which are greater than an array element, simply change single line of code:

if (leftArr[i] > rightArr[j])

to

if (leftArr[i] < rightArr[j])
Saurav Sahu
  • 13,038
  • 6
  • 64
  • 79