How to find common sequences between two lists in Java - java

I'm trying to find common sequences between two lists.If we try to find common sequences in lists which has all unique values , i can do it. For example:
list one: [1, 8, 3, 13, 14, 6, 11]
listTwo : [8, 9, 10, 11, 12, 13, 14, 15]
As we can see , the [13,14] sequence is common for two list. My algorithm is , with retainAll function i'm having the common values , and for this example it's [8,11,13,14]. But since the list one has changed by "retainAll" function, i'm creating copy of list one. Then i'm taking positions of these common values from their original lists (list one and list two). After that i'm getting difference of positions for consecutive values. Like:
list1 list2 difList1 difList2
[8] 1 0 -1 (0-1) -1 (0-1)
[11] 6 3 -5 (1-6) -3 (0-3)
[13] 3 5 3 (6-3) -2 (3-5)
[14] 4 6 -1 (3-4) -1 (5-6)
If both difList1 and difLis2 values are shows "-1" that means , that value and the previous one is consecutive and makes sequence.Since [14] meets the condition in this example, the sequence is [13][14].
For this case , my code is:
public static void main(String args[]) {
List<Integer> list1= new ArrayList(Arrays.asList(1, 8, 3, 13, 14, 6, 11));
List<Integer> list2= new ArrayList(Arrays.asList(8, 9, 10, 11, 12, 13, 14, 15));
list1.retainAll(list2);
List<Integer> ori_list1= new ArrayList(Arrays.asList(1, 8, 3, 13, 14, 6, 11));
List<Integer> difList1= new ArrayList<>();
List<Integer> diffList2= new ArrayList<>();
difList1.add(-1); // Since the first element doesn't have any previous element in common elements list,i'm putting -1 on first index.
diffList2.add(-1); // Since the first element doesn't have any previous element in common elements list,i'm putting -1 on first index.
System.out.println(list1); // common elements are [8, 13, 14, 11]
for(int k=1;k<list1.size();k++){ // Let's say k = 2 ..
int index1_1 = ori_list1.indexOf(list1.get(k)); // For index 2, it takes actual index of 14 value -> 4
int index1_2 = ori_list1.indexOf(list1.get(k-1)); // it takes actual index of 13 value -> 3
int diff_list1 = index1_2-index1_1; // 3-4= -1 -> we got -1 .That means they're consecutive.
difList1.add(diff_list1); // And putting the -1 into the diffList1.
int index2_1 = list2.indexOf(list1.get(k)); // doing the same thing for list2.. -> 6
int index2_2 = list2.indexOf(list1.get(k-1)); // doing the same thing for list2.. -> 5
int diff_doc2 = index2_2-index2_1; // 5-6 = -1
diffList2.add(diff_doc2); // put -1 in diffList2
}
for(int y=1;y<difList1.size();y++){
if(difList1.get(y)==-1 && diffList2.get(y)==-1){ // Since they are both -1 for 14 value
System.out.println("The common sequence is:"+list1.get(y-1)+" "+list1.get(y)); // Print them
}
}
}
But I need the solution for the duplicate elements situation. Let's say we have lists like
list one: [1, 8, 3,10, 13,8,10, 14, 6, 11]
listTwo : [8, 9, 10, 11, 12,8,10, 13, 14, 15]
Now we have another common sequence [8,10].In the output , i wanna see both [13,14] and [8,10]. But i only see [13,14]. Because when indexes are calculating for 8 and 10 , the program takes the indexes of first 8 and 10. For list1 , it takes 1st index for 8 value and 3rd index for 10 value. But i need to pass them since i used them already, i need indexes like 5 and 6,not 1 and 3 again.
And i don't know how to find sequences which has more than two values. For example not only [13,14] but also [13,14,15] or more if they are consecutive. I know it's kinda tough question but i need your help.

I am not exactly sure what you are trying to do but if I was doing common sequences I would do it by creating sublists and comparing them:
public static Set<List<Integer>> findCommonSequence(List<Integer> source, List<Integer> target, int startLength) {
Set<List<Integer>> sequences = new LinkedHashSet<>();
// algorithm works in this way:
// we prepare all possible sublists of source list that are at least startLength length
// and then we check every of those sublists against the target list to see if it contains any
// length is from startLength to maxSize, to check all sublists with that length
// ie if startLength is 2 and source is 10, it will be 2 - 10 and thus it will check all sublist sizes
for (int length = startLength; length < source.size(); length++) {
// startIndex will move from 0 to original_list - length, so if length is 2, it will generate sublists
// with indexes 0,1; 1,2; 2,3 ... 8,9
for (int startIndex = 0; startIndex+length < source.size(); startIndex++) {
// creates lightweight sublist that shares the data
List<Integer> sublist = source.subList(startIndex, startIndex+length);
// add all found subsequences into the set
sequences.addAll(findSequenceIn(target, sublist));
}
}
return sequences;
}
// Returns all subsequences that are inside the target list
private static Set<List<Integer>> findSequenceIn(List<Integer> target, List<Integer> sublist) {
Set<List<Integer>> subsequences = new LinkedHashSet<>();
// simply do the same process as in first method but with fixed length to the length of sublist
for (int i=0; i<target.size() - sublist.size(); i++) {
// create another sublist, this time from target (again, share data)
List<Integer> testSublist = target.subList(i, i+sublist.size());
// compare two sublists, if they are equal, that means target list contains sublist from original list
if (testSublist.equals(sublist)) {
// add it to the set
subsequences.add(new ArrayList<>(sublist));
}
}
return subsequences;
}
You can then optimize the algorithm to just do the checks via sending the indexes instead of sublists and do the comparing manually. Complexity of this algorithm should be from O(n3) to O(n4). Might be O(n4) because we do up to n2 sublists and then compare which is n operation against n sublists of list 2, but it might be n3 because comparisons are smaller, no idea mathematically how close it is to n3 or n4.
Of course there is another n with copy of sublist but you can optimize that one out.

handling the int-values as codePoints:
[1] convert list2 to str2
[2] convert list1 to str1 with all int-values on the left stripped, that are not in list2
[3] move str1 over str2 remembering the longest sequence of str1 standing on top of str2
ArrayList<int[]> results = new ArrayList<>();
String str2 = new String(
new int[] { 1, 8, 3, 10, 13, 14, 8, 10, 14, 6, 11 }, 0, 11 ); //[1]
int[] tmp = new int[] { 8, 9, 10, 11, 12, 8, 10, 13, 14, 15 };
int[] arr1 = IntStream.of( tmp ).dropWhile(
c -> str2.indexOf( c ) < 0 ).toArray(); //[2]
String str1 = new String( arr1, 0, arr1.length );
for( int i = str1.length() - 2; i >= 0; i-- ) { //[3]
int[] rslt = new int[0];
for( int j = 0; j < str2.length() - 2; j++ ) {
int[] idx2 = new int[] { j };
rslt = str1.substring( i ).codePoints().takeWhile(
c -> c == (int)str2.charAt( idx2[0]++ ) ).toArray();
if( rslt.length >= 2 ) {
results.add( rslt );
}
}
}
results.forEach(a -> System.out.println( Arrays.toString( a ) ));
gets: [13, 14], [10, 13, 14], [8, 10]

Related

How to copy a Set<Set<Integer>> into a List<Integer[]> in Java?

I want to copy a Set<Set<Integer>> of array to a List of Integer[]. I want to do that because I want to change the first element of every array when it is 0 , and put it at the end.
I have manage to do that but in a List<Set> , and when looping throw , I can't make changes because the array are still a Set.
No other questions make this clear, instead they explained when we have only Set<SomeWrapperClass>
Maybe some other solutions? Remark: I cannot change the Set<Set<Integer>>
My Set:
Set<Set<Integer>> indexs = new HashSet<>();
I can convert to a List only in this way:
List<Set<Integer>> arrangedIndexOfCourses = new ArrayList<>();
static Set<Set<Integer>> coursesIndex = Sets.powerSet(Sets.newHashSet(1, 2, 3, 4, 5, 6, 7, 8, 9, 0));
static List<Set<Integer>> arrangedIndexOfCourses = new ArrayList<>();
The following method return all the occurence of unique numbers made from 0123456789 ( Guava library )
private void indexSet(Set<Set<Integer>> coursesIndex) {
Set<Set<Integer>> indexs = new HashSet<>();
for (Set<Integer> token : coursesIndex) {
indexs.add(token);
arrangedIndexOfCourses.add(token);
count++;
}
}
And the code where I tried to change the first 0 of the arrays and put at last:
for (Set<Integer> i : arrangedIndexOfCourses) {
if (i.contains(0)) {
Collections.rotate(arrangedIndexOfCourses, -1);
}
}
It appears, the main trick in this task is how to implement rotation of the first element if it is 0.
As mentioned in the comments, not all types of sets maintain order and therefore have a "first" element. But upon converting a set to stream, even for a HashSet there's a first element in the stream unless it's empty.
Therefore, the set of integer sets may be converted to a list of integer arrays as follows:
Take the first element of the stream using limit or findFirst
Compare to 0, if needed put it to the end
Else keep the stream of the set as is
Set<Set<Integer>> data = Set.of(
Collections.emptySet(),
new LinkedHashSet<>(Arrays.asList(0, 1, 2, 3)),
new LinkedHashSet<>(Arrays.asList(4, 5, 6)),
new LinkedHashSet<>(Arrays.asList(10, 0, 100, 1000)),
new TreeSet<>(Arrays.asList(25, 0, 1, 16, 4, 9)),
new HashSet<>(Arrays.asList(0, 5, 50))
);
List<Integer[]> rotatedZeros = data
.stream()
.map(s ->
(s.stream().findFirst().orElse(-1) == 0 // or limit(1).findAny()
? Stream.concat(s.stream().skip(1), Stream.of(0))
: s.stream())
.toArray(Integer[]::new)
)
.collect(Collectors.toList());
rotatedZeros.stream().map(Arrays::toString).forEach(System.out::println);
Output:
[]
[4, 5, 6]
[1, 2, 3, 0]
[10, 0, 100, 1000]
[1, 4, 9, 16, 25, 0]
[50, 5, 0]
the 2-step solution with a lambda w/o external library
(1) convert the Set<Set<Integer>> to a List<Integer[]>
List<Integer[]> arrangedIndexOfCourses = coursesIndex.stream()
.map(s -> s.toArray(Integer[]::new)).collect(toList());
(2) iterate over the arrays and change the zero-arrays in the traditional way
for (Integer[] indices : arrangedIndexOfCourses) {
if (indices.length > 0 && indices[0] == 0) {
for (int i = 0; i < indices.length - 1; i++)
indices[i] = indices[i + 1];
indices[indices.length - 1] = 0;
}
}
a lambda based on Alex's mind blowing solution
moving the zero-rotating-stuff upstream makes the lambda less trickier and You can work with collections instead of arrays
List<Integer[]> arrangedIndexOfCourses = coursesIndex.stream()
.map(s -> {
if (s.stream().findFirst().orElse(-1) == 0) {
List<Integer> l = new ArrayList<>();
l.addAll(s);
l.remove(0);
l.add(0);
return l.toArray(Integer[]::new);
} else
return s.toArray(Integer[]::new);
}).collect(toList());

how to find latest element added or deleted in dynamic array

I am getting an input array in loop which contains numbers in sorted order. on Every iteration the input array will either be added or be deleted with any one number (no duplicates in input array). Example
1st iteration: Input Array [3,4,8,19]
2nd iteration: Input Array [3,4,5,8,19]
Output: 5 added
3rd iteration: Input Array [3,4,5,8,19,40]
Output: 40 added
4th iteration: Input Array [3,5,8,19,40]
Output: 4 deleted
5th iteration: Input Array [1,3,5,8,19,40]
Output: 1 added
Note: I have a solution where I can take a map or different array and copy the input array in new array then from next iteration onward I'm going to iterate input array and compare the input array's elements with new array, the one not present in new array is the one added; and the one present in new array but not present in input array is the one deleted. I am looking for a better approach with most optimized logic in terms of space and time.
Given below is one of the simplest ways:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int first[] = { 3, 4, 8, 19 };
int second[] = { 3, 4, 5, 8, 19 };
int diff = Arrays.stream(second).sum() - Arrays.stream(first).sum();
System.out.println(Math.abs(diff) + (diff > 0 ? " added." : diff < 0 ? " deleted." : ""));
}
}
Output:
5 added.
Demo:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[][] testArrs = {
{ 3, 4, 8, 19 },
{ 3, 4, 5, 8, 19 },
{ 3, 4, 5, 8, 19, 40 },
{ 3, 5, 8, 19, 40 },
{ 1, 3, 5, 8, 19, 40 } };
int i, diff = 0, lastSum = Arrays.stream(testArrs[0]).sum(), currentSum;
for (i = 1; i < testArrs.length; i++) {
currentSum = Arrays.stream(testArrs[i]).sum();
diff = currentSum - lastSum;
System.out.println(Math.abs(diff) + (diff > 0 ? " added." : diff < 0 ? " deleted." : ""));
lastSum = currentSum;
}
}
}
Output:
5 added.
40 added.
4 deleted.
1 added.
If the array is always sorted and the input only changes in one place, then if you have access to both the old and the new array at each iteration, an efficient algorithm could search for the first different elememt between the arrays in O(log n) time with binary search. If mid points to different elements, look in the left half; otherwise, in the right.

Java program to find 'Lucky' numbers from 0 to n using Lists

I have to write a program that finds all the lucky numbers from 0 to any number n.
Here's what a lucky number is:
Consider the sequence of natural numbers.
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 ………………………………….
Removing every second number produces the sequence
1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23 ………………………….
Removing every third number produces the sequence
1, 3, 7, 9, 13, 15, 19, 21, 25 ………………………….
This process continues indefinitely by removing the fourth, fifth…and so on, till after a fixed number of steps, certain natural numbers remain indefinitely. These are known as Lucky Numbers.
I decided to try using ArrayList for this. But I can't seem to figure this small bit out. I've been trying for days now.
Here's the code:
import java.util.*;
class luckyy
{
public static void main(String args[])
{
Scanner scan = new Scanner(System.in);
System.out.println("Enter n: ");
int i, n, index;
n = scan.nextInt();
ArrayList <Integer> ele = new ArrayList <Integer> (n);
//storing in a list
for(i = 1;i<=n;i++)
{
ele.add(i);
}
System.out.println(ele);
int count = 2;
index = 1;
System.out.println("Size: "+ele.size());
while(count<ele.size())
{
for(i = 0;i<ele.size();i++)
{
int chk = ele.get(i);
if(chk%count == 0)
{
ele.remove(i);
}
}
count++;
}
System.out.print(ele);
}
}
This gives the output:
[1, 5, 7]
When the desired output is:
[1, 3, 7]
So, sorry if this code is so bad that it's offensive haha...
But I would really appreciate any help. I am just starting out as a programmer, and have a lot to learn, and would love any advice. Thanks to anyone who tries to help me!
First of all it seems to me that your assumption of the expected output is not correct. According to the task you described, the out should be something like this:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25] // every 2nd number removed
[1, 3, 7, 9, 13, 15, 19, 21, 25] // every 3rd number removed
[1, 3, 7, 13, 15, 19, 25] // every 4th number removed
[1, 3, 7, 13, 19, 25] // every 5th number removed
[1, 3, 7, 13, 19] // 6th number removed = final output
Beside that, I see two mistakes.
You want to remove "every n-th number", so you don't want to test the values, but their position/index in the list.
Whenever you remove an element from an ArrayList, the index of the following elements and the size of the list is reduced by 1. Means if you start with removing the "2", the next number to remove will be the "5", not the "4" as desired (assuming you are testing the index, not the value). One solution for that would be to test the indexes starting at the end of the list. In that case it wouldn't matter that higher indexes change after removing elements, because you already passed them.
Edit
Answer edited in regard to request of #Kedar Mhaswade to provide some code in order to test how removing elements from the end of the list perfoms.
This is my first approach:
List<Integer> removeBackward(List<Integer> numbers) {
int count, sPos;
count = 2;
while(count<=numbers.size())
{
for(sPos = numbers.size(); sPos >= numbers.size()-count; sPos--)
{
if(0 == sPos%count) {
break;
}
}
for(int pos = sPos; pos > 0; pos=pos-count)
{
numbers.remove(pos-1);
}
count++;
}
return numbers;
}
I did some tests (see below) with the result that it performs quite well for small sets of numbers (< 12000). On larger sets the second approach of #Kedar Mhaswade (maintaining an extra list for the elements to retain) outperforms this approach.
Therefore I tried a second approach:
The idea was, that there is no need to maintain a second list for retained elements when at first step half of the elements will be removed and the number of elements to retain decreases step by step.
So I simply moved the elements to retain to the end of the same list and maintain additional pointers in order to know the range of the retained elements. At the end of the process the final result only needs to be extracted from the end of the list.
List<Integer> retainedAtEnd(List<Integer> numbers) {
int removeX, baseQty, retainedQty, basePos, retainedPos;
removeX = 1;
baseQty = numbers.size();
while(removeX <= baseQty)
{
removeX++;
basePos = numbers.size();
retainedPos = basePos;
retainedQty = 0;
for(int checkPos = baseQty; checkPos >= 1; checkPos--)
{
if(0 != checkPos%removeX)
{
basePos = numbers.size()-baseQty+checkPos;
numbers.set(retainedPos-1, numbers.get(basePos-1));
retainedPos--;
retainedQty++;
}
}
baseQty = retainedQty;
}
return numbers.subList(numbers.size()-baseQty, numbers.size());
// return new ArrayList(numbers.subList(numbers.size()-baseQty, numbers.size()));
}
According to my test unforunately this approach doesn't perform to good on small sets (<12000). It can not compete with the first or #Kedar Mhaswade's second approach, but on larger sets, it outperforms both.
Here is how I tested:
public void test() {
int n = 1000;
long start;
System.out.println("Testing with " + n + " numbers ...");
System.out.println("Test removing elements backwards:");
List<Integer> numbers1 = Stream.iterate(1, k -> k + 1).limit(n).collect(Collectors.toList());
start = System.nanoTime();
List<Integer> out1 = this.removeBackward(numbers1);
System.out.println("Time taken:" + (System.nanoTime() - start));
// System.out.println(out1);
System.out.println("Test maintaining extra list for retained elements:");
List<Integer> numbers2 = Stream.iterate(1, k -> k + 1).limit(n).collect(Collectors.toList());
start = System.nanoTime();
List<Integer> out2 = this.extraRetainedList(numbers2);
System.out.println("Time taken:" + (System.nanoTime() - start));
// System.out.println(out2);
System.out.println("Test collecting retained elements at end of list:");
List<Integer> numbers3 = Stream.iterate(1, k -> k + 1).limit(n).collect(Collectors.toList());
start = System.nanoTime();
List<Integer> out3 = this.retainedAtEnd(numbers3);
System.out.println("Time taken:" + (System.nanoTime() - start));
// System.out.println(out3);
System.out.println("Test maintaining extra list for elements to remove:");
List<Integer> numbers4 = Stream.iterate(1, k -> k + 1).limit(n).collect(Collectors.toList());
start = System.nanoTime();
List<Integer> out4 = this.extraDeletedList(numbers4);
System.out.println("Time taken:" + (System.nanoTime() - start));
// System.out.println(out4);
}
This is a tricky problem! I went about it in a slightly different fashion and used another list to store the deleted elements. This is required because of the data structure that I chose. Since I wanted to use integers only and I was using an ArrayList, every time I remove an element, the list gets immediately adjusted. What we really need to do is mark the element for deletion. There are more than one way to do this, but I chose to maintain another list of deleted elements (since all the elements are unique, it is fine to use this idea).
Here is my first attempt then:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/** <p>
* Find the lucky numbers amongst natural numbers from 1 to n.
* Here's how you find Lucky numbers.
* </p>
* Created by kmhaswade on 2/27/16.
*/
public class Lucky {
public static void main(String[] args) {
printLucky1(Integer.valueOf(args[0]));
}
private static void printLucky1(int n) {
List<Integer> numbers = Stream.iterate(1, k -> k + 1).limit(n).collect(Collectors.toList());
System.out.println(numbers);
int delIndex = 1; // index of the element to be removed, we start with 2nd element
while (delIndex < numbers.size()) {
List<Integer> deleted = new ArrayList<>();
for (int i = delIndex; i < numbers.size(); i += (delIndex + 1)) {
deleted.add(numbers.get(i));
}
numbers.removeAll(deleted); // expensive operation!
System.out.println(numbers);
delIndex += 1;
}
System.out.println("number of lucky numbers:" + numbers.size());
System.out.println(numbers);
}
}
This works! But for really long lists, this is very slow, because of the expensive operation: numbers.removeAll(deleted) -- we are removing bunch of elements from an ArrayList that has to move all affected elements on every deletion!
For instance, with the first 100_000 natural numbers, it takes about 10 seconds on my computer. Clearly, I looked for an alternative. What if we devise another list and collect the elements that we want to retain, and then in the next iteration, this list of retained elements becomes our list to operate on? It looked like that will work better because there is no deletion of elements involved. You will still need to have another ArrayList to collect the elements. In analysis terms, this is an O(n) additional space (or c.n where c ~ 0.5).
Here's my second attempt then:
private static void printLucky2(int n) {
List<Integer> numbers = Stream.iterate(1, k -> k + 1).limit(n).collect(Collectors.toList());
System.out.println(numbers);
int delIndex = 1; // index of the element to be removed, we start with 2nd element
while (delIndex < numbers.size()) {
List<Integer> retained = new ArrayList<>();
for (int i = 0; i < numbers.size(); i += 1)
if ((i+1) % (delIndex + 1) != 0)
retained.add(numbers.get(i));
numbers = retained;
System.out.println(numbers);
delIndex += 1;
}
System.out.println("number of lucky numbers:" + numbers.size());
System.out.println(numbers);
}
There may be more improvements possible because for really large inputs the time taken by this algorithm may still be unacceptable (will work on that improvement). But I already see two orders of magnitude improvement!
Here's the complete code. I made sure that both the methods return lists that are same (list1.equals(list2) returns true) and here is the output on my computer (with first 100_000 numbers):
[1, 3, 7, ...]
number of lucky numbers: 357
time taken: 6297
number of lucky numbers: 357
[1, 3, ...]
time taken: 57
for anyone still interested, I found another way to do this.
It isn't revolutionary or anything, and it uses a lot of the stuff the people on this thread told me, but it sort of summarizes all the advice I guess.
I felt it only fair to post my answer.
So, it involves iterating through every element, storing all the elements you need to remove in each round of counting, and then using the removeAll function to remove them before the count increases.
Here's the complete code for anyone interested. Any comments would also be welcome.
import java.util.*;
class luckyy
{
public static void main(String args[])
{
Scanner scan = new Scanner(System.in);
System.out.println("Enter n: ");
int i, n, index;
n = scan.nextInt();
ArrayList <Integer> ele = new ArrayList <Integer> (n);
ArrayList <Integer> toRemove = new ArrayList <Integer> (n);
//storing in a list
for(i = 1;i<=n;i++)
{
ele.add(i);
}
System.out.println(ele);
int count = 2;
System.out.println("Size: "+ele.size());
while(count<=ele.size())
{
for(i = 0;i<ele.size();i++)
{
if((i+1)%count == 0)
{
toRemove.add(ele.get(i));
}
}
ele.removeAll(toRemove);
toRemove.clear();
count++;
}
System.out.println(ele);
}
}
There it is! It works, and boy am I glad! If anyone has any further advice, or anything else for me to learn or checkout, you are totally welcome to comment.
Thanks again everyone!

find unique elements from sorted array with complexity < O(n) [duplicate]

This question already has answers here:
Finding unique numbers from sorted array in less than O(n)
(5 answers)
Closed 8 years ago.
An interveiewer asked me below question:
Search out unique integer values(approx. 1000) from a sorted array of billion records(like 1,1,1,1,3,3,3,4,5,5,6,6,6,6,6,7,7,7,7,7,7,8,8,8,8...........) with complexity less than O(n).
NOTE: NOt to use SET.
One solution that i tried to implement:
Divide that array into two set of arrays,then iterate both subarrays and search in hashmap if element doesnot exit,then add it into hashmap otherwise move to next iteration.
public static void main(String[] args) {
int arr[] = {1,2,4,9,-3,5,6,3,6,12,5,6,2,-1,-3,6,87,9,2,3,5,7,9,1,0,1,3,5,7,6,3,8,6,3,21,45,6};
int size1 =0, size2 = 0;
HashMap<Integer, Integer> map = new HashMap<Integer,Integer>();
System.out.println("length of Array:"+arr.length);
if((arr.length)%2 == 0){
size1 = size2 = arr.length/2;
}else{
size1 = (arr.length + 1)/2;
size2 = (arr.length)/2;
}
for(int i=0;((size1-i-1)>= 0)||((size2+i)<(arr.length - 1));i++){
if(map.containsKey(arr[size1 -i-1])== false){
map.put(arr[size1 -i-1],arr[size1 -i-1]);
}
if(map.containsKey(arr[size2 + i]) == false){
map.put(arr[size2 + i], arr[size2 + i]);
}
}
System.out.println(map.keySet());
}
And its working as expected, then he asked what if we divide the array into n sets?
then the complexity would be O(1) or O(n/n)? Is it possible?
Please suggest if there is another way to implement the same without using hashmap?
I'd try a binary search based approach, start from the middle element - if it's identical to one of the edges, then you can use the fact the array is sorted and eliminate that half. If it's different from each of the ones on the edges - divide the array into halves and proceed on each of them recursively.
This is still O(n) in the worst case, but on average it may be better then passing over the entire array (especially if there are many repetitions)
example -
1 1 1 1 1 2 2 2 2
could be done in two steps
why don't you use a Set instead of Map. Anyways, Set does not allow duplicate elements.
public static void main(String[] args) {
int arr[] = { 1, 2, 4, 9, -3, 5, 6, 3, 6, 12, 5, 6, 2, -1, -3, 6, 87,
9, 2, 3, 5, 7, 9, 1, 0, 1, 3, 5, 7, 6, 3, 8, 6, 3, 21, 45, 6 };
Set<Integer> aset = new HashSet<Integer>();
System.out.println("length of Array:" + arr.length);
for (int i : arr) {
aset.add(i);
}
System.out.println(aset);
}

HashMap wrong values for keys

I am kinda new to Java, and I am trying to write a function that maps all element indexes from an ArrayList into a HashMap, so I can easily see the indexes of duplicate elements.
The code below works , but when I try to print the values using the second for, it shows completely different results!
Example:
60 [40, 64]
What the 2nd for shows
60 [64]
more numbers
60 [64]
HashMap<Integer,ArrayList<Integer>> table= new HashMap<Integer,ArrayList<Integer>>();
//checking all items in an ArrayList a
//and putting their index in a hashTable
for(int i=0; i<a.size(); i++){
ArrayList<Integer> indexes = new ArrayList<Integer>();
indexes.add(i);
for(int j=i+1; j<a.size(); j++){
//are the items equal?
if(a.get(j).equals(a.get(i))){
indexes.add(j);
}
}
//put in the HashMap
table.put(a.get(i), indexes);
System.out.println(a.get(i) + " " +table.get((a.get(i))));
}
//shows completely different results!
for(int ix=1;ix<table.size();ix++)
System.out.println(a.get(ix) + " " +table.get(a.get(ix)));
Try this:
public static void main(String[] args) {
List<Integer> input = Arrays.asList(60, 60, 1, 4, 5, 7, 60);
Map<Integer, List<Integer>> result = new HashMap<>();
for (int n = 0; n < input.size(); ++n) {
List<Integer> list = result.get(input.get(n));
if (list != null) {
list.add(n);
} else {
list = new ArrayList<>();
list.add(n);
result.put(input.get(n), list);
}
}
System.out.println(result); // prints {1=[2], 4=[3], 5=[4], 7=[5], 60=[0, 1, 6]}
}
But I don't get it...What did I do wrong? As far as I see, my code is really inefficient compared to yours, but shouldn't it do the same thing?
Well no. In addition to being inefficient, your version has a significant bug.
Lets take your example input {60, 60, 1, 4, 5, 6, 7, 60}.
First iteration the loop, you build a list containing {0, 1, 7} and put it into the map so that we have map containing{ 60 -> {0, 1, 7} }`
Second iteration of the loop, we build a list containing {1, 7} and put it into the map. But this of course replaces the original (correct) list for 60 ... and we end up with { 60 -> {1, 7} }
And so on. In short, your version will end up producing a map that maps from the values to a list containing just the last index of that value.

Categories