Removing Duplicate Integers in an ArrayList - java

I see a lot of posts with the same topic (mostly with Strings) but haven't found the answer to my question. How would I remove duplicate integers from an ArrayList?
import java.util.*;
public class ArrayList2 {
public static ArrayList<Integer> removeAllDuplicates(ArrayList<Integer> list) {
Collections.sort(list);
for (int i = 0; i < list.size(); i++) {
if (list.get(i) == list.get(i + 1)) {
list.remove(i);
}
}
return list;
}
}
This is the start of my code, the only problem that has arised is that if there are 3 integers with the same value, it only removes one of them. If I put in 4, it removes two of them. PLEASE NO HASHING!!!
The ArrayList and the output when I run it:
List: [-13, -13, -6, -3, 0, 1, 1, 1, 5, 7, 9]
Duplicates Removed: [-13, -6, -3, 0, 1, 1, 5, 7, 9]
This is my first time using this website, so please let me know if I'm doing something wrong with formatting/if there's already an answer to my question that I missed.

The specific reason why your removeAllDuplicates function doesn't work is that you are still iterating after a successful comparison. If you iterate only when list.get(i) != list.get(i + 1), you will get rid of all the duplicates.
public static ArrayList<Integer> removeAllDuplicates(ArrayList<Integer> list) {
Collections.sort(list);
int i = 0;
while(i < list.size() - 1) {
if (list.get(i) == list.get(i + 1)) {
list.remove(i);
} else {
i++;
}
}
return list;
}
It's worth noting that the above function is not as fast as it could be. Though the iteration runs quickly enough, the most significant step will be the sort operation (O(n log n)).
To avoid this extra time complexity, consider using a HashSet instead of an ArrayList (if it still fits within the constraints of your problem).

Other people have "answered" the basic issue, of the if statement skipping over elements because the for-loop is incrementing the index position, while the size of the array is shrinking.
This is just "another" possible solution. Personally, I don't like mutating Lists in loops and prefer to use iterators, something like...
Collections.sort(list);
Iterator<Integer> it = list.iterator();
Integer last = null;
while (it.hasNext()) {
Integer next = it.next();
if (next == last) {
it.remove();
}
last = next;
}
Still, I think some kind of Set would be a simpler and easier solution (and since you'd not have to sort the list, more efficient ;))

This assumes that you cannot use a set ("Please No hashing!!!") for a reason such as homework.
Look what happens when you remove a duplicate. Let's say that the dupe you're removing is the 1st of 3 consecutive equal numbers.
v
-13, -6, -3, 0, 1, 1, 1, 5, 7, 9
Here i is 4 to refer to the first of the 3 1 values. When you remove it, all subsequent elements get shifted down, so that the second 1 value now takes the place of the first 1 value at index i = 4. Then the current iteration ends, another one begins, and i is now 5.
v
-13, -6, -3, 0, 1, 1, 5, 7, 9
But now i is referring to the second of the two 1 values left, and the next element 5 isn't equal, so no dupe is found.
Every time you find a duplicate, you must decrease i by 1 so that you can stay on the first element that is a duplicate, to catch 3 or more duplicates in a row.
v
-13, -6, -3, 0, 1, 1, 5, 7, 9
Now consecutive elements still match, and another 1 value will get removed.

You'd want to have a double for loop as you dont want to check the only the next index but all indexes. As well as remember when you remove a value you want to check that removed value again as it could of been replaced with another duplicate value.

Related

Generating an array of non-repeating integers

I'm trying to generate an array of 5 non-repeating integers in Java, but there are still repeats when I run it. Here's my code so far:
public int[] generateCode(){
code[0] = (int)Math.round(Math.random()*8+1); // initialize first number so it won't be compared against
for(int i=1; i<code.length; i++){
code[i] = (int)Math.round(Math.random()*8)+1;
for(int j=0; j<i; j++){
while(code[i]==code[j]){
code[i] = (int)Math.round(Math.random()*8)+1;
}
} // end inner for loop
} // end outer for loop
return code;
} // end generateCode method
Any help is very much appreciated!
So, your for-loop is checking for repeated characters, BUT each time you generate a new value (in the while-loop) you're not checking to see if the value exits before j.
There are a number of possible ways you might address this issue, I prefer ones which uses Collections.shuffle, but assuming that you can't use features like Arrays, Collections, List, Set or possibly even streams, we need to work with what we have, arrays.
Since you have a small range of acceptable values which need to fit into an even smaller range, a simple solution might be to generate a "master" list of allowed values and randomly select a value from the master list, tracking which values you've already picked.
Sounds more complicated then it actually is, for example...
public int[] generateCode() {
int[] masterValues = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int[] codes = new int[5];
Random rnd = new Random();
int index = 0;
while (index < codes.length) {
int lookupIndex = 0;
do {
lookupIndex = rnd.nextInt(masterValues.length);
} while (masterValues[lookupIndex] == 0);
codes[index] = masterValues[lookupIndex];
masterValues[lookupIndex] = 0;
index++;
}
return codes;
}
This creates a "master" list of values. It then randomly calculates a "lookup" index, checks to see if the value in the master list is 0 or not, if not, it assigns it to the next index in the codes array and sets the value in the master list to 0, otherwise it generates a new random index and tries again. This all repeats till it fills the codes array
So doing something like...
System.out.println(Arrays.toString(generateCode()));
System.out.println(Arrays.toString(generateCode()));
System.out.println(Arrays.toString(generateCode()));
System.out.println(Arrays.toString(generateCode()));
could print (because it's "random")...
[8, 1, 4, 7, 5]
[9, 6, 2, 1, 8]
[6, 5, 9, 4, 7]
[2, 5, 3, 1, 4]
There are much easier ways to do this. Using a Set<Integer> would be one. Here is another.
List<Integer> list = new ArrayList<>(List.of(1,2,3,4,5,6,7,8));
Collections.shuffle(list);
System.out.println(list.subList(0,5));
prints something like
[4, 5, 8, 2, 1]
As was pointed out to me, you may not be allowed to use or know about collections. But I would recommend you at least make a helper method to check for duplicates to reduce the clutter. Something like the following:
public boolean contains(int[] arr, int n) {
for (int v : arr) {
if (v == n) {
return true;
}
}
return false;
}
Then you can keep checking the current value to be false before you add it. Once it works it also lets you focus on other aspects of your code.

Why is there a "0" value remaining in the list, despite code that removes "0" values?

I am working through sample questions for the Computer Science A exam and cannot figure out why the correct answer is correct on the following problem.
Consider the following method.
public static void mystery(List<Integer> nums)
{
for (int k = 0; k < nums.size(); k++)
{
if (nums.get(k).intValue() == 0)
{
nums.remove(k);
}
}
}
Assume that a List values initially contains the following Integer values.
[0, 0, 4, 2, 5, 0, 3, 0]
What will values contain as a result of executing mystery(values)?
The correct answer showing is : [0, 4, 2, 5, 3]
Why does the first 0 remain in the list?
The first 0 remains because that was really the second 0 in nums.
When k is 0, the test succeeds and remove(0) is called, removing the first index. That shifts all the other elements down, so that the list is now [0, 4, 2, 5, 0, 3, 0]. But then k is incremented to 1, so the second 0 (now at index 0) is skipped and not removed.
The other 0 values are removed successfully, just as the first 0 is removed successfully. This code skips only the second of two consecutive 0 values. This would be a bug, assuming that the code is supposed to remove all 0 values.
The answer already given by #rgettman is correct. However, I'd like to add that this is a particular example of a common issue - that of modifying a sequence (collection, whatever) while iterating (indexing, whatever) through it. You need to understand the effect that the modification will have on the in-progress iteration. And sometimes the effect is formally given as "undefined", i.e., you don't want to do it.
For Java, the Iterator<> class has a remove() method that safely removes the current element. If you actually wanted to remove all zeroes from your List<>, rather than just explaining what is happening in the test question, then Iterator would be one way to go about it.

arraylist is always created with a constant capacity

I am trying to compute pascal's triangle given a row number. I am using recursion.
My code is below:
public static List<Integer> getRow(int rowIndex) {
if (rowIndex == 1){
List <Integer> list = new ArrayList(rowIndex+1);
list.add(1);
return list;
}
else{
List<Integer> oldList = getRow(rowIndex -1);
List <Integer> list = new ArrayList(rowIndex+1);
int temp = 0;
list.add(0,1);
list.add(list.size()-1,1);
System.out.println("rowIndex "+rowIndex);
for (int i = 1; i < list.size()-1; i ++){
temp = oldList.get(i) + oldList.get(i-1);
list.add(i,temp);
}
return list;
}
}
It always returns [1,1] regardless of what row I am trying to get. I tried inserting print statements. I noticed the size of list is always 2 regardless of what rowIndex is.
List <Integer> list = new ArrayList(rowIndex+1);
Is the line above not the correct way to create an ArrayList? Seems like my arraylist always has size = 2;
you misunderstand how ArrayLists do work and you really should read the Javadoc.
In short, the constructor's parameter defines the initial size of the ArrayList in memory, not the max size. If you instantiate a new ArrayList<Integer>(2)it only means that the jvm allocates upfront enough space for two Integers, and that when you add a third element then the jvm will grow the size of the ArrayList in order to allow you to add more elements.
Further, you can access an ArrayList position with get() only if an element has been added at this position.
Finally, keep in mind that add at a specific position shifts right all elements. Thus if you add(10,1) then add(2,4), your first add will be shifted right.
Back to your question, if you absolutely want to use an ArrayList and not an array, you have to initialize your ArrayList with the right size, then set values at the right positions.
Here is a working solution :
// the method with your algorithm which has been slightly modified
public static List<Integer> getRow(final int rowIndex) {
// notice that I call a helper method which initialises correctly the ArrayList
final List<Integer> list = init(rowIndex);
if (rowIndex == 1) {
// notice that I set the value at a given position
// I can only do it because I initialised all values to 0 first
list.set(0, 1);
} else {
final List<Integer> previousRowList = getRow(rowIndex - 1);
// again, I set values...
list.set(0, 1);
list.set(rowIndex - 1, 1);
for (int i = 1; i < (list.size() - 1); i++) {
// set again...
list.set(i, previousRowList.get(i - 1) + previousRowList.get(i));
}
}
// lets print out the row
System.err.println(list);
// then return it
return list;
}
public static List<Integer> init(final int size) {
// passing the size is overkill, but well...
final List<Integer> list = new ArrayList<Integer>(size);
// fill the ArrayList with zeros
for (int i = 0; i < size; i++) {
list.add(i, 0);
}
// then return it
return list;
}
public static void main(final String[] args) {
getRow(Integer.parseInt(args[0]));
}
If you run it you'll get a (not so nice, but working) Pascal's triangle. Here follows the result if you want 11 rows :
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
[1, 7, 21, 35, 35, 21, 7, 1]
[1, 8, 28, 56, 70, 56, 28, 8, 1]
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
[1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1]
Hope it helps !
I think you're miss-interpreting the data structures.
And array list is a LIST implemented on top of an array. Setting the size of the array in the constructor is a way to give control to the developer for the initial size of the array (This is rarely necessary as the class manages the array size itself--so just leave out this argument). So the size of an array list is actually the size of the list, which is the number of elements, not the number of buckets in the underlying array which is specified in the constructor.
If you know the size of the array you want, and you want to get and add at specific locations, use a standard array, not an array list.
However, I think your code will work if you move
list.add(list.size()-1,1);
to after your for-loop (I'm actually surprised it doesn't throw an index out of bounds exception). And since you're going left to right, none of your adds need an index specified, since it'll just add it to the end of the existing list.

Removing pairs of elements in an Arraylist in Java

I know similar questions have been asked before, but they typically remove all of the specified element from an Arraylist.
What I need to do, is remove pairs of values from my ArrayList (if a number occurs 3 times, it will remove 2 copies of that element and leave 1 of them). More generally, if a number occurs an odd number of times leave 1, if a number occurs an even number of times remove them all. Thus, if my Arraylist was something like this
[5, 4, 3, 3, 2, 1, 2, 3, 1],
the output would be
[5, 4, 3]
Since the number "2" appears twice, it is complete removed. Same with the number "1". But since the number "3" appears three times, it leaves one of them in the ArrayList, because I only want to remove the numbers in pairs.
I don't care at all about the ordering, as I'm going to reorder it anyways.
Does anyone have any suggestions on doing this? I think I could use a for loop that compares each value over and over, but it seems inefficient, so I was curious if there would be a better way.
Thank you!
If you're allowed to sort the arraylist, this becomes pretty simple.
public void removePairedElements(ArrayList<Integer> a){
Collections.sort(a); //Sort a
int i = 0;
while(i < a.size() - 1){
//Check if i and i+1 are the same element. If so, remove both
if(a.get(i).equals(a.get(i+1))){
//Remove i twice - effectively removes i and i+1
a.remove(i);
a.remove(i);
//Move i *back* one index, which is equivalent to
//moving forward one because we just removed two elements.
//Prevent i from becoming negative though.
i = Math.max(0, (i - 1));
}
else{
i++;
}
}
}
O(n log n) time and O(1) space. Probably the cleanest answer. If you aren't allowed to change the ordering of the arraylist you'd have to do something else, but otherwise this should work.
You can use a Map as a helper.
iterate over the ArrayList
Lets say the i'th element of the list contains the number n.
If map.containsKey(n), map.get(n) is the location in the list of the previous occurrence of n.
remove both the i'th element and the map.get(n)'th element from the list.
remove the key n from the map.
Else
map.put (n,i)
Implementation note :
Don't use an enhanced for loop to iterate over the list (since it doesn't allow removal of elements). Iterate over the indices of the list. This will allow you to remove the elements by their index (but remember that when you remove the i'th element, the previous i+1 element becomes the new i'th element).
I think your best bet would be to sort the list.
Then, going backwards, remove ints if you see the same one while keeping track of the total count of current ints.
If you see an even total count, you will NOT remove the last occurrence.
If you see an odd total count, you will remove the last occurence.
public void removePairElementsFrom(Arraylist<Integer> myArrayList)
{
if (myArrayList == null)
{
return null;
}
int arrayLength = myArrayList.size();
if (arrayLegnth == 1)
{
return myArrayList;
)
Collections.sort(myArrayList);
int lastSeenInt = myArrayList.get(arrayLength - 1);
int lastSeenIntCount = 1;
for (int i = arrayLength - 2; i >= 0; --i)
{
if (myArrayList[i] == lastSeenInt)
{
myArrayList.remove(i+1);
++lastSeenIntCount;
}
else
{
if ((lastSeenIntCount % 2) == 0)
{
myArrayList.remove(i+1);
}
lastSeenInt = myArrayList.get(i);
lastSeenIntCount = 1;
}
}
if ((lastSeenIntCount % 2) == 0)
{
myArrayList.remove(0);
}
}
Enjoy :)
Write a method removeBadPairs that accepts an ArrayList of integers and removes any adjacent pair
of integers in the list if the left element of the pair is larger than the right element of the pair. Every pair's
left element is an even-numbered index in the list, and every pair's right element is an odd index in the list.
For example, suppose a variable called list stores the following element values:
[3, 7, 9, 2, 5, 5, 8, 5, 6, 3, 4, 7, 3, 1]
We can think of this list as a sequence of pairs: (3, 7), (9, 2), (5, 5), (8, 5), (6, 3), (4, 7), (3, 1). The pairs (9,
2), (8, 5), (6, 3), and (3, 1) are "bad" because the left element is larger than the right one, so these pairs
should be removed. So the call of removeBadPairs(list); would change the list to store the following
element values:
[3, 7, 5, 5, 4, 7]
If the list has an odd length, the last element is not part of a pair and is also considered "bad;" it should
therefore be removed by your method.
If an empty list is passed in, the list should still be empty at the end of the call. You may assume that the
list passed is not null. You may not use any other arrays, lists, or other data structures to help you solve
this problem, though you can create as many simple variables as you like.
You will get an error if you start from the start of the list using For loop due to an iterator. So I have an alternate way.
class RemoveBadPairs {
Public Static void main() {
ArrayList numbersList = new ArrayList();
numberslist={3,7,9,2,5,5,8,5,6,3,4,7,3,1,7};
System.out.println("Original List...");
for (e in : numbersList) {
System.out.print("{0},", e);
}
if (((numbersList.Count % 2)!= 0)) {
numbersList.RemoveAt((numbersList.Count - 1));
}
for (int i = (numbersList.Count - 1); (i <= 0); i = (i + -2)) {
if ((numbersList[i] < numbersList[(i - 1)])) {
numbersList.RemoveRange((i - 1), 2);
}
}
System.out.println("Final List...");
for (e in : numbersList) {
System.out.print(","+ e);
}
}
}
I hope this will work.

Any O(n) algorithm for smoosh method?

I am trying to finish a smoosh() method which will takes an array of ints. On completion the array should still contains the same numbers but wherever the array had two or more consecutive duplicate numbers, they are replaced by one copy of the number. Hence,
after smoosh() is done, no two consecutive numbers in the array are the same.
Any unused elements at the end of the array are set to -1.
For example, if the input array is
[ 1 1 0 0 4 4 5 0 0 0 7 ]
it reads
[ 1 0 4 5 0 7 ]
after smoosh() completes.
The method signature is:
public static void smoosh(int[] ints)
I was able to do it like this:
for (int i=0; i<ints.length-1; i++) {
if (ints[i+1]==ints[i])
ints[i]=-1;
}
for (int i=0; i<ints.length-1; i++) {
if (ints[i]==-1) {
for (int j=i+1; j<ints.length; j++) {
if (ints[j]!=-1) {
//swap ints[j] and ints[i] and then break;
}
}
}
}
However, this will be O(n2) time (although almost in place).
I feel like there should be some O(n) in place method to do this but I can't figure out how.
Could anyone think of any O(n) in place algorithm? (Obviously if you make another array of the same size to help processing then you can get O(n) easily but that's not what I am looking for since that's not in place...)
thanks!
Basically, as follows. This O(n)-time, O(1)-space "algorithm" is actually Python code, since that's a very good language for teaching basic algorithms, as long as you avoid all the complex stuff, like lambdas.
I'm actually using it to teach my 8yo son at the moment as he's expressed an interest in what I do all day at work.
array = [1, 1, 0, 0, 4, 4, 5, 0, 0, 0, 7]
print array
count = len (array)
last = array[0] - 1
toidx = 0
for fromidx in range (0, count):
if array[fromidx] != last:
array[toidx] = array[fromidx]
toidx = toidx + 1
last = array[fromidx]
while toidx < count:
array[toidx] = -1
toidx = toidx + 1
print array
The output of this is:
[1, 1, 0, 0, 4, 4, 5, 0, 0, 0, 7]
[1, 0, 4, 5, 0, 7, -1, -1, -1, -1, -1]
as your specification ask for.
It basically runs two indexes through the array, the fromix index advances by one no matter what. The toidx index only advances if the value at fromidx is different to the last one transferred. The initial value of the last one transferred is set to something different to the first element, so as to ensure the first element is transferred.
In other words, on each iteration where that condition is true, the value at the from index is copied to the toidx index, the toidx index is incremented, and the last value is updated. If the value at fromidx is the same as the last transferred, the toidx index is not updated.
Then, at the end, all the remaining values are set to -1.
Since your specs call for the rest of the array to be populated with -1, that's what I've done in the above code.
However, your sample result does not contain the negative values so, on the off chance that you need the array truncated rather than filled with -1, you basically replace the while loop at the end with an array truncation, so that its size is now toidx.
In Python, you could do that with something like:
array = array[0:toidx]
There's no need for your inner loop. You just have to keep track of what the last value you visited was, then start skipping until you find a 'new' number. e.g. in pseudo code
previous = null;
newarray = array();
newpos = 0;
for (i = 0; i < oldarray.length; i++) {
if (oldarray[i] == previous) {
continue; // got a duplicate value, so skip it.
} else {
newarray[newpos++] = oldarray[i];
previous = oldarray[i];
}
}
for (i = newpos; i < oldarray.length; i++) {
newarray[i] = -1; // fill in any empty slots
}
Now you're down to O(n).
If you use a LinkedList instead, you could use a ListIterator for the loop, storing the value of the previous value in the list and calling ListIterator.remove if it equals the current value.

Categories