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++;
}
}
Related
I need to sort the odd numbers in the inputted array. So we do nothing with the even numbers in the array but we sort the odd numbers in ascending order
public static void main(String[] args) {
System.out.println(sortArray(new int[]{1, 5, 2, 7, 3}));
}
public static int[] sortArray(int[] array) {
List<Integer> odds = new ArrayList<>();
for (int elmnt : array) {
if (elmnt % 2 != 0) {
odds.add(elmnt);
}
}
odds.stream().sorted();
for (int i = 0; i < array.length; i++) {
if (array[i] %2 != 0){
for (int j = 0; j < odds.size(); j++) {
array[i] = odds.get(j);
}
}
}
return array;
}
in this example, the array should turn into:
[1, 3, 2, 5, 7]
how can I do this?
First, write a sort routine to sort normally in ascending order. If you don't know how to do that, you can do a web search for bubble sort, selection sort, etc. but the first two are easily implemented.
Once you have that working, simply modify the sort to work only on odd numbers.
It is not necessary to presort any or all of the array and then manipulate the values afterwards (although the cost of filtering and and sorting a smaller array in preparation may have performance benefits over sorting a larger array)
Here is a nifty solution using the Stream API.
public static int[] sortOdds(int[] a) {
var sortedOdds = Arrays.stream(a).filter(v -> v % 2 == 1).sorted().toArray();
for (int i = 0, s = 0; i < a.length; i++) {
if (a[i] % 2 == 1) {
a[i] = sortedOdds[s];
s++;
}
}
return a; // A return here is kind of redundant but whatever.
}
However, as #Yunnosch mentioned, this really isnt a good question for stack overflow. This feels like a homework question, and if it is I doubt any professor would accept the solution above since it uses sorted() instead of actually implementing a sorting algorithm.
I have an array like a[]={1,2,3,4,3,5}, then I want to compare the first element with the 2nd,3rd,4th, and so on... the 2 element with 3,4,3,5..then 3 with 4,3,5...
What I am trying to achive here is to delete duplicate elements.
public static void main(String[] args) {
int a[]= {1,2,1,4,5};
for (int i = 0; i < a.length; i++) {
for (int k = i + 1; k < a.length; k++) {
if (a[i] == a[k]) {
// shifting elements
for( k = i; k < a.length-1; k++) {
a[k] = a[k+1];
}
}
}
}
for(int l=0;l<a.length;l++)
System.out.println(a[l]);
}
Expected: should remove the duplicate elements at last and my array would have [1,2,3,4,5]
Sounds a lot like you want to "de-duplicate" this array.
There are a variety of ways to handle this, but my preferred way would be Streams (using StreamEx, a wrapper around Stream for more verbose code) in Java 8+.
See some other ways here: https://www.baeldung.com/java-remove-duplicates-from-list
So what you're trying to do would look something like this:
List<Integer> integersWithDupes = Arrays.asList(1, 2, 3, 4, 3, 5);
System.out.println(integersWithDupes.size()); // this is now 6
List<Integer> deduped = StreamEx.of(integersWithDupes).distinct().toList();
System.out.println(deduped.size()); // this is now 5
Unless it is a hard requirement for an assignment, I would avoid using arrays and use the List interface instead. It wraps a lot of the functionality that you're trying to accomplish in much more expressive and robust ways.
Happy coding!
You can use IntStream for this:
int[] a = {1,2,1,4,5};
int[] noDuplicates = IntStream.of(a).distinct().toArray();
IntStream.distinct will skip all duplicate elements and toArray() will then collect the remaining elements in a new array.
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 an array with positive integers in random order. A number x
from the list is given ,we need to find any two numbers in the list
having sum equal to x.Running time must be less than n^2.
{edit}
What I did is that , I put all the numbers less than half of x in one array and greater than half of x in another array and all greater than x are discarded and then the idea is that the required two numbers must from the two arrays (not from a single array) and by iterating I can get the two numbers.
Now for the worst case I am little confuse is that approach is good? or if anyone guide me something more better than this also can we achieve log n or n *log n ?
Your solution is both wrong, and in O(n^2).
It is wrong since consider x=5 and arr=[1,2,3,5] - the two numbers needed are from one array, not from both.
What if arr=[3,3,6], x=6, you will place both 3s in one list (not greater than x/2 for example), and will fail to find 3+3=6.
Your algorithm runs in O(n^2), because assume exactly half of the elements are greater than x1, and half are smaller than x. Then, the number of combinations you have to check are (n/2*n/2) /2 = n^2/8
To solve it in O(nlogn), think what happens if you sort the data, given a number arr[i], can you find efficiently if there is a number x-arr[i] in the now sorted array?
You can even enhance the above to O(n) average case by placing the elements in a hash-set, and now, given an number y, can you find efficiently if x-y is also in the set?
EDIT:
Stroked out parts are not relevant anymore since OP editted the question, added a new cause of concern instead.
(1) than x/2 in the editted question.
Here is O(n) solution for finding the first pair of indices of array which sums to the expected target. The solution will stop when it finds the first 2 indices that sum to target, if you need all the pairs that add up to target then instead of using int[] result, you can use ArrayList or even a Map, process the complete array and return it with all the pairs of indices. There is an obvious assumption that the Map's hashcode function is really good and there are not much collisions so that map operations perform in O(1) time.
import java.util.*;
public class Solution {
public static void main(String[] args) {
int[] array = new int[] {1,2,4,7,12,67,12,5,9,1,10};
System.out.println(Arrays.toString(sum(array, 68)));
}
public static int[] sum(int[] array, int target) {
int[] result = new int[2];
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
// n iterations
for (int index = 0; index < array.length; index++) {
// constant
if (map.containsKey(target - array[index])) {
result[1] = index;
// constant
result[0] = map.get(target - array[index]);
return result;
}
// constant
map.put(array[index], index);
}
return result;
}
}
Here you go,
Sort the array using merge sort (Time complexity: n logn). Take two pointers/counters, say i & j, one starts from index 0 and another from n-1 (assuming n size of array is n).
if array[i]+array[j]=sum
return;
else if (array[i]+array[j]<sum) i++;
else j--;
Do it until i>j.
Overall time complexity: n logn
/* Time Complexity = O(n)-since HashMap operations take O(1) time*/
public static ArrayList<Integer> twoSum(int[] arr , int target){
if (arr == null){
throw new IllegalArgumentException();
}
ArrayList<Integer> targetHolder = new ArrayList<>();
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0 ; i < arr.length ; i++){
if (map.containsKey(arr[i])){
int index = map.get(arr[i]);
targetHolder.add(index+1);
targetHolder.add(i+1);
}
else{
map.put(target-arr[i], i);
}
}
return targetHolder;
}
public static void main(String[] args) {
int[] A = {1,2,3,4,5,6};
System.out.println(twoSum(A, 6));
}
}
public void function(int[] array, int sum){
for(int i = 0; i < array.length/2; i ++){
for(int j = array.length-1;; j--){
if(array[i]+array[j] < sum) break;
if(array[i]+array[j] == sum) System.out.println(array[i]+" + "+array[j]+" = "+sum);
}
}
}