Faster Merge Sort with Multithreading - java

My task is instead of giving a huge array to Arrays.sort() which only uses one thread, rather slice a big array into smaller arrays, and each thread sorts an array, then merge all the little arrays into a big one.
I mesured the time each section takes and the join() takes the longest time but i have to wait for every thread to finish.
I also tried merging after a join() since it finished sorting that array but it takes even longer.
Is there any way to make it more faster?
Merging:
/* Create new sorted array by merging 2 smaller sorted arrays */
private static int[] merge(int[] arr1, int[] arr2) {
// TODO: merge sorted arrays 'arr1' and 'arr2'
int[] mergedArray = new int[arr1.length + arr2.length];
int i = 0, j = 0, k = 0;
while (i < arr1.length && j < arr2.length) {
if (arr1[i] < arr2[j]) {
mergedArray[k] = arr1[i];
i++;
} else {
mergedArray[k] = arr2[j];
j++;
}
k++;
}
while (i < arr1.length) {
mergedArray[k] = arr1[i];
i++;
k++;
}
while (j < arr2.length) {
mergedArray[k] = arr2[j];
j++;
k++;
}
return mergedArray;
}
Slicing:
/* Creates an array of arrays by slicing a bigger array into smaller chunks */
private static int[][] slice(int[] arr, int k) {
//TODO: cut 'arr' into 'k' smaller arrays
int temp = 0;
final int[][] copyArray = new int[k][];
int x = (int) Math.ceil((double)arr.length / (double)k); // chunk size
int len = arr.length;
for (int i = 0; i < len - x + 1; i += x)
copyArray[temp++] = Arrays.copyOfRange(arr, i, i + x);
if (len % x != 0)
copyArray[temp++] = Arrays.copyOfRange(arr, len - len % x, len);
return copyArray;
}
Main:
public static int[] sort(int[] array) {
/* Initialize variables */
// TODO: check available processors and create necessary variables
int dbProcessor = Runtime.getRuntime().availableProcessors();
int[][] slicedArray;
Thread[] threads = new Thread[dbProcessor];
/* Turn initial array into array of smaller arrays */
// TODO: use 'slice()' method to cut 'array' into smaller bits
slicedArray = slice(array, dbProcessor);
/* parralelized sort on the smaller arrays */
// TODO: use multiple threads to sort smaller arrays (Arrays.sort())
int[][] finalSlicedArray = slicedArray;
int size = finalSlicedArray.length;
for (int i = 0; i < size ; ++i) {
int finalI = i;
if (finalSlicedArray[finalI] == null) {
break;
}
threads[i] = new Thread(() -> {
// counter++;
Arrays.sort(finalSlicedArray[finalI]);
});
threads[i].start();
}
for (Thread thread : threads) {
try {
if (thread == null) {
break;
}
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/* Merge sorted smaller arrays into a singular larger one */
// TODO: merge into one big array using 'merge()' multiple times
// create an empty array called 'sorted' and in a for cycle use
// 'merge(sorted, arr2d[i])' where arr2d is an array of sorted arrays
int[] sorted = new int[0];
for (int i = 0; i < size; ++i) {
if (finalSlicedArray[i] == null) {
break;
}
sorted = merge(sorted, finalSlicedArray[i]);
}
return sorted;
}

Related

Why do we use two different loop variables while sorting an array using merge sort?

I was learning to merge sort an integer array, when I noticed that while copying the sorted array elements to the original array, we need two separate loop variables to run simultaneously, while the values at those indices are copied to the original array. Here is the code for reference:
class MergeSort {
public static void sort(int arr[], int si, int ei, int mid) {
int merged[] = new int[ei - si + 1];
int index1 = si; // tracks the first array
int index2 = mid + 1; // tracks the second array
int i = 0;
while (index1 <= mid && index2 <= ei) {
if (arr[index1] <= arr[index2]) {
merged[i++] = arr[index1++];
} else {
merged[i++] = arr[index2++];
}
} // end of while
while (index1 <= mid) {
merged[i++] = arr[index1++];
}
while (index2 <= ei) {
merged[i++] = arr[index2++];
}
// to copy merged[] to arr[]
int j = si;
for (i = 0; i < merged.length; i++, j++) {
arr[j] = merged[i];
}
} // end sort()
public static void divide(int arr[], int si, int ei) {
// base case
if (si >= ei) {
return;
} // end of base case
int mid = si + (ei - si) / 2; // same as (ei-si)/2 but with less space complexity
divide(arr, si, mid);
divide(arr, mid + 1, ei);
sort(arr, si, ei, mid);
} // end of divide
public static void main(String args[]) {
int arr[] = { 1, 8, 0, 7, -4 };
int n = arr.length;
divide(arr, 0, n - 1);
for (int i = 0; i < n; i++) {
System.out.print(arr[i] + " ");
} // end of for
} // end of main
} // end of class
Notice that while copying the values of the array merged[] to the array arr[], we are using two separate variables i and j. I did try using only one loop variable, which went like:
for (int i = 0; i < arr.length; i++) {
arr[i] = merged[i];
}
but received an incorrect output. If anyone knows why we need two separate variables for the operation, please let me know. Thank you :)
You could use a single variable in this final loop, but you must add the offset of the start of the slice in the destination array:
for (int i = 0; i < arr.length; i++) {
arr[si + i] = merged[i];
}

Recursive in Merge Sort

public static void main(String[] args) {
int[] numbers = {20,4,7,6,1,3,9,5};
mergeSort(numbers);
}
private static void mergeSort(int[] inputArray) {
int inputLength = inputArray.length;
System.out.println(inputLength);
if (inputLength < 2)
return;
int midIndex = inputLength / 2;
int[] leftHalf = new int[midIndex];
int[] rightHalf = new int[inputLength - midIndex];
for (int i = 0; i < midIndex; i++) {
leftHalf[i] = inputArray[i];
}
for (int i = midIndex; i < inputLength; i++) {
rightHalf[i - midIndex] = inputArray[i];
}
mergeSort(leftHalf);
mergeSort(rightHalf);
merge(inputArray, leftHalf, rightHalf);
}
private static void merge(int[] inputArray, int[] leftHalf, int[] rightHalf) {
int leftSize = leftHalf.length;
int rightSize = rightHalf.length;
int i = 0, j = 0, k = 0;
while (i < leftSize && j < rightSize) {
if (leftHalf[i] <= rightHalf[i]) {
inputArray[k] = leftHalf[i];
i++;
} else {
inputArray[k] = rightHalf[j];
j++;
}
k++;
}
while (i < leftSize) { //eğer karşılaştırılmayan bir tane kalırsa diye yapılıyor yani
inputArray[k] = leftHalf[i];
i++;
k++;
}
while (j < rightSize) {
inputArray[k] = rightHalf[j];
j++;
k++;
}
}
In the mergeSort part if inputLength < 2 part of the code, we return when the length is less than 2. And last time the inputLength was 1, it becomes 2 and returns to the array [20,4].
This did not make sense to me logically. How does it get back to [20,4] when last we had [20] left?
first of all the code you have shared is flawed in the merge function part, you can find the proper code for Merge sort online, you can refer to
https://www.geeksforgeeks.org/java-program-for-merge-sort/
Now for understanding merge sort you have to understand the concept of stack (Last in First out) & recursion. In recursion the lines after the recursive call wait till the recursive call to the function has not executed completely. So in case of the
1st call Merge sort the length of the array is n and waiting for the complete execution of mergeSort(leftHalf) and mergeSort(rightHalf) both of size (n/2).
now for both the mergeSort(leftHalf) and mergeSort(rightHalf)
there will be sub left part and sub right part and this will continue till the size of the array becomes <2 and the remaining part will wait.
and after the successful execution of the smallest part it will return to the previous part from where this part was called. By this eventually this will return to the place where the function was called first.
And in case of your code both the smaller arrays are merged into the larger array so the data of the left and right sub array aren't lost.

Can we solve this Sock Merchant problem in less complexity?

I have solved the hackerrank Sock Merchant problem But I want to reduce the complexity of the code(I am not sure that it is possible or not).
John works at a clothing store. He has a large pile of socks that he must pair by color for sale. Given an array of integers representing the color of each sock, determine how many pairs of socks with matching colors there are.
For example, there are n=7 socks with colors ar= [1,2,1,2,1,3,2]. There is one pair of color 1 and one of color 2. There are three odd socks left, one of each color. The number of pairs is 2.
Function Description
Complete the sockMerchant function in the editor below. It must return an integer representing the number of matching pairs of socks that are available.
sockMerchant has the following parameter(s):
n: the number of socks in the pile
ar: the colors of each sock
Input Format
The first line contains an integer n, the number of socks represented in ar.
The second line contains n space-separated integers describing the colors ar[i] of the socks in the pile.
Constraints
1 <= n <= 100
1 <= ar[i] <= 100 where 0 <= i < n
Output Format
Return the total number of matching pairs of socks that John can sell.
Sample Input
9
10 20 20 10 10 30 50 10 20
Sample Output
3
My solutions :
package com.hackerrank.test;
public class Solution {
public static void main(String[] args) {
//Initialize array
int[] arr = new int[]{10, 20, 20, 10, 10, 30, 50, 10, 20};
//Array fr will store frequencies of element
System.out.println("---------------------------------------");
System.out.println(" sockMerchant output " + sockMerchant(9, arr));
System.out.println("---------------------------------------");
}
static int sockMerchant(int n, int[] ar) {
int pairs = 0;
int frequencyArray[] = new int[ar.length];
int frequencyTemp = -1;
for (int i = 0; i < ar.length; i++) {
int count = 1;
for (int j = i + 1; j < ar.length; j++) {
if (ar[i] == ar[j]) {
count++;
frequencyArray[j] = frequencyTemp;
}
}
if (frequencyArray[i] != frequencyTemp) {
frequencyArray[i] = count;
}
}
for (int i = 0; i < frequencyArray.length; i++) {
if (frequencyArray[i] != frequencyTemp) {
int divide = frequencyArray[i] / 2;
pairs += divide;
}
}
return pairs;
}
}
And the output is :
---------------------------------------
sockMerchant frequency 3
---------------------------------------
You can solve this in a single pass (O(n)) using a HashSet, which has O(1) put and lookup time. Each element is already in the set, in which case it gets removed and the pair counter is incremented, or it's not, in which case you add it:
int[] arr = new int[]{10, 20, 20, 10, 10, 30, 50, 10, 20};
HashSet<Integer> unmatched = new HashSet<>();
int pairs = 0;
for(int i = 0; i < arr.length; i++) {
if(!unmatched.add(arr[i])) {
unmatched.remove(arr[i]);
pairs++;
}
}
This works for java 8!!
static int sockMerchant(int n, int[] ar) {
Set<Integer> list = new HashSet<Integer>();
int count = 0;
for(int i= 0; i < n; i++){
if(list.contains(ar[i])){
count++;
list.remove(ar[i]);
}
else{
list.add(ar[i]);
}
}
return count;
}
It can also be solved using a dictionary as follows in Swift:
func sockMerchant(n: Int, ar: [Int]) -> Int {
var dictionary: [Int: Int] = [:]
var totalNumberOfPairs: Int = 0
// Store all array elements in a dictionary
// with element as key and occurrence as value
ar.forEach{
dictionary[$0] = (dictionary[$0] ?? 0) + 1
}
// Iterate over the dictionary while checking for occurrence greater or equal 2.
// If found add the integer division of the occurrence to the current total number of pairs
dictionary.forEach { (key, value) in
if value >= 2 {
totalNumberOfPairs = totalNumberOfPairs + (value / 2)
}
}
return totalNumberOfPairs
}
Here my solution with JAVA for Sock Merchant test on HackerRank
import java.io.*;
import java.util.*;
public class sockMerchant {
public static void main(String[] args) {
Scanner en = new Scanner(System.in);
int n=en.nextInt();
int[] hash = new int[300];
for(int i=0; i<n; i++){
hash[en.nextInt()]++;
}
long res=0;
for(int f: hash){
res+=f/2;
}
System.out.println(res);
}
}
Py3 solution for the problem using dictionaries
def sockMerchant(n, ar):
pair = 0
d = {}
for i in ar:
if i in d:
d[i] += 1
if i not in d:
d[i] = 1
print(d)
for x in d:
u = d[x]//2
pair += u
return pair
Code for python 3
n = 9
ar = [10, 20, 20, 10, 10, 30, 50, 10, 20]
def sockMerchant(n, ar):
totalpair = 0
test= list(set(ar))
for i in test:
pair = 0
for j in ar:
if i==j:
pair+=1
if pair>=2:
totalpair=totalpair+int(pair/2)
return totalpair
print(sockMerchant(n,ar))
We can use a Hash Table, for it. As Hash Table's Complexity is O(1)
Look at below code snippet, i created a dictionary in python i.e. Hash Table having key and value. In dictionary, there only exists a unique Key for each value. So, at start dictionary will be empty. We will loop over the provided list and check values in dictionary keys. If that value is not in dictionary key, it means it is unique, add it to the dictionary. if we find value in dictionary key, simply increment pairs counter and remove that key value pair from hash table i.e. dictionary.
def sockMerchant(n, ar):
hash_map = dict()
pairs = 0
for x in range(len(ar)):
if ar[x] in hash_map.keys():
del hash_map[ar[x]]
pairs += 1
else:
hash_map.setdefault(ar[x])
return pairs
This is my simple code for beginners to understand using c++ which prints the count of numbers of pair in the user-defined vector:
#include <bits/stdc++.h>
using namespace std;
vector<string> split_string(string);
// Complete the sockMerchant function below.
int sockMerchant(int n, vector<int> ar) {
int count=0;
vector<int> x;
for(int i=0;i<n;i++){
if(ar[i]!=0)
{
for(int j=i+1;j<n;j++)
{
if(ar[i]==ar[j]){
count++;
ar[j]=0;
break;
}
}}
}
return count;
}
int main()
{
int a,b;
vector<int> v;
cin>>a;
for(int i=0;i<a;i++){
cin>>b;
v.push_back(b);
}
cout<<sockMerchant(a,v);
}
function sockMerchant(n, ar) {
//Need to initiate a count variable to count pairs and return the value
let count = 0
//sort the given array
ar = ar.sort()
//loop through the sorted array
for (let i=0; i < n-1; i++) {
//if the current item equals to the next item
if(ar[i] === ar[i+1]){
//then that's a pair, increment our count variable
count++
//also increment i to skip the next item
i+=1
}
}
//return the count value
return count
}
sockMerchant(9, [10, 20, 20, 10, 10, 30, 50, 10, 20])
For Javascript
function sockMerchant(n, ar) {
// Create an initial variable to hold the pairs
let pairs = 0;
// create an object to temporarily assign pairs
const temp = {};
// loop through the provided array
for (let n of ar) {
// check if current value already exist in your temp object
if (temp[n] in temp) {
// if current value already exist in temp
// delete the value and increase pairs
delete temp[n];
pairs += 1;
} else {
// else add current value to the object
temp[n] = n;
}
}
// return pairs
return pairs;
}
package com.java.example.sock;
import java.io.IOException;
/**
*
* #author Vaquar Khan
*
*/
public class Solution1 {
// Complete the sockMerchant function below.
/*
* John works at a clothing store. He has a large pile of socks that he must pair by color for sale. Given an array of integers representing the color of each sock, determine how many pairs
* of socks with matching colors there are.
* For example, there are socks with colors . There is one pair of color and one of color . There are three odd socks left, one of each color. The number of pairs is .
*/
static int sockMerchant(int n, int[] ar) {
int counter = 0;
int count = 0;
//
for (int i = 0; i < ar.length; i++) {
count = 1;
//
for (int j = i + 1; j < ar.length; j++) {
if (ar[i] == ar[j]) {
count++;
}
}
if (count % 2 == 0) {
counter++;
}
}
return counter;
}
public static void main(String[] args) throws IOException {
int array[] = { 10, 20, 20 ,10 ,10, 30, 50, 10 ,20};
System.out.println(sockMerchant(9, array));
}
}
Refer below one using HashMap and having complexity O(1)
static int sockMerchant(int n, int[] ar) {
int pairs=0;
Map<Integer, Integer> map = new HashMap<>();
for(int i=0;i<n;i++){
if(map.containsKey(ar[i])){
int count=map.get(ar[i]);
map.put(ar[i],++count);
}
else{
map.put(ar[i],1);
}
}
for(int i : map.values()){
pairs=pairs+(i/2);
}
return pairs;
}
static int sockMerchant(int n, int[] ar) {
int pairs = 0;
for (int i = 0; i < ar.length; i++) {
int counter = 0;
for (int j = 0; j < ar.length; j++) {
if (j < i && ar[j] == ar[i]) break;
if(ar[j]==ar[i]) counter++;
}
pairs+=counter/2;
}
return pairs;
}
def sockMerchant(n, ar):
socks = dict()
pairs = 0
for i in ar:
if i in socks:
socks[i] = socks[i]+1
if i not in socks:
socks[i] = 1
if socks[i]%2 == 0:
pairs += 1
return pairs
This problem can be done easily with a hashset. We can take advantage of the HashSet's ability to not store duplicate elements. Here is the code below.
Set<Integer> set = new HashSet<>();
int pairCount = 0;
for(int i = 0; i < arr.length; i++) {
if(set.add(a[i])
set.add(a[i])
else {
pairCount++;
set.remove(a[i]);
}
}
return pairCount;
/*
* Complete the 'sockMerchant' function below.
*
* The function is expected to return an INTEGER.
* The function accepts following parameters:
* 1. INTEGER n
* 2. INTEGER_ARRAY ar
*/
function sockMerchant(n, ar) {
// Write your code here
let count = [];
for (var i = 0; i < ar.length; i++) {
if (count.hasOwnProperty(ar[i])) {
count[ar[i]]++;
}
else {
count[ar[i]] = 1;
}
}
let number = 0;
for (const key in count) {
number += parseInt(count[key] / 2);
}
return number;
}
My answer in C
int sockMerchant(int n, int ar_count, int* ar) {
int matchcounter =0;// each number repeating count
int paircounter=0;//total pair
int size=0;int i,j,k;
bool num_av=false;//number available or not in new array
int numberarray[n];//creating new (same length) array of length n
for(i=0;i<n;i++){
num_av=false;
for(k=0;k<=size;k++){
if(numberarray[k] == ar[i]){
num_av=true;
break;
}
}
if(!num_av){
size+=1;
numberarray[size-1]=ar[i];
for(j=0;j<n;j++){
if(ar[i]==ar[j]){
matchcounter++;
}
}
paircounter += matchcounter/2;
matchcounter=0;
}
}
return paircounter;
}
I wanted to solve this using Array. Here is my solution for Sock Merchant problem on HackerRank (Java 8):
....
import org.apache.commons.lang3.ArrayUtils;
import java.util.Arrays;
class Result {
public static int sockMerchant(int n, List<Integer> ar) {
int[] arr = ar.stream().mapToInt(i->i).toArray();
int counter = 0;
for(int i = 0; i<n; i++) {
if(arr[i]>0) {
int t = arr[i];
arr[i] = -1;
int j = ArrayUtils.indexOf(arr, t);
if(j == -1) {
continue;
} else {
arr[j] = -1;
counter += 1;
}
} else {
continue;
}
}
return counter;
}
}
This has a O(n) time complexity.
Code for Javascript
const set = new Set()
let count = 0;
for(let i = 0; i < i; i++) {
if (set.has(ar[i])) {
count++;
set.delete(ar[i])
} else {
set.add(ar[i])
}
}
You can count the number of times a number appears in the list and divide them by 2
def sockMerchant(n, ar):
unq = set(ar)
count = 0
for i in unq:
count_vals = ar.count(i)
if count_vals>1:
count = count + int(count_vals/2)
return count
The more easiest way I preferred. Answer in Kotlin
var counter = 0
for (i in 0 until n) {
if (arr[i] != 0) {
loop# for (j in i + 1 until n) {
if (arr[i] == arr[j]) {
counter++
arr[j] = 0
break#loop
}
}
}
}
Commenting for better programming
it can also be solved using the built in Set data type as below (my try) -
function sockMerchant(n, ar) {
// Write your code here
let numberSet = [...new Set(ar)];
let pairs = 0;
for(let i=0;i<numberSet.length;i++){
let count = 0;
ar.filter(x => {
if(x == numberSet[i])
count++;
});
pairs+= count / 2 >= 1 ? Math.trunc(count / 2) : 0;
}
return pairs;
}
Using Python 3:
def sockMerchant(n, ar):
flag = 0
for i in range(n):
if ar[i:].count(ar[i])%2==0:
flag+=1
return flag
Think how you would do it in real life. If someone handed you these socks one-by-one, you'd like think, "Do I have one of these already?" If not, you'd set it down and move on to check on the next sock. When you find one you've already set down, you'd move the pair to the side and count that as another found pair.
Programmatically you may take advantage of a HashSet given it's quick access (constant) and that it only allows for one entry per unique key. Therefore, you can attempt add to the set. Failure to do so means it already exists, count and remove the pair, and continue.
Time-complexity: O(n) [n = number of socks]
Space-complexity: O(m) [m = number of UNIQUE sock types]
Java 8:
public static int sockMerchant(int n, List<Integer> ar)
{
Set<Integer> uniqueSockFound = new HashSet<Integer>();
int countedPairs = 0;
//Iterate through each sock checking if a match has been found already or not
for(Integer sock: ar)
{
//If adding the sock to the set is a success, it doesn't exist yet
//Otherwise, a pair exists, so remove the item and increment the count of pairs
if(!uniqueSockFound.add(sock))
{
countedPairs++;
uniqueSockFound.remove(sock);
}
}
//Return count of pairs
return countedPairs;
}
Here is my solution and it worked for the given set of inputs.
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int size = sc.nextInt();
int[] arr = new int[size];
for (int i = 0 ; i < size ; i++) {
arr[i] = sc.nextInt();
}
int flag = 0;
for (int j = 0; j < size; j++) {
int count = 0;
for(int i = j + 1; i < size ; i++) {
if (arr[j] == arr[i]) {
count++;
}
}
flag += count / 2;
}
System.out.println(flag);
}
}
I solve it with golang
func sockMerchant(n int32, ar []int32) int32 {
// Write your code here
var indexPairs []int;
var count int32;
var operation bool;
for i := 0; i< len(ar)-1; i++{
for j := i+1; j< len(ar); j++{
//check indexPairs
operation = true;
for k := 0; k< len(indexPairs); k++{
if indexPairs[k] == i || indexPairs[k]==j{
operation = false;
}
}
if operation {
if(ar[i]==ar[j]){
count ++;
indexPairs = append(indexPairs, i, j)
}
}
}
}
return count;
}```
using PYTHON language
from itertools import groupby
def sockmerchant(n,ar):
c=0
a=[]
ar.sort()
for k,g in groupby(ar): # in this same elements group into one list
a.append(len(list(g)))
for i in a:
c=c+(i//2)
return c
n=int(input())
ar=list(map(int,input().split(' ')))
print(sockMerchant(n,ar))
function sockMerchant(n, ar){
let res = 0;
let arr= {};
for (let element of ar){
arr[element] = arr[element]+1 || 1
if(arr[element]%2 == 0){
res++;
}
}
return res;
}
sockMerchant(4,[10,10,20,20]);
public static int sockMerchant(int n, List<Integer> ar) {
int pair = 0;
List<Integer> matchedIndices = new ArrayList<>();
for (int i = 0; i < n; i++) {
if (matchedIndices.contains(i)) {
continue;
}
for (int j = 0; j < n; j++) {
if (j == i || matchedIndices.contains(j)) {
continue;
}
if (ar.get(i) == ar.get(j)) {
pair++;
matchedIndices.add(i);
matchedIndices.add(j);
break;
}
}
}
return pair;
}
I will give an example of solving this problem in C++ using unordered_map. It's overkill, to be honest, and it's done with unordered_set as well (removing the element as a replacement for a boolean in the map). But this more clearly shows the coding path to first do everything right, and only after that take an optimization step and convert to unordered_set.
using namespace std;
/*
* Complete the 'sockMerchant' function below.
*
* The function is expected to return an INTEGER.
* The function accepts following parameters:
* 1. INTEGER n
* 2. INTEGER_ARRAY ar
*/
int sockMerchant(int n, vector<int> ar) {
if (n<1 && n>100 ) return 0;
unordered_map<int, bool> scExtract;
int result=0;
// false --first sock finded
// true --second sock funded
for (auto sCol:ar) {
if (sCol<1 && sCol>100 ) return 0;
if (scExtract.find(sCol) != scExtract.end()) {
if ( scExtract[sCol]) {
scExtract[sCol]=false;
} else {
scExtract[sCol]=true;
result++;
}
} else {
scExtract.insert(pair<int, bool>(sCol, false));
}
}
return result;
}

Different values in array

I'm using Java, I'm trying to get all the different values from 2d array with recursive function only and without use HashSet ArrayList etc..,
The values will be only [0-9]
i.e:
{{4,2,2,1,4},{4,4,3,1,4},{1,1,4,2,1},{1,4,0,2,2},{4,1,4,1,1}}; -> Returns 5 (Because 4,2,3,1,0)
{{4,6,2,1,4},{4,4,3,1,4},{1,1,4,2,1},{1,4,0,2,2},{4,1,4,1,1}}; -> Returns 6 (Because 4,2,3,1,0,6)
{{4,4,4,4,4}}; -> Returns 1 (4)
What I tried:
public static int numOfColors(int[][] map) {
int colors = 0;
if (map == null || map.length == 0) {
return colors;
} else {
int[] subArr = map[map.length - 1];
for (int i = 0; i < subArr.length; i++) {
int j = i + 1;
for (; j < subArr.length; j++) {
if (subArr[i] == subArr[j]) {
break;
}
}
if (j == subArr.length) {
int k = 0;
for (; k < map.length - 1; k++) {
for (int l = 0; l < map[k].length; l++) {
if (subArr[i] == map[k][l]) {
continue;
}
}
}
if (k == map.length - 1) {
colors++;
}
}
}
int[][] dest = new int[map.length - 1][];
System.arraycopy(map, 0, dest, 0, map.length - 1);
colors += numOfColors(dest);
return colors;
}
}
But this hasn't worked for me, where is the miskate?
Recursion doesn't make much sense here. Just use a simple array as storage, and count the instances of different values, if you know the range (0-9) then a simple int[] will be sufficient.
This should do the trick:
public static int numOfColors(int[][] map){
int[] storage = new int[10];
//iterate through all the values
for(int i = 0; i<map.length; i++){
for(int j = 0; j<map[0].length; j++){
//will throw an Exception if an entry in map is not 0-9
//you might want to check for that
storage[map[i][j]]++;
}
}
int colors = 0;
//now check which values exist.
for(int i = 0; i<storage.length; i++){
if(storage[i] != 0) colors++;
}
return colors;
}
As it was already mentioned by #Cash Lo, you need some kind of storage. So you algorithm could looks something like:
#Test
public void numOfColorsTest() {
int[][] map = new int[][] {{4,2,2,1,4},{4,4,3,1,4},{1,1,4,2,1},{1,4,0,2,2},{4,1,4,1,1}};
System.out.println(String.format("numOfColors: %s", numOfColors(map, new int[0], map.length-1)));
map = new int[][] {{4,6,2,1,4},{4,4,3,1,4},{1,1,4,2,1},{1,4,0,2,2},{4,1,4,1,1}};
System.out.println(String.format("numOfColors: %s", numOfColors(map, new int[0], map.length-1)));
map = new int[][] {{4,4,4,4,4}};
System.out.println(String.format("numOfColors: %s", numOfColors(map, new int[0], map.length-1)));
}
public static int numOfColors(int[][] map, int[] collector, int currentPosition) {
int[] result = collector;
if (currentPosition < 0) {
return collector.length;
}
for (int color : map[currentPosition]) {
boolean found = false;
for (int aResult : result) {
if (aResult == color) {
found = true;
break;
}
}
if (!found) {
int[] newResult = new int[result.length + 1];
System.arraycopy(result, 0, newResult, 0, result.length);
newResult[newResult.length - 1] = color;
result = newResult;
}
}
return numOfColors(map, result, currentPosition-1);
}
I know that this is not the answer but you should always think if your solution makes sense.
In my opinion using recursion here is very bad idea because:
the code isn't readable at all
I haven't checked that but I doubt it's more efficient
recursion is hard to debug
you're making really simple problem complicated
Consider the following code. It does exactly what you need:
Integer[][] array = {{4,2,2,1,4},{4,4,3,1,4},{1,1,4,2,1},{1,4,0,2,2},{4,1,4,1,1}};
int size = Arrays.stream(array)
.flatMap(Arrays::stream)
.collect(Collectors.toSet())
.size();
System.out.println("size = " + size);
If you purposely use recursion in this case the only thing I can recommend is Test Driven Development. Write the algorithm and tests simultaneously.

having problems with inversion-counting Algorithm

I wrote an implementation of inversion-counting. This is an assignment being carried out in an online course. But the input i get isn't correct and according to what I have the program has a correct syntax. I dont just know were I went wrong
The program below is my implementation
import java.io.*;
class CountInversions {
//Create an array of length 1 and keep expanding as data comes in
private int elements[];
private int checker = 0;
public CountInversions() {
elements = new int[1];
checker = 0;
}
private boolean isFull() {
return checker == elements.length;
}
public int[] getElements() {
return elements;
}
public void push(int value) {
if (isFull()) {
int newElements[] = new int[elements.length * 2];
System.arraycopy(elements, 0, newElements, 0, elements.length);
elements = newElements;
}
elements[checker++] = value;
}
public void readInputElements() throws IOException {
//Read input from file and until the very last input
try {
File f = new File("IntegerArray.txt");
FileReader fReader = new FileReader(f);
BufferedReader br = new BufferedReader(fReader);
String stringln;
while ((stringln = br.readLine()) != null) {
push(Integer.parseInt(stringln));
}
System.out.println(elements.length);
fReader.close();
} catch (Exception e) {//Catch exception if any
System.err.println("Error: " + e.getMessage());
} finally {
// in.close
}
}
//Perform the count inversion algorithm
public int countInversion(int array[],int length){
int x,y,z;
int mid = array.length/2 ;
int k;
if (length == 1){
return 0;
}else{
//count Leftinversion and count Rightinversion respectively
int left[] = new int[mid];
int right[] = new int[array.length - mid];
for(k = 0; k < left.length;k++){
left[k] = array[k];
}
for(k = 0 ;k < right.length;k++){
right[k] = array[mid + k];
}
x = countInversion(left, left.length);
y = countInversion(right, right.length);
int result[] = new int[array.length];
z = mergeAndCount(left,right,result);
//count splitInversion
return x + y + z;
}
}
private int mergeAndCount(int[] left, int[] right, int[] result) {
int count = 0;
int i = 0, j = 0, k = 0;
int m = left.length, n = right.length;
while(i < m && j < n){
if(left[i] < right[j]){
result[k++] = left[i++];
}else{
result[k++] = right[j++];
count += left.length - i;
}
}
if(i < m){
for (int p = i ;p < m ;p++){
result[k++] = left[p];
}
}
if(j < n){
for (int p = j ;p < n ;p++){
result[k++] = right[p];
}
}
return count;
}
}
class MainApp{
public static void main(String args[]){
int count = 0;
CountInversions cIn = new CountInversions();
try {
cIn.readInputElements();
count = cIn.countInversion(cIn.getElements(),cIn.getElements().length);
System.out.println("Number of Inversios: " + count);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
Your code works if the array length is a power of 2 (actually, I'm not sure whether if does, see second point below).
When reading the input, you double the array size when it's full, but you never resize it to the number of actually read items, which is stored in checker. So your array length is a power of 2, and if the number of ints read from the file is not a power of 2, you have a too long array with some trailing 0 elements corresponding to the places allocated but not filled from the file. Since you call countInversions with the length of the array and not with the number of read items, these 0s are sorted too, yielding some spurious inversions.
After reading the input, allocate a new array
int[] copy = new int[checker];
for(int i = 0; i < checker; ++i) {
copy[i] = elements[i];
}
elements = copy;
copy the elements, and discard the old array with the wrong capacity.
Another problem in your algorithm is that you never change the original array because you allocate a new array for the merge result,
int result[] = new int[array.length];
z = mergeAndCount(left,right,result);
so you are merging unsorted arrays, which may also skew the inversion count. Since you copied the halves of the input array to new arrays for the recursive calls, you can without problem put the merge result in the passed-in array, so replace the above two lines with
z = mergeAndCount(left,right,array);
to get a method that actually sorts the array and counts the inversions.
This post is tackling the problem of count inversion with Java (except for file reading, which you have done OK) - Counting inversions in an array

Categories

Resources