问题
I am having trouble getting this thing to run. I'm not sure if what I have so far is on the right track. I'm not quite sure where is is giving me an out of bounds error.
Here are the instructions:
Write a method called interleave that accepts two ArrayLists of integers a1 and a2 as parameters and inserts the elements of a2 into a1 at alternating indexes. If the lists are of unequal length, the remaining elements of the longer list are left at the end of a1. For example, if a1 stores [10, 20, 30] and a2 stores [4, 5, 6, 7, 8], the call of interleave(a1, a2); should change a1 to store [10, 4, 20, 5, 30, 6, 7, 8]. If a1 had stored [10, 20, 30, 40, 50] and a2 had stored [6, 7, 8], the call of interleave(a1, a2); would change a1 to store [10, 6, 20, 7, 30, 8, 40, 50].
private static void interleave(ArrayList<Integer> a1,
ArrayList<Integer> a2) {
int i = a1.size();
int j = a2.size();
if (i < j) { // a1 is shorter than a2
for (int k = 0; k < a1.size(); k++) { // before k passes a1 size
a1.add(k+1, a2.get(k));
}
for (int l = a1.size(); l < a2.size(); l++) {
a1.add(a1.size(), a2.get(l));
}
} else if (i > j) { // a1 is longer than a2
for (int k = 1; k < a2.size(); k++) {
a1.add(k+1, a2.get(k));
}
} else { // they are equal length
for (int k = 1; k < a2.size(); k++) {
a1.add(k+1, a2.get(k));
}
}
}
回答1:
This should work
private static void interleave(ArrayList<Integer> a1, ArrayList<Integer> a2) {
int i = -1;
for(Integer elem: a2) {
if(i < a1.size()-1) {
i += 2;
} else {
i += 1;
}
a1.add(i, elem);
}
}
public static void main(String[] args) throws Exception {
ArrayList<Integer> a1 = new ArrayList<>(Arrays.asList(10, 20, 30));
ArrayList<Integer> a2 = new ArrayList<>(Arrays.asList(4, 5, 6, 7, 8));
interleave(a1, a2);
System.out.println(a1);
}
edit: I have to admit, that this code is actually a pretty bad solution, because it's gonna be very slow for long lists. Every time an element is added to a1, a big part of the list has to be shifted by one position. So following the advice from "MadProgrammer", here is a much better and much much faster way to do it
private static void interleave(ArrayList<Integer> a1, ArrayList<Integer> a2) {
ArrayList<Integer> r = new ArrayList<>(a1.size() + a2.size());
for(int i = 0, j = 0; i < a1.size() || j < a2.size(); i++, j++) {
if(i < a1.size()) r.add(a1.get(i));
if(j < a2.size()) r.add(a2.get(j));
}
a1.clear();
a1.addAll(r);
}
回答2:
I see that there's already any accepted answer, but I think that this is a case where using a for
loop with proper index variables will be more convenient than the for
loop that works on arrays and collections. For instance, look at what happens as this progress over time. a2
is always the same: [4 5 6 7 8]
, and the indices of its elements that should be inserted into a1
are 0, 1, 2, 3, 4
. Now, the first element (4
) should be inserted into a1
at position 1
. After that, the next element (5
) needs to be inserted at position 3
. Then 6
needs to be inserted at position 5
. In general, the i
th element of a2
needs to be inserted at position i*2+1
in a1
. This will hold until either i*2+1
is bigger than the number of elements in a1
, or you run out of elements in a2
. With this taken into consideration, the proper index to insert the i
th element of a2
into a1
, after all the earlier elements have been inserted, is Math.min( i*2+1, l1.size() )
, since adding at l1.size()
just adds an element to the end of the list.
I agree with the comment about not giving away homework answers, but since there's already an answer with a complete implementation, and I want to contrast the for
with an index to the for-each
loop, I'll include code here. The updated interleave
method is
public static <T> List<T> interleave( final List<T> l1, final List<T> l2 ) {
for ( int i = 0; i < l2.size(); i++ ) {
l1.add( Math.min( i*2+1, l1.size()), l2.get( i ));
}
return l1;
}
This isn't necessarily the most efficient implementation, though. It would make more sense to finish the for
loop when you get to the end of l1
and simply add the remaining elements all at once. I've also written this to take List
s, since that's all the code depends on, though the random accessing with get
will only be efficient on lists that support it efficiently (e.g., it will be fine for ArrayList
s, but will perform poorly for linked lists). The fact that these are just lists also suggests that you might consider a solution using ListIterators.
In context, and with a main
method that demonstrates it:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Interleave {
public static <T> List<T> interleave( final List<T> l1, final List<T> l2 ) {
for ( int i = 0; i < l2.size(); i++ ) {
l1.add( Math.min( i*2+1, l1.size()), l2.get( i ));
}
return l1;
}
public static void main(String[] args) {
final List<Integer> l1 = new ArrayList<Integer>( Arrays.asList( 10, 20, 30 ) );
final List<Integer> l2 = Arrays.asList( 4, 5, 6, 7 ,8 );
System.out.println( interleave( l1, l2 ));
}
}
After thinking about this some more, I think that there's something to be said for using a ListIterator
here. It makes some of the iteration and insertion code a bit cleaner, especially since insertion with a ListIterator
occurs before the iteration cursor. It also doesn't require any complicated index arithmetic. Here's an implementation that uses the ListIterators
:
public static <T> List<T> interleaveWithIterators( final List<T> l1, final List<T> l2 ) {
// Get an iterator for the l1, and position it after the first element
// or at the end, if there's no first element.
final ListIterator<T> it1 = l1.listIterator();
if ( it1.hasNext() ) { it1.next(); }
// Get an iterator for l2. While there are elements remaining in l2,
// keep adding them to l1 by calling it1.add(). While there are elements
// in l1, this also requires pushing it1 forward by one element on each iteration.
final ListIterator<T> it2 = l2.listIterator();
while ( it2.hasNext() ) {
it1.add( it2.next() );
if ( it1.hasNext() ) { it1.next(); }
}
return l1;
}
To avoid checking if ( it1.hasNext() ) { ... }
on each iteration, you could split up the while loop:
public static <T> List<T> interleaveWithIterators( final List<T> l1, final List<T> l2 ) {
final ListIterator<T> it1 = l1.listIterator();
if ( it1.hasNext() ) { it1.next(); }
final ListIterator<T> it2 = l2.listIterator();
while ( it2.hasNext() && it1.hasNext() ) {
it1.add( it2.next() );
it1.next();
}
while ( it2.hasNext() ) {
it1.add( it2.next() );
}
return l1;
}
回答3:
This worked for me.
private static void interleave(ArrayList<Integer>
a1, ArrayList<Integer> a2) {
int i = -1;
for(Integer elem: a2) {
if(i < a1.size()-1) {
i += 2;
} else {
i += 1;
}
a1.add(i, elem);
}
}
public static void main(String[] args) throws Exception {
ArrayList<Integer> a1 = new ArrayList<>(Arrays.asList(10, 20, 30));
ArrayList<Integer> a2 = new ArrayList<>(Arrays.asList(4, 5, 6, 7, 8));
interleave(a1, a2);
System.out.println(a1);
}
回答4:
public static LinkedList<Integer> alternate(LinkedList<Integer> list1, LinkedList<Integer> list2){
List<Integer> newList = new LinkedList<Integer>();
Iterator<Integer> itr1 = list1.iterator();
Iterator<Integer> itr2 = list2.iterator();
while (itr1.hasNext() || itr2.hasNext()){
if (itr1.hasNext()){
newList.add(itr1.next());
}
if (itr2.hasNext()){
newList.add(itr2.next());
}
}
return newList;
}
来源:https://stackoverflow.com/questions/19260820/alternating-between-two-arraylists