Find the number of distinct triplets sums to a given integer - java

You have been given a random integer array/list(ARR) and a number X. Find and return the number of distinct triplet(s) in the array/list which sum to X.
I have written this code:
public class Solution {
public static void merge(int arr[], int lb, int mid, int ub) {
int n1 = mid - lb + 1;
int n2 = ub - mid;
int arr1[] = new int[n1];
int arr2[] = new int[n2];
for (int i = 0; i < n1; i++)
arr1[i] = arr[lb + i];
for (int j = 0; j < n2; j++)
arr2[j] = arr[mid + 1 + j];
int i = 0, j = 0;
int k = lb;
while (i < n1 && j < n2) {
if (arr1[i] <= arr2[j])
arr[k] = arr1[i++];
else
arr[k] = arr2[j++];
k++;
}
while (i < n1)
arr[k++] = arr1[i++];
while (j < n2)
arr[k++] = arr2[j++];
}
public static void mergeSort(int arr[], int lb, int ub) {
if (lb < ub) {
int mid = lb + (ub - lb) / 2;
mergeSort(arr, lb, mid);
mergeSort(arr, mid + 1, ub);
merge(arr, lb, mid, ub);
}
}
public static int tripletSum(int[] arr, int num) {
mergeSort(arr, 0, arr.length - 1);
int n = arr.length;
int count = 0;
for (int i = 0; i < n - 2; i++) {
int sum = num - arr[i];
int j = i + 1;
int k = n - 1;
while (j < k) {
if (arr[j] + arr[k] == sum) {
count++;
k--;
} else if (arr[j] + arr[k] > sum) {
k--;
} else
j++;
}
}
return count;
}
}
Input array was - 1 3 3 3 3 3 3
Input num = 9
Output Generated - 10
Expected output - 20
Help me out with this I'm trying to find the solution for many hours. Also, the time complexity of the program should not exceed O(n²).

The given code works fine for all test cases.
import java.util.Arrays;
public class Solution {
public static int tripletSum(int[] arr, int num) {
Arrays.sort(arr);
int n = arr.length;
int Numtripletsum = 0;
for(int i=0;i<n;i++)
{
int pairSumFor = num - arr[i];
int numPairs = pairSum(arr, (i+1), (n-1), pairSumFor);
Numtripletsum+=numPairs;
}
return Numtripletsum;
}
private static int pairSum(int[] arr, int startIndex, int endIndex, int num ){
int numPair = 0;
while(startIndex < endIndex){
if(arr[startIndex] + arr[endIndex] < num){
startIndex++;
}
else if(arr[startIndex] + arr[endIndex] > num){
endIndex--;
}
else
{
int elementAtStart = arr[startIndex];
int elementAtEnd = arr[endIndex];
if(elementAtStart == elementAtEnd){
int totalElementsFromStartToEnd = (endIndex - startIndex) + 1;
numPair += (totalElementsFromStartToEnd * (totalElementsFromStartToEnd -1) /2);
return numPair;
}
int tempStartIndex = startIndex + 1;
int tempEndIndex = endIndex - 1;
while(tempStartIndex <= tempEndIndex && arr[tempStartIndex] == elementAtStart){
tempStartIndex+=1;
}
while(tempEndIndex >= tempStartIndex && arr[tempEndIndex] == elementAtEnd){
tempEndIndex-=1;
}
int totalElementsFromStart = (tempStartIndex - startIndex);
int totalElementsFromEnd = (endIndex - tempEndIndex);
numPair += (totalElementsFromStart * totalElementsFromEnd);
startIndex = tempStartIndex;
endIndex = tempEndIndex;
}
}
return numPair;
}
}

I did it in this way, hope I understood it well.
public static int tripletSum(int[] arr, int num) {
int loops = 0; // just to check the quality of the code
int counter = 0;
for (int i1 = 0; i1 < arr.length - 2; i1++) {
for (int i2 = i1 + 1; i2 < arr.length - 1; i2++) {
for (int i3 = i2 + 1; i3 < arr.length; i3++) {
loops++;
if (arr[i1] + arr[i2] + arr[i3] == num) {
counter++;
}
}
}
}
// Check for not exceeding O(n^2)
if (loops <= arr.length * arr.length) {
System.io.println("Well done");
} else {
System.io.println("Too many cycles");
}
return counter;
}
I iterate from "left" to "right" over the array and walking more and more to the "right".
1st, 2nd, 3rd
1st, 2nd, 4th
...
1st, 2nd, nth
2nd, 3rd, 4th
2nd, 3rd, 5th
...
2nd, 3rd, nth
.
.
.
nth - 2, nth - 1, nth
I iterated 35 times, but could iterate 7^2 = 49 times --> "Well done"

You missed one loop. The fixed code:
public static int tripletSum(int[] arr, int num) {
//Your code goes here
mergeSort(arr, 0, arr.length - 1);
int n = arr.length;
int count = 0;
for (int i = 0; i < n - 2; i++) {
int sum = num - arr[i];
for (int j = i+1; j < n-1; j++) {
int k = n-1;
while (j < k) {
if (arr[j] + arr[k] == sum) {
count++;
k--;
} else if (arr[j] + arr[k] > sum) {
k--;
} else {
j++;
}
}
}
}
return count;
}

OK, then optimized version:
public static int tripletSum(int[] arr, int num) {
//Your code goes here
mergeSort(arr, 0, arr.length - 1);
int n = arr.length;
int count = 0;
for (int i = 0; i < n - 2; i++) {
int sum = num - arr[i];
int maxK = n - 1;
for (int j = i + 1; j < n - 1; j++) {
int k = maxK;
while (j < k) {
if (arr[j] + arr[k] == sum) {
count++;
k--;
} else if (arr[j] + arr[k] > sum) {
k--;
maxK--;
} else {
j++;
}
}
}
}
return count;
}

Related

How to fix ArrayIndexOutOfBoundsException for a Merge sort method?

So the problem that I am running into is that I am trying to get my merge sort implementation to run, but I keep getting an Exception error that says the Array index is out of bounds. This is a runtime error because I am able to compile the program with no issues and it will run up until it hits my merge sort call. One thing that I tried was changing one of my variables to match the other within the merge method (int k = 0; //line 39). When I did this, the code ran, however, the merge sorted array was not correct. I even tried debugging the code, but couldn't see an issue with it. Below is my code:
public static void merge_sort(int A[], int l, int r){
if(l < r){
int m = (l + r)/2;
merge_sort(A, l, m);
merge_sort(A, m + 1, r);
merge(A, l, m, r);//Line17
}
}
public static void merge(int A[], int l, int m, int r){
int n1 = m - l + 1;
int n2 = r - m;
int L[] = new int [n1];
int R[] = new int [n2];
for(int i = 0; i < n1; i++){
L[i] = A[l + i];
}
for(int j = 0; j < n2; j++){
R[j] = A[m + 1 + j];
}
int i = 0;
int j = 0;
int k = 1; //line39
while(i < n1 && j < n2){
if(L[i] <= R[j]){
A[k] = L[i];
i++;
}
else{
A[k] = R[j];
j++;
}
k++;
}
while(i < n1){
A[k] = L[i];
i++;
k++;
}
while(j < n2){
A[k] = R[j]; //line60
j++;
k++;
}
}
And here is the error:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 15
at HW3.merge(HW3.java:60)
at HW3.merge_sort(HW3.java:17)
at HW3.main(HW3.java:160) //this line is where I call the method within the main
I understand that this means that the array is going out of the set size of 15 but I am unsure of how to fix this issue. I have tried looking at similar problems, but I did not see a solution to the issue I was having.
Everything else is fine in your code.
Except for this line
int k = 1; //line39
this should be k = l (The letter 'L' in small caps)
You can refer the following code
public class StackExchange {
public static
void mergeSort(int A[], int l , int r) {
if (l < r) {
int m = (l+r)/2;
mergeSort(A, l , m);
mergeSort(A, m+1, r);
merge(A, l, m, r);
}
}
private static void merge(int[] A, int l, int m, int r) {
int n1 = m - l + 1;
int n2 = r - m;
int L[] = new int[n1];
int R[] = new int[n2];
for (int i = 0 ; i < n1; i++) {
L[i] = A[l+i];
}
for (int j = 0 ; j < n2; j++) {
R[j] = A[m + 1 + j];
}
int i = 0, j = 0 , k = l;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
A[k] = L[i];
i++;
} else {
A[k] = R[j];
j++;
}
k++;
}
while (i < n1) {
A[k] = L[i];
i++;
k++;
}
while (j < n2) {
A[k] = R[j];
j++;
k++;
}
}
public static void main (String...s) {
int array[] = new int[] {12, 21, 32, 36, 14, 10, 11, 5, 55, 16, 31, 7, 57, 89, 78};
mergeSort(array, 0, array.length - 1);
printArray(array);
}
private static void printArray(int array[]) {
for (int i : array) {
System.out.println(i + " -- ");
}
}
}
How are you calling your function ? Make sure that you are using object.sort(arr, 0, A.length-1); in main when you pass the max value of the array index.
here example
// Initial index of merged subarry array
int k = l; //this is L not a 1

How to fix the "NegativeArraySizeException" on my merge sort code?

If the array to be sorted is 10 elements and above, I get the NegativeArraySizeException. It says it's on this line of code "int R[] = new int[n2];" , how can I fix this?
If you were wondering why my merge sort doesn't have any other methods, my professor specifically asked for a merge sort within just the main method. This merge sort is the iterative version because I couldn't compress the recursive version to main method only.
public static void main(String[] args)
{
int arr[] = {100,99,98,97,96,95,94,93,92,93};
int n = arr.length;
int curr_size;
int left_start;
for (curr_size = 1; curr_size <= n-1;
curr_size = 2*curr_size)
{
for (left_start = 0; left_start < n-1;
left_start += 2*curr_size)
{
int mid = left_start + curr_size - 1;
int right_end = Math.min(left_start
+ 2*curr_size - 1, n-1);
int i, j, k;
int n1 = mid - left_start + 1;
int n2 = right_end - mid;
/* create temp arrays */
int L[] = new int[n1];
int R[] = new int[n2];
/* Copy data to temp arrays L[]
and R[] */
for (i = 0; i < n1; i++){
L[i] = arr[left_start + i];
}
for (j = 0; j < n2; j++){
R[j] = arr[mid + 1+ j];
}
i = 0;
j = 0;
k = left_start;
while (i < n1 && j < n2)
{
if (L[i] <= R[j])
{
arr[k] = L[i];
i++;
}
else
{
arr[k] = R[j];
j++;
}
k++;
}
while (i < n1){
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
}
System.out.printf("\nSorted array is \n");
int i= 0;
for (i=0; i < n; i++){
System.out.print(arr[i]+ " ");
}
}
}
I think I've fixed it. Certainly works for given array.
Try
int mid = Math.min(left_start + curr_size - 1, n-1);
instead of
int mid = left_start + curr_size - 1;

Whats wrong with this QuickSort implementation?

I cannot guess what is wrong with my code.
INPUT:10, 7, 8, 9, 1, 5
OUTPUT:5 7 9 8 10 1
public class QuickSort {
public static void quickSort(int arr[], int p, int r) {
if (p < r) {
// System.out.println(p+" "+r);
int q = partition(arr, p, r);
quickSort(arr, p, q - 1);
quickSort(arr, q + 1, r);
}
}
public static int partition(int arr[], int p, int r) {
int pivot = arr[r];
int i = p - 1;
for (int j = p; j < r - 1; j++) {
// System.out.println("j");
if (arr[j] <= pivot) {
i = i + 1;
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
int temp = arr[i + 1];
arr[i + 1] = arr[r];
arr[r] = temp;
return i + 1;
}
static void printArray(int arr[]) {
int n = arr.length;
for (int i = 0; i < n; ++i)
System.out.print(arr[i] + " ");
System.out.println();
}
}
Please clarity my doubt,where to change code so that it works fine.
You are not iterating to the end of the loop (last element). Hence the partition function will not seperate the elements correctly as smaller to the left of pivot and larger to the right of pivot.
Your for loop
for (int j = p; j < r - 1; j++) {
change to
for (int j = p; j <= r - 1; j++) {
Now it is working fine.
See here Ideone

Check which method is being run

I just have a question on how to see which method is being run in a println to verify if my code is correct. I want to see if the brute force code runs or the max sub array method runs when the user inputs numbers. How would I write that in a println in the main method?
package a3;
import java.util.Scanner;
public class MaxSubarraySum2 {
public static void main(String[] args)
{
Scanner scan = new Scanner(System.in);
System.out.println("Enter number of elements in array");
int N = scan.nextInt();
int[] arr = new int[ N ];
/* Accept N elements */
System.out.println("Enter "+ N +" elements");
for (int i = 0; i < N; i++)
arr[i] = scan.nextInt();
System.out.println("Max sub array sum = "+ max_sum(arr));
}
public static int max_sum(int[] arr)
{
int Crossover = 4;
if (Crossover > arr.length) {
//Max Sub array is being used
return max_sum(arr, 0, arr.length - 1);
}
//Brute Force is being used
else {
int N = arr.length, max = Integer.MIN_VALUE;
for (int i = 0; i < N; i++)
{
int sum = 0;
for (int j = i; j < N; j++)
{
sum += arr[j];
if (sum > max)
max = sum;
}
}
return max;
}
}
// Max Sub Array
public static int max_sum(int[] arr, int low, int mid, int high)
{
int l = Integer.MIN_VALUE, r = Integer.MIN_VALUE;
for (int i = mid, sum = 0; i >= low; i--)
if ((sum += arr[i]) > l)
l = sum;
for (int i = mid +1, sum = 0; i <= high; i++)
if ((sum += arr[i]) > r)
r = sum;
return l + r;
}
public static int max_sum(int[] arr, int low, int high)
{
if (low == high)
return arr[low];
int mid = (low + high)/2;
int max1 = max_sum(arr, low, mid);
int max2 = max_sum(arr, mid + 1, high);
int max3 = max_sum(arr, low, mid, high);
return Math.max(Math.max(max1, max2), max3);
}
}
Does the println have to be in the main function? I would recommend placing such debugging output in the place where the decision is being made:
if (Crossover > arr.length) {
// Max Sub array is being used
System.out.println("Max Sub array is being used");
return max_sum(arr, 0, arr.length - 1);
} else {
// Brute Force is being used
System.out.println("Brute Force is being used");
int N = arr.length, max = Integer.MIN_VALUE;
for (int i = 0; i < N; i++)
{
int sum = 0;
for (int j = i; j < N; j++)
{
sum += arr[j];
if (sum > max)
max = sum;
}
}
return max;
}
Just print stack trace of called method by following:
Thread.dumpStack();
Like:
// Max Sub Array
public static int max_sum(int[] arr, int low, int mid, int high)
{
Thread.dumpStack();
...
}
public static int max_sum(int[] arr, int low, int high)
{
Thread.dumpStack();
...
}

Where are comparisons in java sorting methods?

Question: Where are comparisons being made in each separate sorting method?
Also if you know please tell me which method count numbers are wrong and where to place my counters instead.trying to understand where and how many times sorting methods make comparisons.
Method A B
Selection 4950 4950
Bubble 99 9900
Insertion 99 5049
Merge 712 1028
Shell 413 649
Quick 543 1041
Okay so to explain some parts, basically Array A is an array from 1-100 in ascending order. So this should be the minimum number of comparisons.
Array B is 100-1 in descending order. So I believe it should be the maximum number of comparisons. Array C is just randomly generated numbers, so it changes every time.
I feel like my selection and bubble sorts were counted correctly. Feel free to let me know where comparisons are being made that I haven't counted, or if I'm counting wrong comparisons.
Side note: Made some global variable to count the methods that were recursive in multiple sections.
class Sorting
{
static int[] X = new int[100];
static int mergecount = 0;
static int quickcount = 0;
public static void selectionSort(int list[])
{
int count = 0;
int position = 0, n = list.length;
for(int j = 0; j < n-1; j++)
{
position = j;
for(int k = j+1; k < n; k++)
{
count++;
if(list[k] < list[position])
position = k;
}
Swap(list, j, position);
}
System.out.println("counter" + count);
}
public static void Swap(int list[], int j, int k)
{
int temp = list[j];
list[j] = list[k];
list[k] = temp;
}
public static void bubbleSort(int list[])
{
int count = 0;
boolean changed = false;
do
{
changed = false;
for(int j = 0; j < list.length - 1; j++)
{
count++;
if(list[j] > list[j + 1])
{
Swap(list, j, j+1);
changed = true;
}
}
} while(changed);
System.out.println("counter" + count);
}
public static void insertionSort(int list[])
{
int count = 0;
for(int p = 1; p < list.length; p++)
{
int temp = list[p];
int j = p;
count++;
for( ; j > 0 && temp < list[j - 1]; j = j-1)
{
list[j] = list[j - 1];
count++;
}
list[j] = temp;
}
System.out.println("counter" + count);
}
public static void mergeSort(int list[])
{
mergeSort(list, 0, list.length - 1);
System.out.println("counter" + mergecount);
}
public static void mergeSort(int list[], int first, int last)
{
if(first < last)
{
int mid = (first + last) / 2;
mergeSort(list, first, mid);
mergeSort(list, mid + 1, last);
Merge(list, first, mid, last);
}
}
public static void Merge(int list[], int first, int mid, int last)
{
int count = 0;
int first1 = first, last1 = mid;
int first2 = mid + 1, last2 = last;
int temp[] = new int[list.length];
int index = first1;
while(first1 <= last1 && first2 <= last2)
{
if(list[first1] < list[first2])
{
temp[index] = list[first1++];
mergecount++;
}
else
temp[index] = list[first2++];
index++;
mergecount++;
}
while(first1 <= last1)
temp[index++] = list[first1++];
while(first2 <= last2)
temp[index++] = list[first2++];
for(index = first; index <= last; index++)
list[index] = temp[index];
}
public static void shellSort(int list[])
{
int count = 0;
int n = list.length;
for(int gap = n / 2; gap > 0; gap = gap == 2 ? 1: (int) (gap/2.2))
for(int i = gap; i < n; i++)
{
int temp = list[i];
int j = i;
count++;
for( ; j >= gap && (temp < (list[j - gap])); j -= gap)
{
list[j] = list[j - gap];
count++;
}
list[j] = temp;
}
System.out.println("counter" + count);
}
public static void quickSort(int start, int finish, int list[])
{
int count = 0;
int left = start, right = finish, pivot, temp;
pivot = list[(start + finish) / 2];
do
{
while(list[left] < pivot)
{
left++;
quickcount++;
}
while(pivot < list[right])
{
right--;
quickcount++;
}
if(left <= right)
{
temp = list[left];
list[left++] = list[right];
list[right--] = temp;
quickcount++;
}
} while(left < right);
if(start < right)
quickSort(start, right, list);
if(left < finish)
quickSort(left, finish, list);
}
public static void copy(int list[])
{
for(int i = 0; i < list.length; i++)
X[i] = list[i];
}
public static void restore(int list[])
{
for(int i = 0; i < list.length; i++)
list[i] = X[i];
}
public static void displayArray(int list[])
{
for(int k = 0; k < list.length; k++)
System.out.print(list[k] + " ");
System.out.println();
}
public static void main(String args[])
{
int[] A = new int[100];
for(int i = 0; i < A.length; i++)
A[i] = i + 1;
int[] B = new int[100];
int q = 100;
for(int i = 0; i < B.length; i++)
B[i] = q--;
int[] C = new int[100];
for(int i = 0; i < C.length; i++)
C[i] = (int)(Math.random() * 100 + 1);
displayArray(A);
copy(A);
selectionSort(A);
displayArray(A);
restore(A);
}
Note that QuickSort performance is greatly influenced by your choice of the pivot. With both of your test arrays sorted (ascending / descending) and because you are picking pivot as array[length/2] you are actually always picking the best pivot. So your test case B won't generate maximum number of comparisons for quicksort. If you were picking array[0] as pivot you'd get maximum number of comparisons for test case A and B.
The easiest way to count comparisons is to use a compare function and do it in there.
static int compareCount = 0;
int compareInt(int a, int b) {
compareCount++;
return a - b; // if 0 they are equal, if negative a is smaller, if positive b is smaller
}
Now just use compareInt in all your algorithms and you'll get an accurate count. You'll have to reset compareCount between each run though.

Categories