HashSet not returning expected output - java

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.

Related

Duplicates search in array using HashSet and Brute Force

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.

How to merge two arraylists into one in ascending order

I need to merge two lists into one, in ascending order, not duplicates, and I think my code is really close, I'm just missing something and I can't figure it out. As of now, my code is not working properly in my merge method. I think it has something to do with my loops, but I just can't work around it. My current method prints the new list, but it is not in perfect increasing order. I would appreciate any assistance in figuring out how to make this method print my merged list with ascending order using the contents of l1 and l2.
**Note: I cannot use any built-in array sorting methods.
Thanks!
import java.util.ArrayList;
import java.util.Random;
public class MergeLists {
public static ArrayList<Integer> merge(ArrayList<Integer> l1, ArrayList<Integer> l2){
ArrayList<Integer> mergedList = new ArrayList();
for (int j = 0; j < l1.size(); j++) {
if (l1.get(j) < l2.get(j)) {
mergedList.add(l1.get(j));
mergedList.add(l2.get(j));
} else {
mergedList.add(l2.get(j));
mergedList.add(l1.get(j));
}
}
for (int i = l2.size() - l1.size(); i < l2.size(); i++) {
mergedList.add(l2.get(i));
}
return mergedList;
}
public static ArrayList<Integer> makeRandomIncreasingList(int length) {
ArrayList<Integer> randomList = new ArrayList();
Random rand = new Random();
int inList = rand.nextInt(9) + 1;
int inList2 = rand.nextInt(9) + 1;
for (int i = 0; i < length; i++) {
randomList.add(inList);
inList = inList + inList2;
}
return randomList;
}
public static void doMergeTest() {
ArrayList<Integer> list1 = makeRandomIncreasingList(10);
ArrayList<Integer> list2 = makeRandomIncreasingList(20);
ArrayList<Integer> mergedList = merge(list1, list2);
System.out.println("List 1:" + list1);
System.out.println("List 2:" + list2);
System.out.println("Merged list:" + mergedList);
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println("Performing merge test #" + (i + 1) + ":");
doMergeTest();
}
}
}
Remove duplicates
arrayList1.remove(arrayList2);
Then merge two arrayList:
arrayList1.addAll(arrayList2);
And Lastly sort the last
collections.sort(arrayList1);
Another way is to use SET: Set doesnt allow duplicates
(HashSet is faster depending on the List implementation class)
Set setmerge = new HashSet(list1);
setmerge.addAll(list2);
list1.clear();
list1.addAll(setmerge);
The first part of your merge() method seems ok, if you modify it a little bit. You need to be going through both lists in parallel, something like
int i = 0, j = 0;
for (; i < l1.size() && j < l2.size();)
And compare individual items and increment indices independently, as in
if (l1.get(i) < l2.get(j)) {
...
i++;
} else
...
j++;
}
The way you were doing it you were literally going in parallel, which is not always correct (think of lists [1 2 2] and [1 1 1] => your merge would look like [1 1 1 2 1 2])
Then, after your "parallel" for-loop (the one where you're iterating through both lists), one of your indices is always going to break your loop because it's at the end of its list. For in-order merging, I usually declare i, j outside the loop (you'll need then after your first for-loop, like above) and then do something like (in your notation):
for (int i1 = i; i1 < l1.size(); i1++) {
mergeList.add(l1.get(i1));
}
for (int i2 = j; i2 < l2.size(); i2++) {
mergeList.add(l2.get(i2));
}
After your first for-loop, you get to the end of exactly one of the lists (someone's going to break the loop), so exactly one of the above loops is going to get executed, and that will contain the remaining items, in order.
Edit: your last for-loop of the merge() method is not correct for your purpose.
You have assumed l2 items are always bigger than l1 items, since you are adding remainder of l2 items in the end of the list. You need to compare them with mergedList items and add them accordingly.

Removing duplicates in an Array-based unsorted list without sets

Consider the operation removeAll, which removes all occurrences of element from a list.
The method returns the number of elements removed.
public int removeAll(E element)
Implement this operation for:
1. Array-based unsorted list
we cant use sets.
What i have started for now:
public int removeAll(T element) {
int duplicatesRemoved = 0;
for (int i = 0; i <= array.length - 1; i++) {
for (int j = i + 1; j <= array.length - 1; j++) {
if (array[i] == array[j]) {
}
}
I'm unable to get the rest done, any help please?
Dump the contents in a collection of some sort since you're not allowed to use Set, then pull out what's in the collection.
Arrays (the class, not primitive arrays) support a contains method, but you'll be iterating over the new collection everytime, making it inefficient.
Or if you can't use Array either, you can do it in a primitive array, just walking through looking for duplicates, over and over.
I suggest that you set the duplicate elements to something like null. After you've removed all duplicates, compact the array by swapping non-null elements at the end with the null elements in the middle.
Alternatively, create an empty array, and move non-duplicate elements to the new array.
Short answer
Use a Map.
More answer
Here is an algorithm, it is functional, but can be improved:
Create a Map<T, Boolean>. The second parameter type is whatever you want, I chose Boolean.
Create a new array named newArray; this will contain the unique values.
Iterate through the array. For each item perform the following:
Using the current arrayValue as the key, get the storedValue from the map.
if the storedValue is null (i.e. the Map.get() returned null) then this is a unique item. Insert the arrayValue, Boolean.TRUE into the map and add the arrayValue to the newArray.
if the storedValue is not null, then increment the duplicate count.
After iterating through the list, the newArray contains all non-duplicates and the duplicate count contains the count of duplicates.
1.
/**
* This method removes all duplicates from an array named "array"
* using a temporary List. So it converts the array into
* something like a Java set
*
* #return int number of duplicates removed
*/
public static int removeAll() {
int duplicates = 0;
List<Object> list = new ArrayList<>();
for(int i=0;i<array.length;i++){
Object element = array[i];
if(list.contains(element)) {
duplicates++;
}
else {
list.add(element);
}
}
array = list.toArray();
return duplicates;
}
2.
/**
* This method removes duplicates from an array named "array" using a
* temporary List.
* #param elementToBeRemoved
* #return int number of duplicates removed
*/
public static int removeAll(Object elementToBeRemoved) {
int duplicates = 0;
List<Object> list = new ArrayList<>();
for (int i = 0; i < array.length; i++) {
Object element = array[i];
if (list.contains(elementToBeRemoved)) {
duplicates++;
} else {
list.add(element);
}
}
array = list.toArray();
return duplicates;
}
As this seems homework, no explanation, but this puzzle:
int lengthWithValues = array.length;
for (int i = 0; i < lengthWithValues; i++) {
// Loop invariant: for all at < i array is sorted, unique and complete.
int valueToBeChecked = array[i];
for (int k = i + 1, int j = i + 1; k < lengthWithValues; j++) {
if (array[j] == valueToBeChecked) {
--lengthWithValues; // Remove duplicate
} else {
array[k] = array[j]; // Maintain differing from all at <= i.
++k;
}
}
}
duplicatesRemoved = array.length - lengthWithValues;
// array[0 .. length - duplicatesRemoved] is the unique array.
int[] uniqueArray = new int[lengthWithValues];
System.arrayCopy(array, 0, uniqueArray, 0, lengthWithValues);

Why does my output of randomly selected numbers contain duplicates?

My code needs to randomly select 6 numbers from a list ranging from 1 to 45.
Once when I ran my code (below) the output was [4, 4, 17, 18, 27, 37]. I was not expecting any duplicates in the output. How is it possible that there are duplicates? My code should be removing numbers from list as they are selected.
Random rng = new Random();
int size = 45;
int sixList[] = new int[6];
ArrayList<Integer> list = new ArrayList<Integer>(size);
ArrayList<Integer> list2 = new ArrayList<Integer>(6);
for(int i = 1; i <= size; i++) {
list.add(i);
}
Random rand = new Random();
for(int i = 0; list.size() > 39; i++){
int index = rand.nextInt(list.size());
if (index == 0){
index = rand.nextInt(list.size());
list2.add(index);
list.remove(index);
}else{
list2.add(index);
list.remove(index);
}
}
Collections.sort(list2);
System.out.print(list2);
The problem is that you are adding the index value to your list of random numbers.
Change your code
list2.add(index);
list.remove(index);
To
list2.add(list.remove(index));
List maintain index and does not care about duplicate elements at all. To avoid duplicates you must use Set rather than List. If you have any user-defined class going in the Set, then dont forget to implement equals() and hashcode() which are used to determine if elements are duplicate or not by the Set classes like HashSet.
If you have primitives going in you Set, then forget about duplicates as duplicates will be automatically handled for primitive data-types like int,long etc. So I suggest you to use Set rather than List. to avoid duplicate elements in the collection

Java set iteration in range

I want to iterate over java set by providing some range. like I can do it for list..
//for list
int startRange=3;
int endRange = 5;
List list = myList();
for (int i=startRange;i<endRange;i++){
System.out.println(list.get(i));
}
Now I wanted to iterate on below java set like above in startRange and endRange
Set<String> set = new java.util.HashSet<String>(list);
Thanks.
int count =0, min= 3 , max = 5;
Set<String> s = new HashSet<String>();
Iterator<String> itr = s.iterator();
while (itr.hasNext()) {
if ( count > min && count < max ) {
//Do something
}
count++;
}
You can use a LinkedHashSet and maintain the order.
See my comment below.
#alfasin is correct; List encompasses a total ordering on its elements, but HashSet has no such thing... PS you should use List.listIterator in your original code.
That being said, there is something similar for other kinds of Sets that do impose a total order -- SortedSets. There exists an analogous subSet, as well as headSet and tailSet which have similar functions. These also exist in NavigableSet along with other methods like ceiling, floor, etc.
Note that the orders here are not that much alike. List order is essentially arbitrary, whatever the user intended, while SortedSets order using a comparison sort.
Set is not ordered use Class LinkedHashSet
//You can change the Set to Array and then iterate over a range.
Set<Integer> set = new TreeSet<Integer>();
for (int i = 0; i <= 100; i++) {
set.add(i);
}
Object[] array = set.toArray();
for (Integer i = 0; i < 100; i++) {
System.out.print(array[i] + " ,");
}

Categories