Suppose given an array of size n, with sorted values.
In iteration i, a new random-generated value is given, and inserted into the end of the array.
The array is then resorted, and discard the least value item.
After iteration n, the retained array will contain the largest value items.
For example, in Java syntax, it will be something like:
List l = new ArrayList();
l.add(new Integer(2));
l.add(new Integer(3));
l.add(new Integer(6));
l.add(new Integer(9));
Random rand = new Random();
for (int i=0; i < n; i++) {
l.add(new Integer(rand.nextInt(1000)));
}
Collections.sort(l);
l.remove(0);
But it seems it's inefficient. Any better algorithm?
Use a binary insert (works like a binary search) for the new value. Discard the smallest. Should be quite fast.
By the way - this can be implemented as a handy extension method:
private static int GetSortedIndex( this IList list, IComparer comparer, object item, int startIndex, int endIndex )
{
if( startIndex > endIndex )
{
return startIndex;
}
var midIndex = startIndex + ( endIndex - startIndex ) / 2;
return comparer.Compare( list[midIndex], item ) < 0 ?
GetSortedIndex( list, comparer, item, midIndex + 1, endIndex ) :
GetSortedIndex( list, comparer, item, startIndex, midIndex - 1 );
}
public static void InsertSorted( this IList list, IComparer comparer, object item )
{
list.Insert( list.GetSortedIndex( comparer, item ), item );
}
Java Equivalent
public static void main(String[] args)
{
List l = new ArrayList();
l.add(new Integer(2));
l.add(new Integer(3));
l.add(new Integer(6));
l.add(new Integer(9));
Random rand = new Random();
for (int i=0; i < 10; i++) {
Integer rnd = new Integer(rand.nextInt(1000));
int pos = Collections.binarySearch(l,rnd);
if(pos < 0) pos = ~pos;
l.add(pos,rnd);
}
System.out.println(l);
}
Use a TreeSet instead of a List, it'll maintain the order such that such that the largest value will always be at SortedSet#last(). If using 1.6+ you can use NavigableSet methods; pollLast() will return and remove the highest value.
NavigableSet<Integer> set = new TreeSet<Integer>();
//... setup data
Integer highest = set.pollLast();
set.add(rand.nextInt(1000));
Integer newHighest = set.pollLast();
Use a min-heap to store the data, and after each insertion of a new random value, delete the min in O(1) time.
After n iterations, perform n extract-min's to get the sorted list.
I'm quite surprised no-one has mentioned this yet... The data structure you are looking for is a priority queue. It is without doubt the most efficient way of accomplishing this task. A priority queue can be implemented using a number of different methods (see the linked article), but the most common is based on a binary heap. In the self-binary variety (which is quite typical), insertion and deletion both take O(log n) time.
There seems to be a built-in generic class in the Java library, PriorityQueue<E>, so it would seem you can use this directly. This type does not surprisingly seemed to be based on a heap data structure, though more specific than that I cannot say. It should be very appropiate for your use, in any case.
A very simple optimalization would be to compare the lowest value in the sorted array (should thus be the first item) with the new value before inserting it. If the new value is greater than this value, replace the element with the new value and then resort the array.
Collections.binarySearch()
ArrayList.ensureCapcity()
Your pseudocode inserts a set of new items N into sorted list A of size S and then discards the smallest item. Use Collections.binarySearch() to find the insertion point. [Read the note the performance impact if your List is does not support RandomAccess. ArrayList does support RandomAccess.]
List<Integer> l = new ArrayList<Integer>();
l.add(new Integer(2));
l.add(new Integer(3));
l.add(new Integer(6));
l.add(new Integer(9));
l.ensureCapacity(l.size()+n);
Random rand = new Random();
for (int i=0; i < n; i++) {
final Integer newInt = Integer.rand.nextInt(1000);
int insertPoint = Collections.binarySearch(l, newInt);
if (insertPoint < 0) insertPoint = -(insertPoint + 1);
l.add(insertPoint, newInt);
}
l.remove(0);
But, are you sure you wanted to discard just 1 item? Or did you mean to insert a set of new items N into sorted list A of size S and keep just the S largest items. In that case, keep track of the min value:
int min = l.get(0);
l.ensureCapacity(l.size()+n);
Random rand = new Random();
for (int i=0; i < n; i++) {
final Integer newInt = Integer.rand.nextInt(1000);
if (newInt > min) {
int insertPoint = Collections.binarySearch(l, newInt);
if (insertPoint < 0) insertPoint = -(insertPoint + 1);
l.add(insertPoint, newInt);
}
}
However, if N is large, you may be better off sorting N into an sorted array by itself, discarding the smaller of N(0) or A(0), and then merging the two sorted arrays together [left as an exercise for the reader].
If you end up using an actual array, see Arrays.binarySearch and System.arraycopy.
The fastest algorithm I can think of would be to replace the smallest element with the new one, if needed, and push the new one to its proper place by repeatedly swapping with adjacent elements.
EDIT: The code assumes that the array is sorted in descending order, and thus the last element is the smallest.
void Insert(int[] array, int newValue)
{
// If the new value is less than the current smallest, it should be
// discarded
if (new_value <= array[array.length-1])
return;
array[array.length-1] = newValue;
for (int i = array.length-1; i > 0; --i)
{
if (newValue <= array[i-1])
break;
// Swap array[i] with array[i-1]
array[i] = array[i-1];
array[i-1] = newValue;
}
}
You can use binary search to insert a value into a sorted array.
If you're working with an ArrayList, you can replace the last number in the array with the new number if the new number is larger before you sort the array.
The Java Collections.sort uses merge sort which isn't the most efficient way of sorting in this situation. You want to use a binary search to find the insertion point and then shift all the subsequent numbers along by one.
EDIT: This can all be done with just an array like so:
public static int addDiscard(int[] list, int number)
{
if (number > list[list.length - 1])
{
int index = findInsertionIndex(list, number); // use binary search
for (int i = list.length - 1; i > index; i--)
{
list[i] = list[i - 1];
}
list[index] = number;
}
}
I don't know whether you can change the data structure, or what other operations you need to support, but a heap would be a better fit for the kind of operations you describe.
This will keep the size at 4 and do what you want as I understand it.
SortedSet<Integer> set = new TreeSet<Integer>();
set.add(2);
set.add(3);
set.add(6);
set.add(9);
Random rand = new Random();
for (int i=0; i < n; i++) {
int i = rand.nextInt(1000);
set.remove(set.first());
set.add(i);
}
ShellSort and Natural Mergesort are very performant (< O(n logn)) on largely pre-sorted data.
Inserting into a sorted list with binary search needs much more time since one update requires O(n) anyway.
Alternatively, you could use heap-datastructures.
Do you really need an online one item at a time algorithm? Or are you actually parsing a larger collection of data and just want the top n items? If it's the latter, look at partial qsort.
I'm not sure if the above example would work, what is n? and if you cycle through adding random #'s from 1 to 1,000 you'll always end up with 1000, 999, 998 and 997 - no? I don't think adding a # and then resorting each time is efficient- it would probably be quicker to check each of the four positions and replacing with a higher #.
A lot depends on how many random #'s you'll add, to few # adds and check each of the 4 positions a lot of # adds just assume you get the highest in the range.
A key question is whether you need to know the top 4 items AFTER EVERY NEW ITEM IS GENERATED, or if you only need the top 4 after all the items are generated. Also, is it literally 4 top items, or is that just an example or illustration?
Because if you really are generating thousands of values and only want the top 4, I'd think that comparing each new value to each of the existing 4 and discarding if less than all of them would be a lot faster than doing many sorts. That's just 4 compares for each new item, rather than the potentially much larger number to do repeated sorts.
Similarly if you only need the top N at the end of the process, it may well be faster to collect them all, sort, and then take the top N. But again, if most of the values are being eliminated, sorting the relative positions of the "losers" could be a big waste of time. If we only want the top 4, then whether an item is #5 or #10,382,842 is irrelevant.
Here is another solution which merges the operations into just a search, an array copy and a value set. This avoid the need for sorting or loops.
public static <T extends Comparable<T>>
void insertAndRemoveSmallest(T[] array, T t) {
int pos = Arrays.binarySearch(array, t);
if (pos < 0) pos = ~pos;
// this is the smallest entry so no need to add it and remove it.
if (pos == 0) return;
pos--;
// move all the entries down one.
if (pos > 0) System.arraycopy(array, 1, array, 0, pos);
array[pos] = t;
}
This program
public static void main(String... args) {
Integer[] ints = {2, 3, 7, 6, 9};
System.out.println("Starting with " + Arrays.toString(ints));
for (int i : new int[]{5, 1, 10, 8, 8}) {
insertAndRemoveSmallest(ints, i);
System.out.println("After adding " + i + ": " + Arrays.toString(ints));
}
}
prints
Starting with [2, 3, 7, 6, 9]
After adding 5: [3, 5, 7, 6, 9]
After adding 1: [3, 5, 7, 6, 9]
After adding 10: [5, 7, 6, 9, 10]
After adding 8: [7, 6, 8, 9, 10]
After adding 8: [6, 8, 8, 9, 10]
Related
I currently need to code a program that does a pairwise comparison to find matching pairs in an int list, but the only way I can think of doing it is through a nested for loop. Is there a way to make the time complexity O(n) instead of the O(n^2) of a nested for loop?
int[] arr = new int[n];
int total = 0;
for (int i=0; i<n; i++){
for (int j=i+1; j<n; j++){
if (arr[i] == arr[j]){
total++;
}
}
}
Looks like you are only worried about the count. So, if modifying an array is not an issue then, apply quick sort in O(nlog(n)) and then count the neighbours in O(n).
You can use a HashSet which has O(1) complexity of contains method - because hashCode of Integer value in Java is well distributed (it's just a value of the Integer) you should have constant complexity always
HashSet<Integer> set = new HashSet();
for (int i=0; i<n; i++) {
if(set.contains(arr[i])) {
total++;
} else {
set.add(arr[i]);
}
}
Read more here:
HashSet look-up complexity?
There's also one additional algorithm that could be implemented here but it require some data limitations (due to the max length of array on your machine).
You could just create an int array yourarray initialized with 0 with a length of max(arr) + 1. Then you are iterating over arr every time executing yourarray[arr[i]]++ and then you are iterating over yourarray checking where the value is greater than 1 - if it this then it means that this value has to repeat
O(n) solution.
Try this.
public static void main(String[] args) {
int[] arr = {0, 1, 2, 3, 4, 3, 2, 3};
int total = arr.length - (int)IntStream.of(arr).distinct().count();
System.out.println(total);
}
output:
3
I think you can use a HashSet to resolve this problem. The HashSet in JAVA doesn't allow insert into a duplicate element. So you can use it to making the time complexity become O(n).
int[] arr = new int[n];
Set<Integer> set = new HashSet<Integer>();
Integer total = 0;
for (int x: arr)
{
if (!set.add(x))
{
total++;
}
}
I have an array of objects which I want to sort by indices in the following order. I will always have an array size to the power of 2.
Example array size: 8. Indices: [0][1] [2][3] [4][5] [6][7]
After sort: [0][7] [1][6] [2][5] [3][4]
So basically alternating between first and last element which was not sorted yet.
I already thought of a way of doing it, and I can get the "pairs" but they are in the wrong order (and I think it would not work with any other to the power of 2 array?).
Here I'm using an int array and their indices as values to make it simpler for myself.
int[] array = new int[]{0,1,2,3,4,5,6,7};
int[] sortedArray = new int[8];
for(int i = 0; i < array.length; i+=2){
sortedArray[i] = array[i];
}
for(int i = 1; i < array.length; i+=2){
sortedArray[i] = array[array.length - i];
}
Output: [0][7] [2][5] [4][3] [6][1]
You can do this with a single loop. Consider the following algorithm:
int[] array = new int[]{0,1,2,3,4,5,6,7};
int[] sortedArray = new int[8];
for(int i = 0; i < array.length; i+=2){
sortedArray[i] = array[i/2];
sortedArray[i + 1] = array[array.length - i/2 - 1];
}
System.out.println(Arrays.toString(sortedArray)); // prints [0, 7, 1, 6, 2, 5, 3, 4]
This creates the final array by setting two values at a time:
every even index of the result array is mapped with the first values of the initial array
every odd index of the result array is mapped with the last values of the initial array
Here's a recursive approach to it, and improvements exist.
Motivation: Work your way through the array until you're left with only two values to compare. Once you're done, simply print them. Otherwise, print the value and continue slicing the array. We use two index locations to help us walk through the array, as each one governs its end of the array. We cease recursion when the difference between our start and end index is 1.
Steps to guard against silly things, like a zero-length array, I leave as an exercise for the reader.
public static void arrangeArray(int[] array) {
arrangeArray(array, 0, array.length - 1);
}
private static void arrangeArray(int[] array, int startIndex, int endIndex) {
System.out.printf("[%d][%d]\t", array[startIndex], array[endIndex]);
if(endIndex - startIndex > 1) {
arrangeArray(array, startIndex + 1, endIndex - 1);
}
}
You can extend a List and then Override the way this List works with indices so 0 remains 0 but 1 becomes physically a 2 in your internal array.
If you implement this object correctly, Collections.sort() won't see the difference. Then you can add your methods to get the internal Array for whatever you have to do.
The advantage of this approach is performance. You don't have to scramble the list yourself in a secondary step.
class swap
{
public static void main(String args[])
{
int arr[]={0,1,2,3,4,5,6,7};
int sorted[]=new int[8];
int end=7;
int i=1,j;
for(int k=0;k<8;k++)
{
sorted[k]=arr[k];
}
for(j=1;j<8;j=j+2)
{
sorted[j]=arr[end];
end--;
}
for(j=2;j<8;j=j+2)
{
sorted[j]=arr[i];
i++;
}
for(int k=0;k<8;k++)
System.out.println(sorted[k]);
}
}
I have an ArrayList of int.
The main program - calls a method to get a list of the sum of all (n member) combination of the members of the list. Where n can be anywhere between 2 - 6. E.g. Original List is {1,2,3,4,5}; Then the output should be {6, 7, 8, 8, 9, 10, 9, 10, 11, 12} where n = 3;
I am looking for the optimum way to do this. Right now, the way I have written the program (which is working) is without recursion. I have methods for all numbers i.e.
MyMethod2 -- gives me all the sum of 2 member combinations of MyArrayList
MyMethod3 -- gives me all the sum of 3 member combinations of MyArrayList
MyMethod4 -- gives me all the sum of 4 member combinations of MyArrayList
......
So, you can see that there is a lot of duplicate set of codes.
Also the way the program has currently been written (e.g. My Method3):
MyMethod3
ArrayList<Integer> sum = new ArrayList<Integer>();
for (i = 0; i < MyArrayList.size(); i++){
for (j = i + 1; j < MyArrayList.size(); j++){
for (k = j + 1; k < MyArrayList.size(); k++){
int total = MyArrayList.get(i) + MyArrayList.get(j) + MyArrayList.get(k);
sum.add(total);
}
}
}
return sum;
The MyMethod for n = 6, can become pretty long. Also "n" can change in the future.
Is there a better way to do this using recursion to minimize duplicate code, and using the number n as a variablefor the method call. Is there a standard package in Java that can help with recursion.
Adding the Code based on #Maertin suggestion - which worked for me
ArrayList<Integer> myArray = new ArrayList<Integer>();
myArray.add(5);
myArray.add(6);
myArray.add(4);
myArray.add(2);
myArray.add(1);
ArrayList<Integer> finalSumArray = combineTwoArrayList(3, myArray, myArray);
public static ArrayList<Integer> combineTwoArrayList(int n, ArrayList<Integer> origArray, ArrayList<Integer> finalSumArray) {
if (n == 1) return finalSumArray;
ArrayList<Integer> finalSumArray = new ArrayList<Integer>();
for (int i = 0; i < sumArray.size() - 1; i++){
for (int j = i + 1; j < origArray.size(); j++){
finalSumArray.add(sumArray.get(i) + origArray.get(j));
}
}
--n;
return combineTwoArrayList(n, origArray, finalSumArray);
}
You are correct in wanting to do this via recursion, because now, instead of having three separate methods, you could have one method with a parameter n for n-member combinations.
public int nCombinationSum( int n, int i, ArrayList<Integer> arr, ArrayList<Integer> sumArr) {
/* Gets n-combination sums and puts into sumArr
Input: n consecutive element combination, current index i in arr, and output ArrayList
Output: Gets n consecutive element combinations in arr from index i to index (i + n) and puts into sumArr
*/
//**BASE CASE**
//if index out of arr bounds
if( i + n > arr.size() )
return 0;
//**RECURSIVE CASE**
else {
//sum of values in arr from i to (i + n)
int currComboSum = 0;
for( int j = 0; j < n; j++ )
currComboSum += arr.get(j);
//adding sum to next element in resultant array
sumArr.add( currComboSum );
return nCombinationSum( n, i + 1, arr, sumArr );
}
}
USAGE
In your main method, you can call nCombinationSum and provide it with the kind of combination (n), starting index (in your case, 0), and arrayList (arr), and the arrayList you want to append the sums in (sumArr).
This also has the potential added benefit of allowing you to add any n-combination sum starting from a certain index. If you would like, you could add an end index as well, but this is fairly extended as it is.
EDIT: Please edit your question to reflect that you want the result to be an arrayList of sums, rather than the total sum. It is not clear right now.
Basically, what you want to do with recursion, in general, is to set a base case and a recursive case.
Your base case would be if your index is out of bounds, because you're going to call all elements from index i to i + n.
For the recursive case, use the algorithm below to account for each element in arr, then just keep returning the function with the next index value, and the function will continue running until it is out of bounds.
Algorithm
Getting sum of n-combination elements
Appending that sum into resultant array sumArr
Feel free to refer to the code above for reference.
You can use recursion. Basically, you should have only two for loops. (which is the code for two member combinations). When you compute 'total', pass each 'total' value to an ArrayList MyArrayList2. Now for MyMethod3, you use the elements of MyArrayList2 and the original ArrayList and find new 'total' values again and pass that to MyArrayList3. For MyMethod4, you use the elements of MyArrayList3 and the original ArrayList and find new 'total' values again and pass that to MyArrayList4.... ....
I have the following problem I need to optimize. For a given array(with duplicated keys allowed), for each position i in the array, I need to compute all bigger values right of i, and all smaller values left of i. If we have:
1 1 4 3 5 6 7 and i = 3(value 3), the count of smaller values to left of i is 1(no repeated keys), and to the right, the number of bigger values is 3.
The brute force solution of this problem is ~N^2, and with some extra space I can manage to compute the smaller values from the bigger ones, so reducing complexity to ~(N^2)/2.
My question is: is there a faster way to get it done? Maybe NlgN? I imagine there is a data structure out there I don't know which will allow me to do the computation faster.
EDIT: Thank you all for your replies and discussions. You can find two good solutions two the problem below. Always a pleasure learning from developers in stackoverflow.
Here's an O(n log n) solution.
As hinted by #SayonjiNakate, the solution using segment tree (I used Fenwick tree in my implementation) runs in O(n log M) time, where M is the maximum possible value in the array.
Firstly, note that the problem "number of smaller elements on the left" is equivalent to the problem "number of greater elements on the right" by reversing and negating the array. So, in my explanation below I only describe the "number of smaller elements on the left", which I call "lesser_left_count".
Algorithm for lesser_left_count:
The idea is to be able to find the total of numbers smaller than a specific number.
Define an array tree with size upto MAX_VALUE, which will store the value 1 for seen numbers and 0 otherwise.
Then as we traverse the array, when we see a number num, just assign the value 1 to tree[num] (update operation). Then lesser_left_count for a number num is the sum from 1 to num-1 (sum operation) so far, since all smaller numbers to the left of current position would have been set to 1.
Simple right? If we use Fenwick tree, the update and sum operation can be done each in O(log M) time, where M is the maximum possible value in the array. Since we are iterating over the array, total time is O(n log M).
The only disadvantage of the naive solution is that it uses a lot of memory as M gets bigger (I set M=2^20-1 in my code, which take around 4MB of memory). This can be improved by mapping distinct integers in the array into smaller integers (in a way that preserve the order). The mapping can be done in simply O(n log n) by sorting the array. So the number M can be reinterpreted as "number of distinct elements in the array".
So the memory wouldn't be any problem anymore, because if after this improvement you indeed need huge memory, that means there are that many distinct numbers in your array, and the time complexity of O(n) will already be too high to be calculated in normal machine anyway.
For the sake of simplicity, I didn't include that improvement in my code.
Oh, and since Fenwick tree only works for positive numbers, I converted the numbers in the array to be minimum 1. Note that this doesn't change the result.
Python code:
MAX_VALUE = 2**20-1
f_arr = [0]*MAX_VALUE
def reset():
global f_arr, MAX_VALUE
f_arr[:] = [0]*MAX_VALUE
def update(idx,val):
global f_arr
while idx<MAX_VALUE:
f_arr[idx]+=val
idx += (idx & -idx)
def cnt_sum(idx):
global f_arr
result = 0
while idx > 0:
result += f_arr[idx]
idx -= (idx & -idx)
return result
def count_left_less(arr):
reset()
result = [0]*len(arr)
for idx,num in enumerate(arr):
cnt_prev = cnt_sum(num-1)
if cnt_sum(num) == cnt_prev: # If we haven't seen num before
update(num,1)
result[idx] = cnt_prev
return result
def count_left_right(arr):
arr = [x for x in arr]
min_num = min(arr)
if min_num<=0: # Got nonpositive numbers!
arr = [min_num+1+x for x in arr] # Convert to minimum 1
left = count_left_less(arr)
arr.reverse() # Reverse for greater_right_count
max_num = max(arr)
arr = [max_num+1-x for x in arr] # Negate the entries, keep minimum 1
right = count_left_less(arr)
right.reverse() # Reverse the result, to align with original array
return (left, right)
def main():
arr = [1,1,3,2,4,5,6]
(left, right) = count_left_right(arr)
print 'Array: ' + str(arr)
print 'Lesser left count: ' + str(left)
print 'Greater right cnt: ' + str(right)
if __name__=='__main__':
main()
will produce:
Original array: [1, 1, 3, 2, 4, 5, 6]
Lesser left count: [0, 0, 1, 1, 3, 4, 5]
Greater right cnt: [5, 5, 3, 3, 2, 1, 0]
or if you want Java code:
import java.util.Arrays;
class Main{
static int MAX_VALUE = 1048575;
static int[] fArr = new int[MAX_VALUE];
public static void main(String[] args){
int[] arr = new int[]{1,1,3,2,4,5,6};
System.out.println("Original array: "+toString(arr));
int[][] leftRight = lesserLeftRight(arr);
System.out.println("Lesser left count: "+toString(leftRight[0]));
System.out.println("Greater right cnt: "+toString(leftRight[1]));
}
public static String toString(int[] arr){
String result = "[";
for(int num: arr){
if(result.length()!=1){
result+=", ";
}
result+=num;
}
result+="]";
return result;
}
public static void reset(){
Arrays.fill(fArr,0);
}
public static void update(int idx, int val){
while(idx < MAX_VALUE){
fArr[idx]+=val;
idx += (idx & -idx);
}
}
public static int cntSum(int idx){
int result = 0;
while(idx > 0){
result += fArr[idx];
idx -= (idx & -idx);
}
return result;
}
public static int[] lesserLeftCount(int[] arr){
reset();
int[] result = new int[arr.length];
for(int i=0; i<arr.length; i++){
result[i] = cntSum(arr[i]-1);
if(cntSum(arr[i])==result[i]) update(arr[i],1);
}
return result;
}
public static int[][] lesserLeftRight(int[] arr){
int[] left = new int[arr.length];
int min = Integer.MAX_VALUE;
for(int i=0; i<arr.length; i++){
left[i] = arr[i];
if(min>arr[i]) min=arr[i];
}
for(int i=0; i<arr.length; i++) left[i]+=min+1;
left = lesserLeftCount(left);
int[] right = new int[arr.length];
int max = Integer.MIN_VALUE;
for(int i=0; i<arr.length; i++){
right[i] = arr[arr.length-1-i];
if(max<right[i]) max=right[i];
}
for(int i=0; i<arr.length; i++) right[i] = max+1-right[i];
right = lesserLeftCount(right);
int[] rightFinal = new int[right.length];
for(int i=0; i<right.length; i++) rightFinal[i] = right[right.length-1-i];
return new int[][]{left, rightFinal};
}
}
which will produce same result.
Try segment tree data structure used for solving RMQ.
It would give you exactly n log n.
And look through RMQ problem generally, your problem may be reduced to it.
Here's a relatively simple solution that's O(N lg(N)) that doesn't rely on the entries being among finitely many integers (in particular, it should work for any ordered data type).
We assume the output is to be stored in two arrays; lowleft[i] will at the end contain the number of distinct values x[j] with j < i and x[j] < x[i], and highright[i] will at the end contain the number of distinct values x[j] with j > i and x[j] > x[i].
Create a balanced tree data structure that maintains in each node, the number of nodes in the subtree rooted at that node. This is fairly standard, but not a part of the Java standard library I think; it's probably easiest to do an AVL tree or so. The type of the values in the nodes should be the type of the values in your array.
Now first iterate forward through the array. We start with an empty balanced tree. For every value x[i] we encounter, we enter it into the balanced tree (near the end there are O(N) entries in this tree, so this step takes O(lg(N)) time). When searching for the position to enter x[i], we keep track of the number of values less than x[i] by adding up the sizes of all left subtrees whenever we take the right subtree, and adding what will be the size of the left subtree of x[i]. We enter this number into lowleft[i].
If the value x[i] is already in the tree, we just carry on with the next iteration of this loop. If the value x[i] is not in there, we enter it and rebalance the tree, taking care to update the subtree sizes correctly.
Each iteration of this loop takes O(lg(N)) steps, for a total of O(N lg(N)). We now start with an empty tree and do the same thing iterating backward through the array, finding the position for every x[i] in the tree, and every time recording the size of all subtrees to the right of the new node as highright[i]. Total complexity therefore O(N lg(N)).
Here is an algorithm which should give you O(NlgN):
Iterate over the list once and build a map of key => indexList. So for ever key (element in the array) you store a list of all the indices where that key is in the array. This will take O(N) (iterate over the list) + N*O(1) (appending N items to lists) steps. So this step is O(N). The second step requires that these lists are sorted which they will be as we are iterating over the list from left to right so a newly inserted index in a list will always be larger than all the other ones which are already in there.
Iterate over the list again and for each element search the index lists for all keys which are larger than the current element for the first index which is after the current index. This gives you the number of elements to the right of the current one which are larger than the current element. As the index lists are sorted you can do a binary search which will take O(k * lgN) steps with k being the number of keys larger then the current one. If the number of keys has an upper limit then this is a constant as far as big-O is concerned. The second step here is to search all smaller keys and find the first index in the list which is prior to the current one. This will give you the number of element to the left of the current one which are smaller. Same reasoning as above this is O(k * lgN)
So assuming the number of keys is limited this should give you O(N) + N * 2 * O(lgN) so overall O(NlgN) if I'm not mistaken.
Edit: Pseudo code:
int[] list;
map<int => int[]> valueIndexMap;
foreach (int i = 0; i < list.length; ++i) { // N iterations
int currentElement = list[i]; // O(1)
int[] indexList = valueIndexMap[currentElement]; // O(1)
indexList.Append(i); // O(1)
}
foreach (int i = 0; i < list.length; ++i) { // N iterations
int currentElement = list[i]; // O(1)
int numElementsLargerToTheRight;
int numElementsSmallerToTheLeft;
foreach (int k = currentElement + 1; k < maxKeys; ++k) { // k iterations with k being const
int[] indexList = valueIndexMap[k]; // O(1)
int firstIndexBiggerThanCurrent = indexList.BinaryFindFirstEntryLargerThan(i); // O(lgN)
numElementsLargerToTheRight += indexList.Length - firstIndexBiggerThanCurrent; // O(1)
}
foreach (int k = currentElement - 1; k >= 0; --k) { // k iterations with k being const
int[] indexList = valueIndexMap[k]; // O(1)
int lastIndexSmallerThanCurrent = indexList.BinaryFindLastEntrySmallerThan(i); // O(lgN)
numElementsSmallerToTheLeft += lastIndexSmallerThanCurrent; // O(1)
}
}
Update: I tinkered around with a C# implementation in case anyone is interested;
My code needs to randomly select 6 numbers from a list ranging from 1 to 45.
Once when I ran my code (below) the output was [4, 4, 17, 18, 27, 37]. I was not expecting any duplicates in the output. How is it possible that there are duplicates? My code should be removing numbers from list as they are selected.
Random rng = new Random();
int size = 45;
int sixList[] = new int[6];
ArrayList<Integer> list = new ArrayList<Integer>(size);
ArrayList<Integer> list2 = new ArrayList<Integer>(6);
for(int i = 1; i <= size; i++) {
list.add(i);
}
Random rand = new Random();
for(int i = 0; list.size() > 39; i++){
int index = rand.nextInt(list.size());
if (index == 0){
index = rand.nextInt(list.size());
list2.add(index);
list.remove(index);
}else{
list2.add(index);
list.remove(index);
}
}
Collections.sort(list2);
System.out.print(list2);
The problem is that you are adding the index value to your list of random numbers.
Change your code
list2.add(index);
list.remove(index);
To
list2.add(list.remove(index));
List maintain index and does not care about duplicate elements at all. To avoid duplicates you must use Set rather than List. If you have any user-defined class going in the Set, then dont forget to implement equals() and hashcode() which are used to determine if elements are duplicate or not by the Set classes like HashSet.
If you have primitives going in you Set, then forget about duplicates as duplicates will be automatically handled for primitive data-types like int,long etc. So I suggest you to use Set rather than List. to avoid duplicate elements in the collection