Duplicates search in array using HashSet and Brute Force - java

I am just trying a simple program on finding duplicates in given array.
/* Using HashSet */
int[] arrays = {1,2,4,5,4,2};
Set<Integer> hs = new HashSet<Integer>();
for(int ar:arrays)
{
if(!hs.add(ar))
{
System.out.println("Dupicate is:" +ar);
}
}
/* Nested for loop */
int arraySearch[] = {2,2,3,4,5,6,7,7,7,8,10};
int m = 0; boolean flag = true;
for(int i=0; i<arraySearch.length; i++)
{
flag=true;
for(int j=i+1; j<arraySearch.length; j++)
{
if(arraySearch[i] == arraySearch[j])
{
m=arraySearch[i];
flag = false;
break;
}
}
if(flag == false)
{
System.out.println(m);
}
}
Both the approaches are giving correct result but the problem which I am facing is if there are two numbers repeated one after another its getting printed twice.
say int[] arrays = {1,1,2,4,5,4,2};

The number gets printed more than once if the duplicates are more than one. To get it printed only once the code has to be changed.
Try doing this
Set<Integer> hs=new HashSet<Integer>();
Set<Integer> duplicate= new HashSet<Integer>();
for(int ar:arrays)
{
if(!hs.add(ar))
{
duplicate.add(ar);
}
}
This way the duplicate printing can be avoided. After this the elements in the set can be displayed using a loop.

If you are on Java 8 or further, you can do it the following way:
int[] arrays = {1,1,2,4,5,4,2};
List<Integer> hs = IntStream.of(arrays).boxed().collect(Collectors.toList());
hs.stream().filter(i -> Collections.frequency(hs, i) > 1)
.collect(Collectors.toSet()).forEach(System.out::println);

Are you seeing additional output when there are doubles, or when there are triples?
For example, for this input:
int arraySearch[] = {2,2,3,4,5,6,7,7,7,8,10};
There will be two detections of 7. That there are two detections is a result of one loop testing element 6 against elements 7 through 10, and a second loop testing element 7 against elements 8 through 10:
first detection: {2,2,3,4,5,6,(7),(7),7,8,10};
second detection: {2,2,3,4,5,6,7,(7),(7),8,10};
If the array is in ascending order, the test could be done with a single loop. With the array in random order, some tracking of which elements have been detected as having duplicates seems necessary.

Related

HashSet not returning expected output

I want to store unique lists, so I am using HashSet. But, I am not getting desired output. Here is my code, Could you tell me what is going wrong?
public List<List<Integer>> threeSum(int[] nums) {
Set<List<Integer>> res = new HashSet<>();
for(int i = 0; i< nums.length; i++){
int target = 0-nums[i];
Set<Integer> neg = new HashSet<>();
for(int j = i+1 ; j<nums.length; j++){
int rem = target - nums[j];
if(neg.contains(nums[j])){
res.add(new ArrayList<>(Arrays.asList(nums[i], rem, nums[j])));
}
else{
neg.add(rem);
}
}
}
System.out.println(res);
return new ArrayList<>(res);
}
Here my nums is [-1,0,1,2,-1,-4].
My output is [[-1,2,-1],[0,1,-1],[-1,0,1]]. Why am I getting both [0,1,-1] and [-1,0,1] into res as both contain the same elements. I what only one of these? What should I do?
From the Javadoc of List.equals:
Returns true if and only if the specified object is also a list, both lists have the same size, and all corresponding pairs of elements in the two lists are equal.
So, [0,1,-1] and [-1,0,1] aren't equal, despite containing the same elements, because they aren't in the same order.
The easiest way to solve this would be to sort the list:
res.add(Stream.of(nums[i], rem, nums[j]).sorted().collect(toList()));
You can sort the List before adding to the Set so they will be in the same order.

How to check if a number is between a range

I have a problem for an online course I am doing. The question is:
Given an Integer x, write a program which generates random numbers between x and 0 until each number in this range has been generated at least once. Once all numbers in this range have been generated, the program should display the numbers which were generated.
I have written a program which I thought would solve this but am having problems with the checking if a number is in the range. Here is my code so far:
public static void main(String[] args) {
Random generator = new Random();
ArrayList<Integer> range = new ArrayList<Integer>();
ArrayList<Integer> generated = new ArrayList<Integer>();
int x = 10;
int count = 0;
for(int i = 0; i<x+1; i++){
range.add(i);
}
while(range.isEmpty() != true){
int temp = generator.nextInt(x-1);
count++;
generated.add(temp);
if(range.contains(temp)){
range.remove(temp);
}
}
}
}
My idea was to first create two arraylists. The first would hold all numbers between 0 and the given x. The second would contain the random numbers generated. I then fill the range arraylist with the range between 0 and x. My While loop then checks this range list to see if it is empty. If not, it generates a random number, adds it to my second arraylist. I then check if this number is in the range arraylist - if it is it removes it and carries on. The problem I am having is it is running into IndexOutOfBoundsException after a few goes. I think this is because I am removing the generated numbers from the arraylist. Can anyone help me with fixing this
EDIT: I cant use any collections or other APIs. This part of the course is mainly about using Arrays and loops etc, not advanced Java stuff.
remove is an overloaded method, there is remove(int) which removes the item at the index specified and there is remove(T) which removes the first object int the list that is equal to the argument you passed in
since you passed an int to the method not an Integer, the first method is chosen
the simpliest modification to your code is replacing range.remove(temp); with range.remove(range.indexOf(temp)); or range.remove((Integer)temp)
also you have to call generator.nextInt(x+1); or else the program will be stuck in an infinite loop
You can just replace range.remove(temp); with range.removeIf(t -> t == temp);
Random generator = new Random();
ArrayList<Integer> range = new ArrayList<Integer>();
ArrayList<Integer> generated = new ArrayList<Integer>();
int x = 10;
int count = 0;
for(int i = 0; i<x+1; i++){
range.add(i);
}
while(range.isEmpty() != true){
int temp = generator.nextInt(x-1);
count++;
generated.add(temp);
if(range.contains(temp)){
range.removeIf(t -> t == temp);
}
}
OR You can use Iterator to remove from the List
for (Iterator<Integer> it = range.iterator(); it.hasNext(); ) {
Integer obj= it.next();
if (obj == temp) {
// Remove the current element from the iterator and the list.
it.remove();
break;
}
}
One more issue in your logic
int temp = generator.nextInt(x-1); The random number you are generating doesn't contain all the numbers. It should be int temp = generator.nextInt(x+2);
Hope the below will meet your requirement.
Random random = new Random();
int x = 3;
List<Integer> range = new ArrayList<>();
for(int i = 0; i <x+1; i++) {
range.add(i);
}
List<Integer> list = new ArrayList<>();
while (!list.containsAll(range)) {
list.add(random.nextInt(x + 1));
}
System.out.println(list);

Getting n random elements in array

I want to get n unique random elements from my array.
For example:
if n = 4;
I want to randomly get
array[0], array[3], array[7], array[2]
The problem is getting a random integer will lead to collisions easily (psuedocode):
for n times
{
r = generateRandomInteger within n-1
list.push(array[r]); //array[r] can be the same.
}
collisions abound, especially on small arrays.
What's a particularly elegant way to solve this?
You can use a Set instead of a List which will eliminate the duplicates. Accordingly you'll need to change your loop condition as well. Something like this
while set.size() is less than n
{
r = generateRandomInteger within n-1
set.add(array[r]); //if its the same, it won't be added to the set and the size won't increase
}
You can do this two way : i suggest you to use first one .
First by using SET :
for n times
{
r = generateRandomInteger within n-1
// you can use SET instead of LIST cause SET not allow duplication.
set.push(array[r]); //array[r] can be the same.
}
Second by using LIST :
for n times
{
r = generateRandomInteger within n-1
if(!list.contains(array[r]))
list.push(array[r]); //array[r] can be the same.
}
You can add all random ints to a list and generate a new random, till the list doesnt contains this random int. Thats not the best performance, but it works.
List<Integer> randoms = new ArrayList<Integer>()
for(int i=0; i<n;i++){
while(randoms.contains(r)) {
r = Random.nextInt(array.length-1);
}
randoms.add(r);
list.push(array[r]);
}
Using a Set is probably the best thing to do.
If you want unique elements from array (i.e. the values of array[]) then use R.J's solution. If you want unique indices:
while set.size() is less than n
{
r = generateRandomInteger within n-1
set.add(r);
}
foreach(r: set)
{
list.add(array[r]);
}
Be carefull if you want more elements then the length of the array, since you will get an infinite loop:
if(n>array.length)
{
print("Cannot get more then ... elements!");
return null;
}
int n = 4;
for (int i = 0; i < n; i++)
{
int index = RandomBetweenInclusive(i, array.length() - 1); //made up function
int temp = array[i];
array[i] = array[index];
array[index] = array[i];
}
//array values between indices 0 and n-1 will be random and unique values of array
What I usually do in this scenario is push all the items I want to select-from into a collection
var selectFrom = original.clone; // or build array
var selected = new collection;
Then I go about removing random elements in the selectFrom collection
for (i = 0; i < toSelect; i++)
{
var rndItem = selectFrom[ rand() * selectFrom.length ];
selected.add(rndItem);
selectFrom.remove(rndItem);
}
This way I select randomly from what remains and do not have to worry about clashes in random numbers / indexs.

Java - Compare 2 int Arrays and judge them based on their values [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I have a set of 2 int Arrays containing the same amount of numbers
for example
int[] array1 = {1,3,5,5,2}
int[] array2 = {5,3,4,4,4}
I need to compare them based on 2 criteria
How many elements have the same value on the same index
How many elements have the same value on a different index
And return an array of integers presenting the values
I have a few examples so you can understand better:
int[] array0 = {1,3,5,5,2};
int[] array1 = {5,3,4,4,4};
int[] array2 = {5,3,4,4,5};
int[] array3 = {2,3,2,2,4};
int[] array4 = {5,5,2,1,3};
int[] array5 = {3,1,5,5,2};
int[] array6 = {1,3,5,5,2};
compare(array0,array1); //this would return 1,1
compare(array0,array2); //this would return 1,2
compare(array0,array3); //this would return 1,1
compare(array0,array4); //this would return 0,5
compare(array0,array5); //this would return 3,2
compare(array0,array6); //this would return 5,0
For the first number it's easy, I just need to check if element on index i of array1 is the same as in array2.
I have problems producing the second number because the lowest number from one of the arrays should be taken.
If I just look if element of array1 is somewhere in array2 it produces a wrong result in some cases.
if you look at
int[] array1 = {1,3,5,5,2}
and
int[] array3 = {2,3,2,2,4};
and check if array1 has the same contents as array3 on an index, it would return 3 numbers are equal but on a different spot which is wrong because it should judge from the lowest number and the result there should be 1.
If I switch it around to compare if array3 has the same contents as array1 on some index it works for this case but not for others.
Any ideas on how to approach this, I'm pretty clueless?
Clone the two input integer arrays.
Check and see if the elements have the same value on the same index.
If so, add 1 to the same index counter and change the values in the input integer arrays to -1 (or a number less than the smallest valid value).
Check and see if the elements have the same value using a nested for loop. In other words, check the first element of the first integer array with all the elements of the second integer array. Skip the element that has the same index in both arrays, and skip elements that are less than the smallest valid value.
If so, add 1 to the different index counter and change the values in the input integer arrays to -1 (or a number less than the smallest valid value).
This should do the trick:
public void compare(int[] arrayA, int[] arrayB) {
int sameIndex = 0;
int diffIndex = 0;
//Made two new empty arrays to save the status of each element in the corresponding array, whether it has been checked our not, if not, it'd be null.
String[] arrayAstatus = new String[arrayA.length];
String[] arrayBstatus = new String[arrayB.length];
for (int i = 0; i < arrayA.length; i++) {
if (arrayA[i] == arrayB[i] || arrayAstatus[i] != null) {
sameIndex++;
continue;
}
for (int a = 0; a < arrayB.length; a++) {
if (a == i || arrayBstatus[a] != null) {
continue;
}
if (arrayA[i] == arrayB[a]) {
arrayAstatus[i] = "checked";
arrayBstatus[a] = "checked";
diffIndex++;
break;
}
}
}
System.out.println(sameIndex + ", " + diffIndex);
}
Maybe for the second condition, you could try removing the numbers from the arrays and breaking out of the loop once they are found to have a match. You could have temporary arrays that you set to the values of the original arrays. Then check the value of array1[0] against all the values in array3. When a match is found, remove the number from both arrays and check array1[1] against all the values in array3.
So when you check array1[4] against array3[0], you will get a match. Then you will remove both numbers and break out of the loop. There are no more numbers to check in array1, so the result would be 1.
I think this would clear up the issue of counting the same value twice.
First of all, this sounds like a really bad idea. Don't return an array where each index means something different and cryptic. Make two methods: compareSameIndex(array, array) and compareDifferentIndex(array, array).
To the actual implementation of how these, you could check every index in the first array to see if they appear anywhere in the second array and call that just compare(). Then compareDifferentIndex() becomes compare()-compareSameIndex(). For instance:
public int compare (int[] array0, int[] array1) {
int matches = 0;
List<Integer> list1 = Arrays.asList(array1);
for (int curInt : array0) {
if (list1.contains(curInt)) {
matches++;
}
}
return matches;
}
public int compareSameIndex(int[] array0, int[] array1) {
int matches = 0;
for (int i=0; i < array0.length; i++) {
if (array0[i] == array1[i]) {
matches++
}
}
return matches;
}
public int compareDifferentIndex(int[] array0, int[] array1) {
return compare(array0, array1) - compareSameIndex(array0, array1);
}
The requirements seem a little vague about what happens when the same number appears twice but you could build that logic into compare() to accommodate. Also you could optimize this for very large arrays where you don't check the same number twice, but this would be the general approach I would take.
You could have a Set with numbers already visited. You would something like: (not testing if this code runs, it's just to give you an idea)
public int[] compare(int[] first, int[] second) {
Set<Integer> numbersFoundInFirstArray = new LinkedHashSet<Integer>();
Set<Integer> numbersFoundInSecondArray = new LinkedHashSet<Integer>();
int inSameIndex = 0;
int inDifferentIndex = 0;
for (int i; i < first.length; i++) {
if (first[i] == second[i]) {
inSameIndex++;
}
if (numbersFoundInFirstArray.contains(second[i])) {
inDifferentIndex++;
}
if (numbersFoundInSecondArray.contains(first[i])) {
inDifferentIndex++;
}
numbersFoundInFirstArray.add(first[i]);
numbersFoundInSecondArray.add(second[i]);
}
return new int[] {inSameIndex, inDifferentIndex};
}
The contains comparison in set has O(1) if I remenber well. The property of Set is that you will only have one element of a certain type. So, if you add 1 two times, it will have only one reference of 1. This way, you will only test if the current number was already found in the other array :)
Checking arguments is just good practice.
There are probably some minor optimizations that can be done here, but a Hash will let you store positions you've already visited so as to not count them more then once.
private int[] compare(int[] arr1, int[] arr2){
int same_index = 0;
Hashtable differences = new Hashtable();
int diff_count = 0;
if (arr1.length != arr2.length){
throw new IllegalArgumentException("Array Size is not identical.");
} else {
for(int count = 0; count < arr1.length; count++){
if (arr1[count] == arr2[count]{
same_index++;
differences.put(count, null);
} else {
for (int count2 = 0; count2 < arr1.length; count2++){
if(!differences.containsKey(count2) && arr1[count] == arr2[count2]){
differences.put(count2, null);
diff_count++;
}
}
}
}
}
int[] returnArray = new int[2];
returnArray[0] = same_count;
returnArray[1] = diff_count;
return (returnArray);
}
You need to transpose all the arrays into a tree-like structure which will basically index all the unique values in all arrays and store pointers to their positions in the array and which array. The basic blueprint structure for that tree would be:
uniqElemVal->arrayIndx->arrayID = true
Where uniqElemVal would be 1,3,5,2 for 1,3,5,5,2, 2,3,4 for 2,3,2,2,4, etc, all unionized
arrayIndx would be for 1 in the 1st array 0, for 5 would be 2 and 3 etc.
array ID is something arbitrary that lets you key each array, e.g. 1 for the first, 2 for the second etc.
So in this case:
#1st array
1->0->1
3->1->1
5->2->1
5->3->1
2->4->1
#2nd array
2->0->2
3->1->2
3->2->2
2->3->2
2->4->2
4->5->2
Then when you traverse this tree, whenever you have a 2nd level node with more than 1 leafs, that means that there is a match for that particular element value in the particular position across multiple arrays.
I would personally define this tree as
HashMap<Integer, HashMap<Integer, ArrayList<Integer>>>
So whenever you have a leaf array list with >1 elements, that means you have a match

Check to see if any duplicate index values exist within an array?

So basically, I have one array, with ten values...
int[] input = new int[10];
The user controls the input to each value.
What would be a good technique to check to see if any of the values inside of the array are equal to any of the other values?
Edit:
public static void main(String[] args) {
P2 numbers = new P2();
for (int i = 0; i < numbers.input.length; i++) {
numbers.input[i] = numbers.scan.nextInt();
}
numbers.Check();
if (numbers.Check()) { System.out.println("Duplicate"); }
if (numbers.Check() == false) { System.out.println("NOT Duplicate"); }
}
public boolean Check() {
int length = input.length;
for(int i : input) {
for(int j = i + 1; j < length; j++) {
if(input[i] == input[j]) return true;
}
}
return false;
}
The codes works ask long as duplicate numbers are index-neighbors.
If you really only have ten values in the array, you're better off with a double-nested loop that breaks when it finds a duplicate.
int length = input.length;
for(int i = 0; i < length; i++) {
for(int j = i + 1; j < length; j++) {
if(intput[i] == input[j]) return true;
}
}​
If you're expecting to scale up to a large number, you're better off populating a hashset and breaking when you find a value that's already in the Hashset.
HashSet<Integer> set = new HashSet<Integer>();
for(int i : input) {
if(set.contains(i)) return true;
set.add(i);
}​
You could
compare all values in two loops (inefficient)
sort the array and then only compare adjacent values. This is more efficient, especially for longer arrays.
The first one is very easy to implement and if your array only has a length of 10, this should suffice.
If the stored int values are small non-negative values, then a BitSet might be appropriate:
BitSet set = new BitSet();
for(int i : input) {
if(set.get(i)) return true;
set.set(i);
}​
If you instead have a wide range of values, and lots of them, but a low likelihood of there being a duplicate (i.e. you expect there to be no duplicates, and simply need to confirm that), you can hash the ints, and use a BitSet(8192) of the low 13 bits of the hash (for example). That only uses roughly 1k. This can be used to easily confirm that there are no duplicates, but if it does find a hash collision, then you need to re-check with a less efficient method.
You can also sort the array and scan quickly for duplicates. Compared to building a hashset, it won't use as much memory, but will be slower.

Categories