Java: Interleaving multiple arrays into a single array - java

I found similar question about interleaving two arraylists into one, but its in PHP. I was asked this question in interview as well but could'nt solve it, came back to SO to look if it was addressed already, but i could only find this paper
So any pointers to pseudo code or method definition ?
Big(O) restrictions : O(n) - time cost and O(1) - space cost
Example:
a[]= a1, a2, ..., an
b[]= b1, b2, ..., bn
Rearrange the arraylist to a1, b1, a2, b2, ..., an, bn
Editv1.0 : Arraylists a[] and b[] are of same size
Editv2.0 : What if the question is extended to rearrange in one of given two arrays, but not create a new array ?

For simplicity, assume that the arrays are the same length, and are int arrays.
int[] merge(int[] a, int[] b)
{
assert (a.length == b.length);
int[] result = new int[a.length + b.length];
for (int i=0; i<a.length; i++)
{
result[i*2] = a[i];
result[i*2+1] = b[i];
}
return result;
}

I think this is not doable with your given constraints (O(n) time and O(1) space, i.e. no additional space) for an array or array-based list. (Assuming of course, that we can't simply create a new List object delegating to the original ones.)
If you have two linked lists, this is doable - if we assume the garbage collector is fast enough, i.e. deleting an element from one list and adding it to another list does not violate the space limitation.
public <X> void interleaveLists(List<X> first, List<X> second)
{
ListIterator<X> firstIt = first.listIterator();
ListIterator<X> secondIt = second.listIterator();
while(secondIt.hasNext()) {
fistIt.next();
firstIt.add(secondIt.next());
secondIt.remove();
}
}
This method works for any pair of lists, but is only O(n) for linked lists.
For a custom linked list where we can modify the pointers, we don't have to rely on the garbage collector, we would simply change the nodes. Here for a singly-linked list:
public void interleaveLinkedLists(Node<X> firstList, Node<X> secondList) {
while(secondList != null) {
Node<X> nextFirst = firstList.next;
Node<X> nextSecond = secondList.next;
firstList.next = secondList;
secondList.next = nextFirst;
firstList = nextFirst;
secondList = nextSecond;
}
}
For a doubly-linked list, we would also have to adapt the prev-pointers.
Here the wrapping variant mentioned in the first paragraph:
public List<X> interleaveLists(final List<X> first, final List<X> second)
{
if (first.size() != second.size())
throw new IllegalArgumentException();
return new AbstractList<X>() {
public int size() {
return 2 * first.size();
}
public X get(int index) {
return index % 2 == 0 ? first.get(index / 2) : second.get(index / 2);
}
// if necessary, add a similar set() method. add/remove are not sensible here.
};
}
This is actually O(1) in time, too.

I've done up a small solution going on the assumption that you are talking about using the ArrayList (see my comment on the question). I may be oversimplifying the problem based on some of the responses here, but here goes anyway.
The below example takes a and b both of type ArrayList<Integer> and interleaves them by inserting b[0] after a[0], b[1] after a[1] etc. This snippet of course naively assumes that a and b are of the same size as per your Edit v1.0. It also does not create a new ArrayList as per your Edit v2.0.
//a and b are of type ArrayList<Integer>
for (int i = a.size(); i > 0; i--)
{
a.add(i, b.get(i - 1));
}
No matter what happens if you are combining the ArrayLists you're going to have twice the size.

I believe the mod (%) operations in Matt's answer are incorrect. Under the same assumption (that the arrays are the same length), I'd propose the following solution instead:
static int[] merge(final int[] a, final int[] b)
{
final int[] result = new int[a.length * 2];
for (int i=0; i < a.length; i++)
{
result[i << 1] = a[i];
result[(i << 1) + 1] = b[i];
}
return result;
}
I tested (very briefly), and it appears to work, but of course makes no attempt to handle error conditions such as null arguments or input arrays mismatched in size.

in the meantime lambda was introduced
O(n) time complexity
O(1) space complexity
int[] merge(int[] a, int[] b) {
return( IntStream.range( 0, a.length ).flatMap(
n -> IntStream.of( a[n], b[n] ) ).toArray() );
}

The lists don't have to be the same size:
public class InterleaveTwoLists<X> {
public List<X> interleaveLists(final List<X> first, final List<X> second) {
return new AbstractList<X>() {
private int minSize;
private int combinedMinSize;
private int size;
private List<X>largerList;
{{
minSize = Math.min(first.size(), second.size());
combinedMinSize = minSize*2;
size = first.size() + second.size();
largerList = first.size() > minSize ? first : second;
}}
public int size() {
return size;
}
public X get(int index) {
if (index < combinedMinSize) {
return index % 2 == 0
? first.get(index / 2)
: second.get(index / 2);
}
else {
return largerList.get(index-minSize);
}
}
};
}
}
To test this:
public class InterleaveTwoListsTest {
private static final Logger log =
LoggerFactory.getLogger(InterleaveTwoListsTest.class);
List<String> first = new ArrayList<String>() {
{
add("one"); add("three"); add("five");
add("seven"); add("eight"); add("nine");
}};
List<String> second = new ArrayList<String>() {
{
add("two"); add("four"); add("six");
}};
private InterleaveTwoLists<String> interleaveTwoLists;
#Before
public void setUp() throws Exception {
interleaveTwoLists = new InterleaveTwoLists<>();
}
#Test
public void test() {
List<String> combinedList = interleaveTwoLists.interleaveLists(first, second);
for( int i = 0; i < first.size() + second.size(); i++) {
log.debug("{}: {}", i, combinedList.get(i));
}
}
}

Related

Obtain the Total number of element in the Sorted Array that are within the given Range in O(1) time

I'm trying to write a class named Range, that takes an array of integers (unsorted) of length n, containing only numbers in the range are from 0 to k.
At first, I declare a constructor which will preprocess the array via Counting Sort algorithm.
Then I want to write query() method that takes two integer arguments: a and b, which form a range of numbers from a to b and returns the total frequency of all the elements in the array having the values within the given range.
My code:
import java.util.Arrays;
import java.util.HashMap;
public class Range {
private int[] a;
private int k;
public Range(int[] a, int k) {
int index = 0;
int[] counterArray = new int[k + 1];
for (int i : a)
counterArray[i]++; // initialize counterArray
for (int i = 0; i < counterArray.length; i++)
while (0 < counterArray[i]) {
a[index++] = i;
counterArray[i]--;
} // end while()
this.a = a;
this.k = k;
} // end constructor()
public int query(int a, int b) {
HashMap<Integer, Integer> map = new HashMap<>(a);
} // end query()
#Override
public String toString() {
return Arrays.toString(a);
} // end toString()
}
I chose HashMap data structure because I need query() method to be executed in constant time O(1).
So my question is: Is it possible to implement the method query() via HashMap?
If not, what are the alternatives? (Note: the time complexity should be O(1) for query(), not bothering about the space complexity).
Code in the main() :
int[] a = {13,12,13,1,2,0,0,1,3,4};
Range range = new Range(a, 13);
System.out.print(range); // prints [0,0,1,1,2,3,4,12,13,13] because array has been sorted
System.out.print(range.query(1, 4)); // calculating number of elements in the range [1, 4]
Expected Output:
5 // elements 1,1,2,3,4 are within the range [1, 4]
Explanation: provided arguments of the query() are: a=1 and b=4, hence, values to be tested are 1,2,3,4. The output should be 5 because there are 5 elements: 1,1,2,3,4.
To obtain the number of elements in the given range (from a to b inclusive) in O(1) time after the array has been sorted, you don't need to use HashMap. Instead, you can reuse the countingArray by making it an instance variable.
This approach also requires a slight modification of the sorting in order to retain the values in the countingArray intact. It's done by introducing one additional variable.
Note that it's a good practice to avoid mutating the input, that why in the code I've used Arrays.copyOf() (you can remove it, if you consider it irrelevant for this exercise).
I've extracted the logic responsible for sorting from the constructor into a separate method. And introduced a method which is meant to calculate the cumulative count for every number in the array (i.e. a number of element having values from 0 up to the current number inclusive).
So, after invoking method init() on the instance of Range we would be able to find the number of elements in the range from a to b by looking at the values stored in the countingArray at corresponding indices. And that would have a cost O(1).
public class Range {
private int[] arr;
private int[] counterArray;
private int k;
private Range(int[] arr, int k) { // constructor is not exposed
this.arr = Arrays.copyOf(arr, arr.length);
this.counterArray = new int[k + 1];
this.k = k;
}
public static Range getInstance(int[] arr, int k) {
Range range = new Range(arr, k);
range.init();
return range;
}
private void init() {
sort();
sumUpCount();
}
private void sort() {
for (int i : arr) {
counterArray[i]++;
}
int index = 0;
int copy;
for (int i = 0; i < counterArray.length; i++) {
copy = counterArray[i];
while (0 < counterArray[i]) {
arr[index++] = i;
counterArray[i]--;
}
counterArray[i] = copy;
}
}
private void sumUpCount() {
for (int i = 1; i < counterArray.length; i++) {
counterArray[i] += counterArray[i - 1];
}
}
public int query(int a, int b) {
return a == 0 ? counterArray[b] : counterArray[b] - counterArray[a - 1];
}
}
main()
public static void main(String[] args) {
int[] a = {13,12,13,1,2,0,0,1,3,4};
Range range = Range.getInstance(a, 13);
System.out.print(range.query(1,4));
}
Output:
5
Yes, in order to cache/precompute the return values of query(), you need to create a composite key, that holds both values. The easiest way to do that is to use a string that holds both numbers divided by a separator. Separator is important otherwise composite key(21, 5) = "215" and key(2, 15) = "215". With separator that would be "21;5" and "2;15" respectivly.
private String key(int a, int b) {
return String.format("%d;%d", a, b);
}
Then for each composite key possible you put the value into HashMap. In the query(a, b) method you just get the value from the Map.
public query(int a, int b) {
return map.get(key(a, b));
}
The downside of this approach is that creation of this object is pretty expensive.

Java Priority Queue of Objects :: A way to check an object's member?

Suppose I have an array of integers:
[ 1,2,3,4,5,6,1,2,3,1,2... ]
I want to know the K most frequent elements. The phrase "K most frequent" immediately makes me think of a Max Heap data structure, so I've decided to create a custom object to both count and prioritize elements:
public class countedInts implements Comparable<countedInts>{
public int theInt, count;
public countedInts(int a, int b) {
this.theInt = a;
this.count = b;
}
#Override
public int compareTo(countedInts o) {
return this.count - o.count;
}
}
This object is essentially two ints, paired together. Simple.
Okay: now for the main code:
public int[] topKFreq(int[] arr, int k) {
PriorityQueue<countedInts> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
for( int i=0; i<arr.length; i++ ) {
// If arr[i] is not tracked with a countedInts object:
maxHeap.offer( new countedInts(arr[i], 1) );
// But if arr[i] is already stored within a countedInts object...
countedInts tmp = maxHeap.get( ??? );
tmp.count++;
maxHeap.offer( tmp );
}
}
You see the problem. As I consider the elements in arr, I need a way to check maxHeap to see if I have an countedInts object already checking the element. I need to look at the member of the object within the PriorityQueue. Is there a way to do this? Or is there a better strategy to this problem.
FULL DISCLOSURE: Yes, this is a LeetCode problem. I always like to research a solution before I give up and look at the solution. That's a better way to learn.
=======================================================================
[EDIT] :: A user suggested the below strategy, which worked. Posted in case it can help others...
public class countedInts implements Comparable<countedInts>{
public int theInt, count;
public countedInts(int a, int b) {
this.theInt = a;
this.count = b;
}
#Override
public int compareTo(countedInts o) {
return this.count - o.count;
}
}
public int[] topKFrequent(int[] arr, int k) {
// Edge cases
if( arr == null ) {
return null;
}
else if( arr.length == 0 ) {
return arr;
}
int[] ret = new int[k];
HashMap<Integer,Integer> myMap = new HashMap<>();
PriorityQueue<countedInts> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
// Populate HashMap
for( int i=0; i<arr.length; i++ ) {
if( !myMap.containsKey(arr[i]) ) {
myMap.put(arr[i], 1);
}
else {
myMap.put(arr[i], myMap.get(arr[i])+1);
}
}
// Transfer data into MaxHeap
for( Map.Entry<Integer, Integer> glork : myMap.entrySet() ) {
maxHeap.offer( new countedInts(glork.getKey(), glork.getValue()) );
}
// Pick out K-most values
for( int i=0; i<k; i++ ) {
countedInts tmp = maxHeap.poll();
ret[i] = tmp.theInt;
}
return ret;
}
An approach using streams, which I think is somehow self documenting:
Stream over your array, group by identity and map to frequency using Collectors.counting, stream over the resulting map, sort entries by value in reverse order, limit the stream to k elements, and map to key
public int[] topKFrequent(int[] nums, int k) {
return Arrays.stream(nums)
.boxed()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
.entrySet().stream()
.sorted(Map.Entry.<Integer, Long>comparingByValue().reversed())
.limit(k)
.map(Map.Entry::getKey)
.mapToInt(Integer::intValue)
.toArray();
}
You can first use a HashMap to count the frequencies of all the numbers in the given array.
Then iterate through the hashmap to create the CountedInts objects and insert those objects into the priority queue.
As described in the answer by #Haoliang, you can generate a Map containing a frequency of each number in the source array. And then populate a PriorityQueue with entries of this map.
This approach would more performant than sorting all the entries, especially when k is much lower than the number element in the array.
That is how the code might look like:
public static int[] topKFrequent(int[] nums, int k) {
int[] result = new int[k];
Map<Integer, Integer> freq = getFrequencies(nums);
Queue<Map.Entry<Integer, Integer>> entries = populateQueue(freq);
for (int i = 0; i < result.length; i++) {
result[i] = entries.remove().getKey();
}
return result;
}
Java 8 method merge() is used to make the code for generating the map frequencies more concise:
public static Map<Integer, Integer> getFrequencies(int[] arr) {
Map<Integer, Integer> hist = new HashMap<>();
for (int next: arr) {
hist.merge(next, 1, Integer::sum);
}
return hist;
}
The method provided below creates a Max Heap using PriorityQueue and populates it with entries of the map:
public static Queue<Map.Entry<Integer, Integer>> populateQueue(Map<Integer, Integer> hist) {
Queue<Map.Entry<Integer, Integer>> entries =
new PriorityQueue<>(Map.Entry.<Integer, Integer>comparingByValue().reversed());
entries.addAll(hist.entrySet());
return entries;
}
main()
public static void main(String[] args) {
int[] nums = {1, 1, 1, 2, 2, 3};
System.out.println(Arrays.toString(topKFrequent(nums, 2)));
nums = new int[]{1};
System.out.println(Arrays.toString(topKFrequent(nums, 1)));
}
Output:
[1, 2]
[1]

Reversing the order printed by an array

How would I go about reversing the order from ascending to descending? I am a bit stumped because I cannot use array.reverse, etc because on our tests we won't be allowed to use java built-in methods. Which is a bummer, arrays.sort is amazing. But as of now, it does print in ASCENDING ORDER. I am relatively new to coding and this site so if my format is bad. I apologise.
public static int[] sort(int a, int b, int c) {
int[] j = {a, b ,c};
int i, k, temp = 0;
for(i = 0; i < j.length; i++) {
for(k = i + 1; k < j.length; k++) {
temp = 0;
if(j[i] > j[k]) {
temp = j[i];
j[i] = j[k];
j[k] = temp;
}
}
}
}
public static void main (String[] args) {
System.out.println(Arrays.toString((sort(6,3,11))));
Simply switch this line:
if(j[i] > j[k]) {
to this:
if(j[i] < j[k]) {
This is because in the existing code, you are swapping the elements if the first one is larger (thus moving the larger one to later in the list).
When you use the second code snippet, it swaps the elements if the first one is smaller, thus moving the smaller one to later in the list.
You can use Java 8 streams to simplify this function. Add a parameter to your sort function, and it becomes easy:
private static int[] sort(int a, int b, int c, boolean ascending) {
Comparator<? super Integer> comparator = (ascending) ? Integer::compareTo : Comparator.reverseOrder();
return Stream.of(a, b, c).sorted(comparator).mapToInt(t -> t).toArray();
}

Find number of elements across multiple lists and combine; remove if/else complex?

I have a list of lists:
List<List<String>> someList = new List<List<>>();
The maximum size of a list is five strings. It's something like below:
someList.get(0).size(); // 4 elements
someList.get(1).size(); // 1 elements
someList.get(2).size(); // 3 elements
someList.get(3).size(); // 1 elements
...
I'm trying to devise a method to create a new list of a specific size (1-5 elements) by combining some of the above nested lists. I could do something like the below (in this example, three elements):
public List<String> getThree() {
for (int j = 0; j < someList.size(); j++) {
//look for nested lists of size 3
if (someList.get(j).size() == 3) {
return someList.get(j);
}
for (int j = 0; j < someList.size(); j++) {
//if found nested list of size 2, find one of size 1 to combine
if (someList.get(j).size() == 2) {
for (int k = 0; k < someList.size(); k++) {
if (someList.get(k).size() == 1) {
return someList.get(j).add(someList.get(k).get(0));
}
}
}
for (int j = 0; j < someList.size(); j++) {
//if found nested list of size 1, find one of size 2 to combine
if (someList.get(j).size() == 1) {
for (int l = 0; l < someList.size(); l++) {
if (someList.get(l).size() == 2) {
return someList.get(j).addAll(someList.get(l));
}
}
}
}
}
I haven't included the loop for if no sublists are of size 2, to find three of size 1, but you can imagine how long and how ugly it can get. The order is important, thus the for loops incrementing sequentially (ie. I'd rather combine subList 1 + 2 more than 2 + 3, 1 + 3 more than 2 + 3, etc).
I'm hoping to find a way to dynamically implement this. I can only fathom how unreadable and long the getFive method will be provided my current methodology. I have multiple methods (getOne through getFive), it doesn't need to be dynamic in this sense, I'd just like to get rid of a lot of the if/else and for loops to reduce complexity and improve readability.
I should mention this is homework related, so I don't quite want a specific answer, but a nudge in the right direction. Something modulo perhaps? To do with remainders?
edit; to clarify and give an example:
aList = new List<String>;
aList.add("a");
aList.add("b");
someList.add(aList);
bList = new List<String>;
bList.add("c");
someList.add(bList);
newList = someList.getThree();
//newList.size() == 3
//newList contains "a","b","c"
The getThree() method is creating a new list comprised of elements from the sublists of someList. It cannot split a sublist (ie. it can't take 1 element from a sublist of 2 elements), it's combining whole sublists.
If your intention is to keep collecting from successive lists until you get 5 elements, keep adding then break out when your list is full:
public static List<String> fill(List<List<String>> sources, int size) {
List<String> list = new ArrayList<>();
for (List<String> source : sources)
if (source.size() <= size - list.size())
list.addAll(source);
return list;
}
If you want to consume the largest lists first, add this line as the first line of the method:
Collections.sort(sources, (a, b) -> b.size() - a.size());
In java 8, quite succinct:
public static List<String> fill(List<List<String>> sources, int size) {
return sources.stream().reduce(new ArrayList<>(),
(a, b) -> {if (b.size() <= a.size() - size) a.addAll(b); return a;});
}
and with the largest-first mod:
public static List<String> fill(List<List<String>> sources, int size) {
return sources.stream()
.sorted((a,b) -> b.size() - a.size())
.reduce(new ArrayList<>(), (a, b) ->
{if (b.size() <= a.size() - size) a.addAll(b); return a;});
}
Since you state that the priority of combining Lists is from left to right. An O(N^2) loop is sufficient to handle combining sublists to be less than or equal to your desired amount.
public static void main(String[] args) throws Exception {
List<List<String>> someList = new ArrayList() {{
add(new ArrayList() {{
add("a1");
add("a2");
}});
add(new ArrayList() {{
add("b1");
}});
add(new ArrayList() {{
add("c1");
add("c2");
add("c3");
}});
add(new ArrayList() {{
add("d1");
}});
}};
combine(someList, 4);
for(List<String> subList : someList) {
System.out.println(subList);
}
}
private static void combine(List<List<String>> someList, int combineAmount) {
for (int i = 0; i < someList.size(); i++) {
// Check if the current list already equals or exceeds the combineAmount
if (someList.get(i).size() >= combineAmount) {
continue;
}
// Add sublists to the current sublists until the size of the current
// sublist equals or exceeds the combineAmount
for (int j = i + 1; j < someList.size(); j++) {
if (someList.get(i).size() + someList.get(j).size() > combineAmount) {
continue;
}
someList.get(i).addAll(someList.get(j));
someList.remove(j);
j--;
// Don't bother checking other sublists if the newly
// combined sublists equals or exceeds the combineAmount
if (someList.get(i).size() >= combineAmount) {
break;
}
}
}
}
Results (combineAmount = 4):
[a1, a2, b1, d1]
[c1, c2, c3]
Results (combineAmount = 2):
[a1, a2]
[b1, d1]
[c1, c2, c3]
Results (combineAmount = 6):
[a1, a2, b1, c1, c2, c3]
[d1]
From what I understand you want to combine a list of lists into a total of 5 indexes. When doing this you want it to prioritize the left side first.
Here is a method I have created to do this. I know you did not want a specific example, but I think an example will help you understand as well as help others who also have this question:
private static List<String> getListOf(List<List<String>> someList, int size) {
List<List<String>> combine = new ArrayList<List<String>>();
List<List<String>> combinePrev = new ArrayList<List<String>>();
int value = 0;
int indexCloseValue = 0;
int indexClose;
for(int i = 0; i < someList.size(); i++){//Loops through the lists
value = someList.get(i).size();
boolean[] indexAdded = new boolean[someList.size()];//Used to make sure to not add duplicates
indexAdded[i] = true;
combine.add(someList.get(i));//add current loop to the combine list.
do{//A loop to try to add values other than the one of index i to equal size. This loops multiple times because it may take more than two to equal size.
indexCloseValue = 0;
indexClose = -1;
for(int j = 0; j < someList.size(); j++){
if(!indexAdded[j]){
int listSize = someList.get(j).size();
if(value + listSize > indexCloseValue && value + listSize <= size){
indexCloseValue = listSize;
indexClose = j;
}
}
}
if(indexClose == -1){
break;
}else{
combine.add(someList.get(indexClose));
value+=indexCloseValue;
indexAdded[indexClose] = true;
}
}while(value + indexCloseValue < size);
int added = 0;
for(List<String> str : combine){//Check size of combine list
added+=str.size();
}
int addedPrev = 0;
for(List<String> str : combinePrev){//Check size of combinePrev list
addedPrev+=str.size();
}
if(added > addedPrev && added <= size){
combinePrev = new ArrayList<List<String>>(combine);//Set combinePrev to combine if it is larger but less than size
}
combine = new ArrayList<List<String>>();//Reset combine
}
List<String> returnList = new ArrayList<String>();
for(List<String> list : combinePrev){//converts double list to a single list of strings at length "size".
for(String str : list){
returnList.add(str);
}
}
return returnList;
}
If there are any problems with this code or you have a question ask me in the comments.

How to sort an array of ints using a custom comparator?

I need to sort an array of ints using a custom comparator, but Java's library doesn't provide a sort function for ints with comparators (comparators can be used only with objects). Is there any easy way to do this?
If you can't change the type of your input array the following will work:
final int[] data = new int[] { 5, 4, 2, 1, 3 };
final Integer[] sorted = ArrayUtils.toObject(data);
Arrays.sort(sorted, new Comparator<Integer>() {
public int compare(Integer o1, Integer o2) {
// Intentional: Reverse order for this demo
return o2.compareTo(o1);
}
});
System.arraycopy(ArrayUtils.toPrimitive(sorted), 0, data, 0, sorted.length);
This uses ArrayUtils from the commons-lang project to easily convert between int[] and Integer[], creates a copy of the array, does the sort, and then copies the sorted data over the original.
How about using streams (Java 8)?
int[] ia = {99, 11, 7, 21, 4, 2};
ia = Arrays.stream(ia).
boxed().
sorted((a, b) -> b.compareTo(a)). // sort descending
mapToInt(i -> i).
toArray();
Or in-place:
int[] ia = {99, 11, 7, 21, 4, 2};
System.arraycopy(
Arrays.stream(ia).
boxed().
sorted((a, b) -> b.compareTo(a)). // sort descending
mapToInt(i -> i).
toArray(),
0,
ia,
0,
ia.length
);
You can use IntArrays.quickSort(array, comparator) from fastutil library.
If you don't want to copy the array (say it is very large), you might want to create a wrapper List<Integer> that can be used in a sort:
final int[] elements = {1, 2, 3, 4};
List<Integer> wrapper = new AbstractList<Integer>() {
#Override
public Integer get(int index) {
return elements[index];
}
#Override
public int size() {
return elements.length;
}
#Override
public Integer set(int index, Integer element) {
int v = elements[index];
elements[index] = element;
return v;
}
};
And now you can do a sort on this wrapper List using a custom comparator.
You don't need external library:
Integer[] input = Arrays.stream(arr).boxed().toArray(Integer[]::new);
Arrays.sort(input, (a, b) -> b - a); // reverse order
return Arrays.stream(input).mapToInt(Integer::intValue).toArray();
By transforming your int array into an Integer one and then using public static <T> void Arrays.sort(T[] a,
Comparator<? super T> c) (the first step is only needed as I fear autoboxing may bot work on arrays).
java 8:
Arrays.stream(new int[]{10,4,5,6,1,2,3,7,9,8}).boxed().sorted((e1,e2)-> e2-e1).collect(Collectors.toList());
If you are interested with performance and reducing number of object created on the way consider using implementation from eclipse collections.
It uses custom IntComparator, which operates on primitives thus no boxing is required.
Here is some code (it's actually not Timsort as I originally thought, but it does work well) that does the trick without any boxing/unboxing. In my tests, it works 3-4 times faster than using Collections.sort with a List wrapper around the array.
// This code has been contributed by 29AjayKumar
// from: https://www.geeksforgeeks.org/sort/
static final int sortIntArrayWithComparator_RUN = 32;
// this function sorts array from left index to
// to right index which is of size atmost RUN
static void sortIntArrayWithComparator_insertionSort(int[] arr, IntComparator comparator, int left, int right) {
for (int i = left + 1; i <= right; i++)
{
int temp = arr[i];
int j = i - 1;
while (j >= left && comparator.compare(arr[j], temp) > 0)
{
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = temp;
}
}
// merge function merges the sorted runs
static void sortIntArrayWithComparator_merge(int[] arr, IntComparator comparator, int l, int m, int r) {
// original array is broken in two parts
// left and right array
int len1 = m - l + 1, len2 = r - m;
int[] left = new int[len1];
int[] right = new int[len2];
for (int x = 0; x < len1; x++)
{
left[x] = arr[l + x];
}
for (int x = 0; x < len2; x++)
{
right[x] = arr[m + 1 + x];
}
int i = 0;
int j = 0;
int k = l;
// after comparing, we merge those two array
// in larger sub array
while (i < len1 && j < len2)
{
if (comparator.compare(left[i], right[j]) <= 0)
{
arr[k] = left[i];
i++;
}
else
{
arr[k] = right[j];
j++;
}
k++;
}
// copy remaining elements of left, if any
while (i < len1)
{
arr[k] = left[i];
k++;
i++;
}
// copy remaining element of right, if any
while (j < len2)
{
arr[k] = right[j];
k++;
j++;
}
}
// iterative sort function to sort the
// array[0...n-1] (similar to merge sort)
static void sortIntArrayWithComparator(int[] arr, IntComparator comparator) { sortIntArrayWithComparator(arr, lIntArray(arr), comparator); }
static void sortIntArrayWithComparator(int[] arr, int n, IntComparator comparator) {
// Sort individual subarrays of size RUN
for (int i = 0; i < n; i += sortIntArrayWithComparator_RUN)
{
sortIntArrayWithComparator_insertionSort(arr, comparator, i, Math.min((i + 31), (n - 1)));
}
// start merging from size RUN (or 32). It will merge
// to form size 64, then 128, 256 and so on ....
for (int size = sortIntArrayWithComparator_RUN; size < n; size = 2 * size)
{
// pick starting point of left sub array. We
// are going to merge arr[left..left+size-1]
// and arr[left+size, left+2*size-1]
// After every merge, we increase left by 2*size
for (int left = 0; left < n; left += 2 * size)
{
// find ending point of left sub array
// mid+1 is starting point of right sub array
int mid = Math.min(left + size - 1, n - 1);
int right = Math.min(left + 2 * size - 1, n - 1);
// merge sub array arr[left.....mid] &
// arr[mid+1....right]
sortIntArrayWithComparator_merge(arr, comparator, left, mid, right);
}
}
}
static int lIntArray(int[] a) {
return a == null ? 0 : a.length;
}
static interface IntComparator {
int compare(int a, int b);
}
Here is a helper method to do the job.
First of all you'll need a new Comparator interface, as Comparator doesn't support primitives:
public interface IntComparator{
public int compare(int a, int b);
}
(You could of course do it with autoboxing / unboxing but I won't go there, that's ugly)
Then, here's a helper method to sort an int array using this comparator:
public static void sort(final int[] data, final IntComparator comparator){
for(int i = 0; i < data.length + 0; i++){
for(int j = i; j > 0
&& comparator.compare(data[j - 1], data[j]) > 0; j--){
final int b = j - 1;
final int t = data[j];
data[j] = data[b];
data[b] = t;
}
}
}
And here is some client code. A stupid comparator that sorts all numbers that consist only of the digit '9' to the front (again sorted by size) and then the rest (for whatever good that is):
final int[] data =
{ 4343, 544, 433, 99, 44934343, 9999, 32, 999, 9, 292, 65 };
sort(data, new IntComparator(){
#Override
public int compare(final int a, final int b){
final boolean onlyNinesA = this.onlyNines(a);
final boolean onlyNinesB = this.onlyNines(b);
if(onlyNinesA && !onlyNinesB){
return -1;
}
if(onlyNinesB && !onlyNinesA){
return 1;
}
return Integer.valueOf(a).compareTo(Integer.valueOf(b));
}
private boolean onlyNines(final int candidate){
final String str = String.valueOf(candidate);
boolean nines = true;
for(int i = 0; i < str.length(); i++){
if(!(str.charAt(i) == '9')){
nines = false;
break;
}
}
return nines;
}
});
System.out.println(Arrays.toString(data));
Output:
[9, 99, 999, 9999, 32, 65, 292, 433, 544, 4343, 44934343]
The sort code was taken from Arrays.sort(int[]), and I only used the version that is optimized for tiny arrays. For a real implementation you'd probably want to look at the source code of the internal method sort1(int[], offset, length) in the Arrays class.
I tried maximum to use the comparator with primitive type itself. At-last i concluded that there is no way to cheat the comparator.This is my implementation.
public class ArrSortComptr {
public static void main(String[] args) {
int[] array = { 3, 2, 1, 5, 8, 6 };
int[] sortedArr=SortPrimitiveInt(new intComp(),array);
System.out.println("InPut "+ Arrays.toString(array));
System.out.println("OutPut "+ Arrays.toString(sortedArr));
}
static int[] SortPrimitiveInt(Comparator<Integer> com,int ... arr)
{
Integer[] objInt=intToObject(arr);
Arrays.sort(objInt,com);
return intObjToPrimitive(objInt);
}
static Integer[] intToObject(int ... arr)
{
Integer[] a=new Integer[arr.length];
int cnt=0;
for(int val:arr)
a[cnt++]=new Integer(val);
return a;
}
static int[] intObjToPrimitive(Integer ... arr)
{
int[] a=new int[arr.length];
int cnt=0;
for(Integer val:arr)
if(val!=null)
a[cnt++]=val.intValue();
return a;
}
}
class intComp implements Comparator<Integer>
{
#Override //your comparator implementation.
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
return o1.compareTo(o2);
}
}
#Roman:
I can't say that this is a good example but since you asked this is what came to my mind.
Suppose in an array you want to sort number's just based on their absolute value.
Integer d1=Math.abs(o1);
Integer d2=Math.abs(o2);
return d1.compareTo(d2);
Another example can be like you want to sort only numbers greater than 100.It actually depends on the situation.I can't think of any more situations.Maybe Alexandru can give more examples since he say's he want's to use a comparator for int array.

Categories