Optimization of deletion nodes in 2-d array - java

I have double dimensional array of dimensions 720x90. Let's denote rows by R and C as columns.
R1 = {C1,...,C90}
....
R720 = {C1,...C90}
Now, I want to see if any of the data in any of the rows appears anywhere else in any other rows. For instance, lets say the data in row 470 and column 67 is a duplicate of row 672 and column 34. In that case, I want to remove both row 470 and row 672 from the data set and continue checking. After I have checked all the rows, I want to print just the index of the rows that have survived. I have written a brute-force method of this. However, when I run this code, it never returns and I am not able to diagnose why. Also, is there a more efficient way to do this?
//check all the subsets of the interleaved data
public static int checkSubsets(String[][] subsets){
List subset = new ArrayList();
for(int i = 0; i< 720; i++){
for(int j = 0; j < 90; j++)
subset.add(subsets[i][j]);
}
Object duplicate;
Iterator itr = subset.iterator();
while(itr.hasNext()){
duplicate = itr.next();
while(itr.hasNext()){
subset.remove(duplicate);
itr=subset.iterator(); //to avoid concurrent modification
itr.next();
}
}
return subset.size();
}
Clarifications: Lets say I am iterating through looking at each value in the matrix. I take the first value in R1 C1 (row 1 - column 1). I find that these values are found somewhere in the 12, 346,123, 356 row. Then I remove all those rows from the matrix. So now the matrix is 5 rows smaller. I stop checking row 1 now and move onto row 2. I continue checking, skipping over row 12, 346, 123, and 356. Hence, I am after a row that is unique (has 90 values that are all unique).

I am not sure what the code you wrote has to do with the requirement, I will give you the approach of the answer yet you have to try it yourself first.
it is clear that you need to iterate on each row to check for possible duplicates yet this will cause a performance failure , you can overcome this with a smiple use of HashMap, first store each entry in the map , the key will be the value of the node of the array, and the value should be the coordinates of this node.
When iterating over the array for each row you should find the y coordinates from the map which is common between all nodes of the row, so duplicate rows detected.
In order to avoid keep checking the already removed rows try to store all the rows to be deleted and remove them once you are done, you can use Set to store them to avoid duplicate.
Good luck with the implemenation.

The algorithm is almost there, but helpfull data-structures are missing.
To add a bit of spice I used Java 8 somewhat.
As you did one can collect the values to check for duplicates.
However one needs to remember the first row of that value, as only there it is still unknown whether a duplicate exists.
public static int checkSubsets(String[][] subsets) {
// The results.
final Set<Integer> duplicateRows = new HashSet<>();
// From the first occurrence of a duplicate value we do not know it yet,
// so need to remember.
final Map<String, Integer> firstRowOfValue = new HashMap<>();
for (int i = 0; i < subsets.length; ++i) {
for (int j = 0; j < subsets[i].length; ++j) {
final String value = subsets[i][j];
Integer oldRow = firstRowOfValue.putIfAbsent(value, i);
if (oldRow != null) { // Duplicates
duplicateRows.add(i);
duplicateRows.add(oldRow);
// oldRow might already be added if third duplicate or same row.
}
}
}
IntStream.rangeOf(0, subsets.length)
.filter(i -> !duplicateRows.contains(i))
.forEach(System.out::println);
return subsets.length - duplicateRows.size();
}
The IntStream part would be in java 7:
for (int i = 0; i < subsets.length; ++i) {
if (!duplicateRows.contains(i)) {
System.out.println(i);
}
}
With java 7 you can safely substitute here putIfAbsent with put.

Related

looping through List and removing element skipps certain elements

If have a workflow that removes elements of a List by a certain criteria. However certain items are skipped? Why is this happening?
List<Integer> listWithAge = new ArrayList<>();
int randomNumber = 100;
for (int i = 0; i < randomNumber; i++) {
listWithAge.add(i);
}
// this is my loop
for (int i = 0; i < listWithAge.size(); i++) {
System.out.println(i);
if ((listWithAge.get(i) % 3) == 2) listWithAge.remove(i);
}
Above code is my loop. I replaced my condition with something simpler. If I run this code my second loop only runs for 67 turns instead of 100.
It is problematic to iterate over a list and remove elements while iterating over it.
If you think about how the computer has to reconcile it, it makes sense...
Here's a thought experiment for you to go through.
If you have a list that is size 10 and you want to remove elements 1, 5, and 9 then you would think maybe the following would work:
List<String> listOfThings = ...some list with 10 things in it...;
list.remove(0);
list.remove(4);
list.remove(8);
However, after the first remove command, the list is only size 9.. Then after the second command, it's size has become 8. At this point, it hardly even makes sense to do list.remove(8) anymore because you're looking at an 8-element list and the largest index is 7.
You can also see now that the 2nd command didn't even remove the element now that you wanted.
If you want to keep this style of "remove as I go" syntax, the more appropriate way is to use Iterators. Here's an SO that talks about it and shows you the syntax you would need (see the question). It's easy to read up on elsewhere too.
How Iterator's remove method actually remove an object
Skipping a value would be the result of your list getting out of sync with your loop index because the list is reduced in size. This causes you to hop over some locations since the reduction in size affects future locations that have not been reached.
So the first thing you could do is simply correct the synchronization by decrementing i when you remove a value from the list. This will keep index at the same spot as the list shifts "left" caused by the removal.
for (int i = 0; i < listWithAge.size(); i++) {
if ((listWithAge.get(i) % 3) == 2) listWithAge.remove(i--);
}
The other option is to loop thru the list backwards.
for (int i = listWithAge.size()-1; i >= 0; i--) {
if ((listWithAge.get(i) % 3) == 2) {
listWithAge.remove(i);
}
}
This way, no values should be skipped since the removing of the element does affect the loop index's future positions relative to the changing size of the list.
But the best way would be to use an iterator as has already been mentioned by
Atmas
As a side note, I recommend you always use blocks {} even for single statements as I did above in the if block. It will save you some serious debugging time in the future when you decide you need to add additional statements and then wonder why things are no longer working.
And deleting like this from a list is very expensive, especially for large lists. I would suggest that if you don't have duplicate values, you use a Set. Otherwise, instead of deleting matching values, add the non-matching to a second list.
List<Integer> listWithAge = new ArrayList<>();
int randomNumber = 100;
for (int i = 0; i < randomNumber; i++) {
listWithAge.add(i);
}
// this is my loop
List<Integer> itemsToBeDeleted = new ArrayList<>();
for (int i = 0; i < listWithAge.size(); i++) {
System.out.println(i);
if ((listWithAge.get(i) % 3) == 2) {
itemsToBeDeleted.add(i);
}
//delete all outside the loop
//deleting inside the loop messes the indexing of the array
listWithAge.removeAll(itemsToBeDeleted);

Compare elements from an ArrayList

I have a small problem, I want to go through a list and compare two objects of the array. Each object has 3 elements, I use a StringTokenizer to be able to remove the separator, so each object has 3 elements. I would like to know how to make a method that gets the third element of each object and compare them. And if that element is less than another delete that element and the 2 before it.
I tried to make them with an iterator but I wouldn't know very well that it started from the 3 element and increased the position by 3.
Iterator<Integer> it = lisM.iterator();
int num;
while (it.hasNext()){
num = it.next();
System.out.println(num);
}
Is --> if, I was wrong to put it in the picture
This only answers part of your question. I could not understand the question completely, please edit it and I can edit my answer.
You should not remove items from a list whilst in a for loop, therefore you can, for example, create another boolean list with the same size divided by 3 and just fill it with true Booleans then set the position divided by 3 to false if you want to delete the three items. Then you can create a new list, iterate over the boolean list and add 3 "Objects" which are actually Strings (thanks #JB Nizet) at a time, every time the boolean list element is true. When it is false you just don't add the elements and by doing so you are practically deleting the two elements before that element together with that element.
You casted a String to an int, that does not work you have to parse the Strings.
I corrected some of your code and added the boolean list here:
ArrayList<String> lisM = new ArrayList<>(); // here I initialise the list as an array list with strings.
ArrayList<Boolean> booleanList = new ArrayList<>();
for (int i = 0; i < lisM.size() / 3; i++) {
booleanList.add(true);
}
for(int i = 3; i < lisM.size();i+=3) {
int m = Integer.parseInt(lisM.get(i)); // here I changed the casting to parsing and moved it out of the for loop, there is no need to initialize it again every single time since you do not change it in the second for loop.
for (int j = 6; j < lisM.size(); j += 6) {
int m1 = Integer.parseInt(lisM.get(j));// here I changed the casting to parsing again.
if (m > m1) { // this makes no sense here because you are going over all of the elements of the list and comparing them to all of them. But I kept it here for the sake of example.
booleanList.set(i/3,false);
}
// if you want to go over the whole list you will have to clear the list and start over again for every element.
}
}
and here is how you could create the new list without the elements you do not want:
ArrayList<String> newLisM = new ArrayList<>();
for (int i = 0; i <booleanList.size(); i++) {
if(booleanList.get(i))
for (int j = 0; j < 3; j++) {
newLisM.add(lisM.get(i+j));
}
}

2 dimensional array & method calls - beginner

I'm currently working on a homework assignment for a beginner-level class and I need help building a program that tests if a sodoku solution presented as an int[][] is valid. I do this by creating helper methods that check both rows, columns and grids.
To check the column I call a method called getColumn that returns a column[]. When I test it out it works fine. I then pass it out on a method called uniqueEntries that makes sure that there are no duplicates.
Problem is, when I call my getColumn method, it returns an array consisting of only one number (for example 11111111, 22222222, 33333333). I have no idea why it does that. Here is my code:
int[][] sodokuColumns = new int[length][length];
for(int k = 0 ; k < sodokuPuzzle.length ; k++) {
sodokuColumns[k] = getColumn(sodokuPuzzle, k);
}
for (int l = 0; l < sodokuPuzzle.length; l++) {
if(uniqueEntries(sodokuColumns[l]) == false) {
columnStatus = false;
}
}
my helper is as follows
public static int[] getColumn(int[][] intArray, int index) {
int[] column = new int[intArray.length];
for(int i = 0 ; i < intArray.length ; i++) {
column[i] = intArray[i][index];
}
return column;
}
Thanks !
You said:
when I call my getColumn method, it returns an array consisting of only one number (for example 11111111, 22222222, 33333333).
I don't see any issue with your getColumn method other than the fact it's not even needed because getColumn(sodokuPuzzle, k) is the same as sodokuPuzzle[k]. If you're going to conceptualize your 2D array in such a way that your first index is the column then for your purpose of checking uniqueness you only need to write a method to get rows.
The issue you're having would seem to be with another part of your code that you did not share. I suspect there's a bug in the logic that accepts user input and that it's populating the puzzle incorrectly.
Lastly a tip for checking uniqueness (if you're allowed to use it) would be to create a Set of some kind (e.g. HashSet) and add all of your items (in your case integers) to that set. If the set has the same size as your original array of items then the items are all unique, if the size differs there are duplicates.

Initialize 2D arrayList with specified number of columns but not rows?

I want to make a 2D ArrayList, with the number of columns already specified but not the rows.
For example, I want to create a table with 26 columns and 0 or 1 columns at first, then after each loop of doing something else, the number of rows will increase along with that loop.
When I increase the number of rows (length of an ArrayList of ArrayLists), I also want all 26 arrays to increase as well. What is the syntax for it?
And how would I index into, or add a new item into a specific location - say array[2][3] = item?
BTW this is a DFSA table converted from a NFSA table
You could have a list of lists, essentially something like so:
List<List<String>> table = new ArrayList<List<String>>();
Then add the 26 columns:
for(int i = 0; i < 26; i++)
{
table.add(new ArrayList<String>());
}
You can then have a method, called, say, addToColumn(int column, String value, List<List<String>> table) in which you do something like so:
for(int i = 0; i < table.size(); i++)
{
if(i == column)
{
table.get(i).add(value);
}
else
{
table.get(i).add("");
}
}
This should allow you to have lists which grow together. Of course, my assumption in the above is that you will be entering one element at a time.
Alternatively, you can do something like so:
public void addToColumns(Map<int, String> data, List<List<String>> table)
{
for(int key : data.keyset())
{
table.get(key).add(data.get(key));
}
for(int i = 0; i < table.size(); i++)
{
if(!data.containsKey(i))
{
table.get(i).add("");
}
}
}
The above algorithm should allow you to add items to multiple columns, while filling the rest up with empty strings. This should allow you to end up with rows of equal length. Also, the map will be used to store a key-value pair where the key is the column number, and the value will be whatever string you would like to throw in there. This will allow you to populate your table one row at a time.
You can simply create an array by giving only the number of rows:
int[][] array = new int[4][];
Now you may treat your array as the transpose of what you have defined so if you want to enter an element at 3rd column of 2nd row you can enter as transpose i.e.
array[3][2]=5;

Delete data from ArrayList with a For-loop

I got a weird problem.
I thought this would cost me few minutes, but I am struggling for few hours now...
Here is what I got:
for (int i = 0; i < size; i++){
if (data.get(i).getCaption().contains("_Hardi")){
data.remove(i);
}
}
The data is the ArrayList.
In the ArrayList I got some strings (total 14 or so), and 9 of them, got the name _Hardi in it.
And with the code above I want to remove them.
If I replace data.remove(i); with a System.out.println then it prints out something 9 times, what is good, because _Hardi is in the ArrayList 9 times.
But when I use data.remove(i); then it doesn't remove all 9, but only a few.
I did some tests and I also saw this:
When I rename the Strings to:
Hardi1
Hardi2
Hardi3
Hardi4
Hardi5
Hardi6
Then it removes only the on-even numbers (1, 3, 5 and so on).
He is skipping 1 all the time, but can't figure out why.
How to fix this? Or maybe another way to remove them?
The Problem here is you are iterating from 0 to size and inside the loop you are deleting items. Deleting the items will reduce the size of the list which will fail when you try to access the indexes which are greater than the effective size(the size after the deleted items).
There are two approaches to do this.
Delete using iterator if you do not want to deal with index.
for (Iterator<Object> it = data.iterator(); it.hasNext();) {
if (it.next().getCaption().contains("_Hardi")) {
it.remove();
}
}
Else, delete from the end.
for (int i = size-1; i >= 0; i--){
if (data.get(i).getCaption().contains("_Hardi")){
data.remove(i);
}
}
You shouldn't remove items from a List while you iterate over it. Instead, use Iterator.remove() like:
for (Iterator<Object> it = list.iterator(); it.hasNext();) {
if ( condition is true ) {
it.remove();
}
}
Every time you remove an item, you are changing the index of the one in front of it (so when you delete list[1], list[2] becomes list[1], hence the skip.
Here's a really easy way around it: (count down instead of up)
for(int i = list.size() - 1; i>=0; i--)
{
if(condition...)
list.remove(i);
}
Its because when you remove an element from a list, the list's elements move up. So if you remove first element ie at index 0 the element at index 1 will be shifted to index 0 but your loop counter will keep increasing in every iteration. so instead you of getting the updated 0th index element you get 1st index element. So just decrease the counter by one everytime you remove an element from your list.
You can use the below code to make it work fine :
for (int i = 0; i < data.size(); i++){
if (data.get(i).getCaption().contains("_Hardi")){
data.remove(i);
i--;
}
}
It makes perfect sense if you think it through. Say you have a list [A, B, C]. The first pass through the loop, i == 0. You see element A and then remove it, so the list is now [B, C], with element 0 being B. Now you increment i at the end of the loop, so you're looking at list[1] which is C.
One solution is to decrement i whenever you remove an item, so that it "canceles out" the subsequent increment. A better solution, as matt b points out above, is to use an Iterator<T> which has a built-in remove() function.
Speaking generally, it's a good idea, when facing a problem like this, to bring out a piece of paper and pretend you're the computer -- go through each step of the loop, writing down all of the variables as you go. That would have made the "skipping" clear.
I don't understand why this solution is the best for most of the people.
for (Iterator<Object> it = data.iterator(); it.hasNext();) {
if (it.next().getCaption().contains("_Hardi")) {
it.remove();
}
}
Third argument is empty, because have been moved to next line. Moreover it.next() not only increment loop's variable but also is using to get data. For me use for loop is misleading. Why you don't using while?
Iterator<Object> it = data.iterator();
while (it.hasNext()) {
Object obj = it.next();
if (obj.getCaption().contains("_Hardi")) {
it.remove();
}
}
Because your index isn't good anymore once you delete a value
Moreover you won't be able to go to size since if you remove one element, the size as changed.
You may use an iterator to achieve that.
for (Iterator<Object> it = data.iterator(); it.hasNext();) {
if ( it.getCaption().contains("_Hardi")) {
it.remove(); // performance is low O(n)
}
}
If your remove operation is required much on list. Its better you use LinkedList which gives better performance Big O(1) (roughly).
Where in ArrayList performance is O(n) (roughly) . So impact is very high on remove operation.
It is late but it might work for someone.
Iterator<YourObject> itr = yourList.iterator();
// remove the objects from list
while (itr.hasNext())
{
YourObject object = itr.next();
if (Your Statement) // id == 0
{
itr.remove();
}
}
In addition to the existing answers, you can use a regular while loop with a conditional increment:
int i = 0;
while (i < data.size()) {
if (data.get(i).getCaption().contains("_Hardi"))
data.remove(i);
else i++;
}
Note that data.size() must be called every time in the loop condition, otherwise you'll end up with an IndexOutOfBoundsException, since every item removed alters your list's original size.
This happens because by deleting the elements you modify the index of an ArrayList.
import java.util.ArrayList;
public class IteratorSample {
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList<Integer> al = new ArrayList<Integer>();
al.add(1);
al.add(2);
al.add(3);
al.add(4);
System.out.println("before removal!!");
displayList(al);
for(int i = al.size()-1; i >= 0; i--){
if(al.get(i)==4){
al.remove(i);
}
}
System.out.println("after removal!!");
displayList(al);
}
private static void displayList(ArrayList<Integer> al) {
for(int a:al){
System.out.println(a);
}
}
}
output:
before removal!!
1
2
3
4
after removal!!
1
2
3
There is an easier way to solve this problem without creating a new iterator object. Here is the concept. Suppose your arrayList contains a list of names:
names = [James, Marshall, Susie, Audrey, Matt, Carl];
To remove everything from Susie forward, simply get the index of Susie and assign it to a new variable:
int location = names.indexOf(Susie);//index equals 2
Now that you have the index, tell java to count the number of times you want to remove values from the arrayList:
for (int i = 0; i < 3; i++) { //remove Susie through Carl
names.remove(names.get(location));//remove the value at index 2
}
Every time the loop value runs, the arrayList is reduced in length. Since you have set an index value and are counting the number of times to remove values, you're all set. Here is an example of output after each pass through:
[2]
names = [James, Marshall, Susie, Audrey, Matt, Carl];//first pass to get index and i = 0
[2]
names = [James, Marshall, Audrey, Matt, Carl];//after first pass arrayList decreased and Audrey is now at index 2 and i = 1
[2]
names = [James, Marshall, Matt, Carl];//Matt is now at index 2 and i = 2
[2]
names = [James, Marshall, Carl];//Carl is now at index 3 and i = 3
names = [James, Marshall,]; //for loop ends
Here is a snippet of what your final method may look like:
public void remove_user(String name) {
int location = names.indexOf(name); //assign the int value of name to location
if (names.remove(name)==true) {
for (int i = 0; i < 7; i++) {
names.remove(names.get(location));
}//end if
print(name + " is no longer in the Group.");
}//end method
This is a common problem while using Arraylists and it happens due to the fact that the length (size) of an Arraylist can change. While deleting, the size changes too; so after the first iteration, your code goes haywire. Best advice is either to use Iterator or to loop from the back, I'll recommend the backword loop though because I think it's less complex and it still works fine with numerous elements:
//Let's decrement!
for(int i = size-1; i >= 0; i--){
if (data.get(i).getCaption().contains("_Hardi")){
data.remove(i);
}
}
Still your old code, only looped differently!
I hope this helps...
Merry coding!!!

Categories