Any O(n) algorithm for smoosh method? - java

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.

Related

Getting elements from an array where next element greater than previous by 1

I have an array:
int[] arr = {-4, -2, -1, 0, 1, 3, 4, 5, 6, 9, 10, 12, 13, 14, 18};
The array is ordered in ascending order.
The main idea is to get elements and group them, where next element is greater than a previous by 1. The minimum length of such elements should be equal 3.
In output, I have to get a string:
"-4, (-2-1), (3-6), 9, 10, (12-14), 18"
The first interval is:
-2, -1, 0, 1 - it should looks like range i.e -2-1
The next interval is:
3, 4, 5, 6 - it should looks like range i.e 3-6
9, 10 - the length is less than 3
So the last interval is:
12, 13, 14 it should looks like range i.e 12-14
this answer was pre-edit.
You are printing input[i] but you are expecting the values in input[i] - 1
when you run this in the debugger you can see it finds -1 (which has -2 before it, and equals -2 + 1). You then print the -1 you found.
If you want the output to contain the -2 , you should print input[i - 1]
post-edit:
you have grouped elements by braces which means you are looking for a sequence. This will require you to keep the start number somewhere. the code you originally posted (simple loop that checks left) will need a bit more work.
In rough terms:
if (continuing a range)
if(this number is still in range / +1) continue
else: stop the range and print it (start point plus current -1). if current -1 is the start point, don't use braces and range indicator. if it is different use braces and - between start and end)
else
start a range. (keep track of the number you are on and continue.
that's what you need in pseudocode
Algorithm
You could loop through the array.
If found consecutive number, add it to a group (e.g. List).
If group satisfies minimum length (e.g. 3) then add it to output formatted as range (and clear the group).
If no consecutive number, just add it to output.
Code
public static String formatAsRange(List<Integer> list) {
return String.format("(%d-%d)", list.get(0), list.get(list.size()-1));
}
public static String consecutiveElements(int[] array, int minGroupLength) {
StringBuilder sb = new StringBuilder();
List<Integer> group = new ArrayList<>();
for(int i = 0; i < array.length; i++) {
if (i == 0 || array[i] == array[i-1] + 1) {
group.add(array[i]);
} else {
if (group.size() >= minGroupLength) {
sb.append(formatAsRange(group)).append(',');
} else {
var csv = group.stream().map(String::valueOf).collect(Collectors.joining(","));
sb.append(csv).append(',');
}
group.clear();
sb.append(array[i]).append(',');
}
}
return sb.toString();
}
For your input it prints:
-4,-2,(-1-1),3,(4-6),9,10,12,13,14,18,
See the demo on IDEone.
To do
So there is still something to fix. For example
expected range 12-14 is missing
expected range -2-1 is not complete with -2, (-1-1)
expected range 3-6 is not complete with 3, (4-6)
the last comma can be removed
can add a space following each comma

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.

Circular Array Loop, detection

I am working on a problem, and have spent some time on it.
Problem statement:
You are given an array of positive and negative integers. If a number n at an index is positive, then move forward n steps. Conversely, if it's negative (-n), move backward n steps. Assume the first element of the array is forward next to the last element, and the last element is backward next to the first element. Determine if there is a loop in this array. A loop starts and ends at a particular index with more than 1 element along the loop. The loop must be "forward" or "backward'.
Example 1: Given the array [2, -1, 1, 2, 2], there is a loop, from index 0 -> 2 -> 3 -> 0.
Example 2: Given the array [-1, 2], there is no loop.
Note: The given array is guaranteed to contain no element "0".
Can you do it in O(n) time complexity and O(1) space complexity?
And this is my solution in progress, however, I am not sure how should I end the do-while condition, when there is no loop detected. I believe my code will run infinitely if there is no loop detected.
public static boolean circularArrayLoop(int[] nums) {
int size = nums.length;
if(size < 2) return false;
int loopStart = nums[0];
int index = 0;
int start = nums[0];
do{
if(nums[index] > 0){
index = moveForward(index, nums[index], size);
}else {
index = moveBackward(index, Math.abs(nums[index]), size);
}
}while (loopStart != nums[index]);
}
This can be seen as a version of cycle detection in a directed (possibly disconnected) graph or more like finding a minimum spanning trees for all the connected subgraphs in the given graph. The numbers in the array are vertices and an edge will be formed between the vertices based on the vertice value. There are no known graph parsing algorithms which can possibly solve it in O(1) space complexity. This might be solved in O(n) time complexity as the best graph parsing algorithms can be solved in O(V+E) time and V=E in this case which makes it possible to solve with O(n) time complexity in some cases. The best-known algorithm is Kruskal's: http://www.geeksforgeeks.org/greedy-algorithms-set-2-kruskals-minimum-spanning-tree-mst/ which solves in O(nlogn) time.
Since there are guaranteed no elements with value 0, there is always going to be a loop. The qualifier is loops must be greater than a single element long.
With this condition, when advancing to the next index as directed by the array element value results in the same index being reached, "no" loop is present.
The fast and slow moving cursors can be used to find the beginning of the loop. Then advancing a single cursor until it returns to the same index would let you iterate over the elements of the loop. If a single advancement returns the cursor to the same index no loop is present.
public static void main(String[] args) {
int[] loop = {2, -1, 1, 2, 2};
int[] noloop = {-1, 2};
System.out.println(circularArrayLoop(loop));
System.out.println(circularArrayLoop(noloop));
}
static int nextIndex(int[] nums, int cur) {
// Get next index based on value taking into account wrapping around
}
static boolean circularArrayLoop(int[] nums) {
int fast = 0;
int slow = 0;
do {
// advance fast cursor twice
// advance slow cursor once
} while (fast != slow);
int next = nextIndex(nums, fast);
// return if the loop has more than a single element
}
Am I wrong to think there is no guarantee that the loop will go on with the first element ? Thus, you can't just do int loopStart = nums[0];
What if your example 1 was rather [2, -1, 1, 4, 2], then the loop would be from index 0 -> 2 -> 3 -> 2. And, your check with loopstart wouldn't work, since it checks sums[0].
A good solution is to use 2 variables and move them at different speed (one twice the speed). If the array/linked list is circular, you'll get to a point where var1 equals var2.
Here's the pseudocode:
if array.length<=1
return false
int i=0;
//loop is when "var1 == var2"
//end is when "var1 == abs(array.length)"
loop (until var1 == var2 or var1 reaches the end)
var1 = moveToNext(var1)
if (i++ % 2 == 0)
var2 = moveToNext(var2)
return var1 == var2;
This is quite similar to a question generally asked using linked list: How to detect a loop in a linked list?

Remove all elements in one list and add to another list - Java

So I'm trying to remove all cards from a player's 'Rack' (an ArrayList) and put it in the discard pile (Stack), one by one. So far I have the following code which I've come to realize will stop once it hits 5. (Note: each players rack has 10 cards).
int rackSize = player.getPlayerRack().getRack().size(); // rackSize = 10
for (int i = 0; i < rackSize; i++) {
getDeck().getDiscardPile().add(player.getPlayerRack().getRack().remove(i));
}
My question is how do I remove all items in the players 'Rack' so the rackSize = 0, and add all of them to the discard pile?
Terribly sorry if this is confusing. You can generalize this by saying there are 10 integers in an ArrayList<Integer> hand, so: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]. How would you remove all items from this list and add them to a Stack<Integer> discardPile?
What's happening is as you're removing the elements from Rack, you're still incrementing with i++, meaning that the new value at the old index i is still left behind. What you'll want to do is change your code to this:
int rackSize = player.getPlayerRack().getRack().size(); // rackSize = 10
for (int i = 0; i < rackSize; i++) {
getDeck().getDiscardPile().add(player.getPlayerRack().getRack().remove(0));
}
This way, you're always reaching into the first new element in the Rack until it's empty, assuming Rack is not repopulated while this code is executing.
Use 0 instead of i to access the ArrayList.
When you remove an element, it is no longer in the ArrayList so you have to keep this is mind - further references through the array index should be decremented 1 in each iteration. As a result you probably want to access the first item every time.

JAVA Arrays - Need somebody to explain what this code does?

int temp;
for (int i = 0; i < numbers.length/2; i++) {
temp = numbers[i];
numbers[i] = numbers[numbers.length - 1 - i];
numbers[numbers.length - 1 - i] = temp;
So this is code in one of my labs but I don't understand it.
So first you declare int temp, but why? Shouldn't you declare a temp int array?
Then the second line is a loop that goes through the index and you /2 because you don't actually need to go through all values if you switch half, you switched all of them.
I don't understand the next two lines at all.
Thanks in advance.
Carefully step through the inside of the loop a couple times:
temp = numbers[i];
numbers[i] = numbers[numbers.length - 1 - i];
numbers[numbers.length - 1 - i] = temp
On your first pass i is 0
so start with an array like this:
[1,2,3,4,5]
if temp=numbers[0] then temp will be 1. numbers.length will be 5, so numbers.length-1-i will be 4. numbers[4] is 5. after you have done each line, repeat with i=1 then i=2. Examine your array when you are done (it will change).
just keep on going like that. It's better to figure it out for yourself than to be handed answers--much more fun :)
(1) You declare int temp to store the value of variables that are being swapped in the array, so when you overwrite one of the array elements, you still have the variable value. You don't need to use an entire array for this.
(2) The code you don't understand swaps the array values at numbers[i] and numbers[numbers.length - 1 - i], using temp as a placeholder.
All this code does is reverse an array. It goes all the way to array.length/2 (other wise if it went to length it would swap, then swap back doing nothing).
The temp doesn't need to be an array because it's just a variable to hold on to old values while the swap is happening.
The final two lines of the code is where the swap actually happens.
So if you provide input of {1, 2, 3, 4, 5} it will output {5, 4, 3, 2, 1}.
If you look at when i =0:
numbers[0] = 1, numbers[4] = 5,
temp will become 1, then numbers[0] will become 5, then numbers[4] will become the value of temp (1).
So after that first iteration of the loop, your array will look like this:
{5, 2, 3, 4, 1}
Don't worry too much about the declaration. Let's focus on the lines you don't understand at all. The three lines inside the loop are as follows:
temp = numbers[i];
This puts the value of the ith element of the array into temp (so we can get to it later).
numbers[i] = numbers[numbers.length - 1 - i];
This puts the value of the ith element from the end into the ith element.
numbers[numbers.length - 1 - i] = temp;
This puts the original value of the ith element into the ith element from the end.
That is, we iterate over the first half of the array, swapping elements with the opposite elements from the second half of the array. In other words, we are reversing the array.

Categories