Implement a data type that supports insert an item, delete the item added least recently, and delete a random item. Each operation should take constant expected amortized time per operation and should use space (at most) proportional to the number of items in the data structure.
eg. 1 2 3 4 5 6 -> 1 2 4 5 6
I have already implemented the queue as below, but now I do not know how the delete a random item with amortized time, should I rearrange the array every time when there is a random remove like moving number after the random removed number one slot forward in array? is it a really bad practice? or should I implement the Queue using linked list? but my gut feeling tells me linked list also need average O(n) to reach the random node from the head of the linked list.
public class RandomQueue<Item> implements Iterable<Item> {
Item[] items;
int N;
int first;
int last;
public RandomQueue() {
items = (Item[]) new Object[2];
N = 0;
first = last = 0;
}
public static void main(String[] args) {
RandomQueue<Integer> rd = new RandomQueue<>();
}
public boolean isEmpty() {
return N == 0;
}
public String toString() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < N; i++) {
sb.append(items[(first + i) % items.length]).append(" ");
}
return sb.toString();
}
private void resize(int length) {
Item[] temp = (Item[]) new Object[length];
for (int i = 0; i < N; i++) {
temp[i] = items[(first + i) % items.length];
}
items = temp;
first = 0;
last = N;
}
public void enqueue(Item item) {
if (N == items.length)
resize(items.length * 2);
items[last++] = item;
if (last == items.length)
last = 0;
N++;
}
public Item dequeue() {
if (isEmpty())
throw new NoSuchElementException("Queue is empty");
Item first = items[first];
items[first] = null;
N--;
if (first == items.length)
first = 0;
else
first++;
if (N > 0 && N == items.length / 4)
resize(items.length / 2);
return first;
}
}
The key to this problem is that you are not removing a random item from the queue you are creating a queue that excludes a random item. You should have a function in your program that accepts a queue as input and does the following:
Create a second queue that will exclude the random deleted item.
Generate a random number less than or equal to the total length of the first queue.
Create a loop that will remove items from the first queue and add them to the second queue.
If the counter in your loop equals the random number, do not add it to the second queue.
Delete the first queue and return the second queue.
Related
I'm attempting a problem from Pat Morin's Open Data Structures textbook, based on his ArrayQueue implementation:
https://opendatastructures.org/ods-java/2_3_ArrayQueue_Array_Based_.html
"Exercise 2..3 Design and implement a RandomQueue. This is an implementation of the Queue interface in which the remove() operation removes an element that is chosen uniformly at random among all the elements currently in the queue. (Think of a RandomQueue as a bag in which we can add elements or reach in and blindly remove some random element.) The add(x) and $ remove() operations in a RandomQueue should run in constant time per operation."
His code for remove():
T remove() {
if (n == 0) throw new NoSuchElementException();
T x = a[j];
j = (j + 1) % a.length;
n--;
if (a.length >= 3*n) resize();
return x;
}
Logically, what I'm trying to do is:
Swap a random element and the next element.
Return the next element (now swapped with another random element).
I must be missing something, because performing this operation returns some elements multiple times, and never returns others. My code (renamed variables for clarity):
public T remove() {
if (count == 0) throw new NoSuchElementException();
Random random = new Random();
int randIndex = random.nextInt(count);
T objTemp = array[nextElem];
array[nextElem] = array[randIndex];
array[randIndex] = objTemp;
T obj = array[nextElem];
// Increment next element:
nextElem = (nextElem + 1) % array.length;
// Decrement element count:
count--;
if (array.length >= (3 * count))
resize();
return obj;
}
What am I doing wrong, here? Why am I getting some elements multiple times, and others not at all?
(Note: This is for an online university course.)
Credit to Andreas for pointing out my silly mistake.
I needed to get my index based on the index of the head (of course!). This code works:
public T remove() {
if (count == 0) throw new NoSuchElementException();
Random random = new Random();
int randIndex = random.nextInt(count);
int safeRandomIndex = (headIndex + randIndex) % array.length;
// Swap that randomly chosen element with our nextElem:
T objTemp = array[headIndex];
array[headIndex] = array[safeRandomIndex];
array[safeRandomIndex] = objTemp;
T obj = array[headIndex];
headIndex = (headIndex + 1) % array.length;
count--;
if (array.length >= (3 * count))
resize();
return obj;
}
My dequeue method currently does not delete the item I wish for it too, instead it deletes the last element from the collection. For example,
If I add in the elements: 1, 2, 3
My toString method will return 1, 2, 3 as expected.
Then when I use my driver to call dequeue, it should dequeue the 0th element, 1 in this case.
Although, the method says "Removed element 1 from the queue" prompting that the T result variable has embodied the correct value, although the method does not work as expected, as when calling the toString method after to print the contents, it will print: 1, 2
Then when I call the enqueue method again, on the same queue, if I enqueue the String 3, it will print this as the new queue: 3, 2, 3
I am confused as to where my logic error is, as I assume it is with the extreme case of the collection being filled, but I still run into errors with my dequeue method when the collection is not at max. I attached my code below.
public class QueueRA<T> implements QueueInterface<T> {
protected T[] items;
protected int front, back, numItems;
#SuppressWarnings("unchecked")
public QueueRA() {
front = 0;
numItems = 0;
back = 0;
items = (T[]) new Object[3];
}
#Override
public boolean isEmpty() {
return numItems == 0;
}
#Override
public void enqueue(T newItem) throws QueueException {
if(numItems == items.length) {
resize();
enqueue(newItem);
}
else {
items[back] = newItem;
back = (back + 1) % items.length;
numItems++;
}
}
#Override
public T dequeue() throws QueueException {
T result;
if(numItems != 0) {
result = items[front];
items[front] = null;
front = (front + 1) % items.length;
numItems--;
}
else {
throw new QueueException("The queue does not contain any elements.");
}
return result;
}
#SuppressWarnings("unchecked")
#Override
public void dequeueAll() {
back = 0;
front = 0;
numItems = 0;
items = (T[]) new Object[3];
}
#Override
public T peek() throws QueueException {
T result;
if(numItems != 0) {
result = items[front];
}
else {
throw new QueueException("The queue does not contain any elements.");
}
return result;
}
/**
*
*/
#SuppressWarnings("unchecked")
protected void resize() {
T[] newItems = (T[]) new Object[numItems+4];
for(int i = 0; i < numItems; i++) {
newItems[i] = items[i];
}
this.front = 0;
this.back = numItems;
this.items = newItems;
}
/**
*
*/
public String toString() {
String toReturn = "";
for(int i = 0; i < numItems; i++) {
if( (i+1) == numItems) {
toReturn = toReturn.concat(items[i] + " ");
}
else {
toReturn = toReturn.concat(items[i] + ", ");
}
}
return toReturn;
}
}
Your toString() and resize() methods are wrong.
In your example code, you created an arraySize of 3 initially and then added 1, 2, and 3. This occupies all the 3 slots in the array and your numItems is set to 3. The front is set to 0 and back is also set to 0 because after adding 3, your algorithm is:
back = (back+1)%items.length;
back was initially 2. Now it is calculated as:
-> (2+1)%3
-> 3%3
-> 0
So your back is 0 at this moment.
Now, when you call dequeue(), the front pops 1 out. So now your array looks like this:
items[0] -> empty
items[1] -> 2
items[2] -> 3
back = 0
front = 1
So, when you enque next and add 3, your enqueue() method checks that number of items in the system is less than size of the array (2 < 3) and adds 3 to the location pointed by back (which is 0 now) and increments it to 1. So, your array looks like this now:
items[0] -> 3
items[1] -> 2
items[2] -> 3
back = 1
front = 1
Now, in your toString() method, without considering the values of front and back, you start from 0 to end:
for(int i = 0; i < numItems; i++) {
.
.
.
}
What you need to be doing is start at i = front. if front < back, that means that you travel lineraly from "front" to "back" and print everything. If front > back then you do two loops:
for(int i=front; i < arr.length; i++) {
// Code to print arr[i]
}
for(int i=0; i < back; i++) {
// Code to print arr[i]
}
This way, it will print from front to end and then from 0 to back.
Also, your resize method is wrong for the same reason. You cannot copy from 0 to numItems, you need to start the copy at "front" and then progress like how I wrote for the toString(). Doing this will ensure that your queue's order is preserved across multiple resizes.
Adding to #Arun Subramanian's answer.
Your resize() method was simply copying all of the elements sequentially from items to newItems, then setting front = 0 and back = numItems. Which would have been fine if you had implemented the queue sequentially. However, your implementation of a queue is not sequential, it's a circular / ring implementation. Therefor you have to copy the elements in a circular way.
One way to do this is mentioned in #ArunSubramanian's answer, "If front < back, that means that you travel linearly from front to back and print everything. If front > back then you do two loops." Another way is to use a do-while loop with modular arithmetic, as in the following:
#SuppressWarnings("unchecked")
protected void resize() {
T[] newItems = (T[]) new Object[numItems + 4];
int i = 0; // index used to copy items to newItems
// do-while used in case the queue is full (i.e. front == back)
do {
newItems[i] = items[front];
// modular arithmetic used to circle back on items
front = (front + 1) % items.length;
i += 1;
} while(front != back);
this.front = 0;
this.back = numItems;
this.items = newItems;
}
Similarly your toString() method can be implemented in the following way:
#Override
public String toString() {
String toReturn = "";
// need this check, otherwise do-while will illegally access items[0]
if(!isEmpty()) {
int len = items.length;
int i = front;
// do-while used in case front == back
do {
String delim = ((i+1) % len == back) ? " " : ", ";
toReturn += items[i] + delim;
i = (i+1) % len; // modular arithmetic used to circle back
} while(i != back);
}
return toReturn;
}
I have implemented a function which removes an element from an array list. I should not the ArrayList libraries! See my code below:
/**
* removes a LendItem at a specified (index) position.
* This functions returns the item removed from the list or null if no such item exists. This
* function leaves no gaps, that means all items after the removed item are shifted one position.
* #param list is the item to be removed
* #param n is the index of the item to be removed
* #return the removed item
*/
public static LendItem remove(LendItemArrayList list, int n) {
if (list.next == 0) {
return null;
}
if (n < 0 || n > list.INITIAL_SIZE) {
return null;
}
LendItem itemToBeRemoved = list.lendItems[n]; // itemToBeRemoved is the item which has the index n, which we want to remove from the list.
int i;
for (i = n; i < list.next - 1; i++) { // iterate through the list, starting where the index of the itemToBeRemoved is.
list.lendItems[i] = list.lendItems[i + 1];
}
list.lendItems[i] = null;
list.next--;
return itemToBeRemoved;
}
and here is the class :
public class LendItemArrayList {
int INITIAL_SIZE = 20;
boolean resizeable = false;
LendItem[] lendItems = new LendItem[INITIAL_SIZE];
int next = 0;
}
I have tested my functions with a few test methods which have been provided, and i am only failing one of them. Specifically the test is called:
removeNonExistingElement
and it fails like this:
java.lang.AssertionError: 10 elements have been added, next should be 10 (no changes) but found 9.
EDIT:
Added the add() function.
public static boolean add(LendItemArrayList list, LendItem p) {
if (list.next == list.lendItems.length) {
if (list.resizeable == false) {
return false;
}
}
if (list.next == list.lendItems.length) {
if (list.resizeable == true) {
LendItem[] resizedList = new LendItem[list.lendItems.length*2];
for (int i = 0; i < list.next; i++) {
resizedList[i] = list.lendItems[i];
}
list.lendItems = resizedList;
}
}
list.lendItems[list.next++] = p;
return true;
}
Leave this as it is, as it checks if the indices are out of range.
if (n < 0 || n >= list.INITIAL_SIZE) {
return null;
}
Next, add this line of code:
if (list.lendItems[n] == null) {
return null;
}
Afterwards, you may or may not add the if statement which checks if the given list is empty. It makes no difference unless it is required to be used.
if (list.next == 0){
return null;
}
Change this line:
if (n < 0 || n > list.INITIAL_SIZE) {
To this:
if (n < 0 || n >= list.INITIAL_SIZE) {
>= means greater or equal. If n == list.INITIAL_SIZE, then that item can't be removed either, because since indices start with 0, the last value in a list has an index of size - 1. It's one of those things that hurt your brain when you start programming.
I am working on a project in which I have to sort an array of Integer objects by using Comparable.
My add method takes an item of type E. If my size variable (which tracks the elements in my array theData[]) is = 0 (which it is initialized to), I simply put the item in theData[0].
If it is not, I use item.compareTo to compare the item against each item already in the array. If the result of compareTo is < 0 for a number in the array, I shift everything at that number and after to the right, and insert the item before it.
If compareTo returns a 0, meaning the item is equal to the number in the array, I do nothing as I don't want duplicates in the array.
If none of the compareTo statements in the loop return a -1 or a 0, I put the item in theData[size], the end of the array, as it must be larger than all the other numbers.
However, this doesn't work. Any time I make a new Set and add a few numbers to it, then try to print out the contents of my set using a for loop,I keep getting a java.lang.ArrayIndexOutOfBoundsException: 10 error for this line:
theData[j + 1] = theData[j];
I've tried starting from scratch and re-writing my loop with different logic, and each time I keep hitting this wall. I know I must either be shifting incorrectly or not increasing the size of the array correctly with my reallocate method, but I can't wrap my head around it.
import java.util.*;
public class Set<E extends Comparable<E>> {
String s;
String name;
private static final int INITIAL_CAPACITY = 10;
private E[] theData;
private int size = 0;
private int capacity = INITIAL_CAPACITY;
#SuppressWarnings("unchecked")
public Set() {
theData = (E[]) new Comparable[capacity];
}
public Set(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void add(E item) {
if (size == capacity) {
reallocate();
}
if (size == 0) { // If size is 0, add item to theData[0]
theData[size] = item;
size++;
return;
}
else { // Else compare the item to every item in loop.
for (int i = 0; i < size; i++) {
int result = item.compareTo(theData[i]);
if (result < 0) {
for (int j = 0; j < size; j++) { //If item is less than a number, shift everything
theData[j + 1] = theData[j]; //after that index to the right, and add item
theData[j] = item;
}
}
if (result == 0) {
return;
}
else { //If item is not less than or equal to any
theData[size] = item; //numbers in the array, add it to the end
size++;
}
}
}
}
/*
* if (size>=1){ int result = item.compareTo(theData[size-1]); if(result<0){
* E temp = theData[size-1]; theData[size-1] = item; theData[size] = temp; }
* if(result>1){ return; } }
*/
public E get(int index) {
if (index < 0 || index >= size) {
throw new ArrayIndexOutOfBoundsException(index);
}
return theData[index];
}
public int size() {
return size;
}
private void reallocate() {
capacity = 2 * capacity;
theData = Arrays.copyOf(theData, capacity);
}
}
Edit: The driver method I'm using to test it -
public class Driver {
String one = "two";
public static void main(String[] args){
Set<Integer> one = new Set<Integer>();
one.add(63);
one.add(20);
one.add(127);
one.add(10);
one.add(26);
one.add(15);
for(int i = 0; i < one.size(); i++){
System.out.println(one.get(i));
}
}
}
When j == size - 1, theData[j+1] will take you out of the array.
You want to loop to one before the end instead.
for (int j = 0; j < size - 1; j++) { //If item is less than a number, shift everything
theData[j + 1] = theData[j]; //after that index to the right, and add item
theData[j] = item;
}
So I've also taken a look at the logic you've got for the insertion, and it doesn't make a lick of sense. Why do you delay the insertion at all? If you've got the room, just add it!
Next, the double loops are essentially implementing bubble sort, but there's a fatal flaw with it: you don't ever complete the swap; you only overwrite your values repeatedly. You're also not comparing in the right direction; you want to swap if the value on the left is larger than the value on the right, since you're starting from the beginning of the array.
So, with that...this is what an implementation would have the form of...
public void add(E item) {
if (size == capacity) {
reallocate();
}
theData[size++] = item;
for (int i = 0; i < size - 1; i++) {
for (int j = 0; j < size - 1; j++) {
if (theData[j].compareTo(theData[j + 1]) > 0) {
// perform the swap (you need an extra variable!
}
}
}
}
I leave implementing the swap as an exercise for the reader.
First, in your shift loop, you are inserting the new item in every position instead of shifting then inserting in [i] because you copy theData[j] to the next position, but always assign item to theData[j], is that right?
Second, you are starting from the beginning of array since j starts with 0. J should start with i.
Third and main bug, you verify if result < 0 then you verify IF result == 0, change for a ELSE IF so the else don't get executed even when result < 0
shift elements to right can be done from right to left, like:
for (int j = size; j > i; j--) { // If item is less than a
// number, shift
// everything
theData[j] = theData[j - 1]; // after that index to the
// right, and add item
}
size++;
theData[i] = item;
break;// after insert the number, we can just break the for loop
once the new number is inserted, break the for loop, else, the size variable will not be correct
else { // If item is not less than or equal to any
theData[size] = item; // numbers in the array, add it to the end
size++;
break;
}
Trying to use Arrays.binarySearch() to search for a string in an array and return the index. However each time I call Arrays.binarySearch() I get the following exception -
Exception in thread "main" java.lang.NullPointerException
at java.util.Arrays.binarySearch0(Unknown Source)
at java.util.Arrays.binarySearch(Unknown Source)
at project.ArrayDirectory.lookupNumber(ArrayDirectory.java:97)
at project.test.main(test.java:12)
Here is my ArrayDirectory class -
public class ArrayDirectory implements Directory {
static Entry[] directory = new Entry[50];
#Override
public void addEntry(String surname, String initials, int extension) {
int n = 0;
for (int i = 0; i < directory.length; i++) { // counting number of
// entries in array
if (directory[i] != null) {
n++;
}
}
if (n == directory.length) {
Entry[] temp = new Entry[directory.length * 2]; // if array is full
// double the
// length.
for (int i = 0; i < directory.length; i++)
temp[i] = directory[i];
directory = temp;
}
int position = -1;
for (int i = 0; i < directory.length; i++) {
position = i;
if (directory[i] != null) { // sorting the array into alphabetical
// order by surname.
int y = directory[i].getSurname().compareTo(surname);
if (y > 0) {
break;
}
}
else if (directory[i] == null) {
break;
}
}
System.arraycopy(directory, position, directory, position + 1,
directory.length - position - 1);
directory[position] = new Entry(initials, surname, extension); // placing
// new
// entry
// in
// correct
// position.
}
#Override
public int lookupNumber(String surname, String initials) {
// TODO Auto-generated method stub
Entry lookup = new Entry(surname, initials);
int index = Arrays.binarySearch(directory, lookup);
return index;
}
}
Any ideas how I use binary search to find the correct index?
Thank you for you help.
edit -
I have also overridden comapreToin my Entry class -
public int compareTo(Entry other) {
return this.surname.compareTo(other.getSurname());
}
In your invocation of
int index = Arrays.binarySearch(directory,lookup);
directory seems to contain only null elements. Check that you are initializing elements correctly.
I note two things:
static Entry [] directory = new Entry [1];
First, that code allocates space for one Entry in the array. It doesn't actually instantiate an Entry. That is, directory[0] is null. Secondly, a binary-search on an array with one entry is crazy. There is only one element. It must be directory[0]. Finally, you should sort your array to do a binary search on it.
The basic concept behind a binary search is the recursion of the following steps(Note the search assumes the list or array of elements is sorted in some form and the element exists there.):
Go to the middle element of the array.
check if the searched element is equal to the element at the middle. If it is then return its index.
if not then check if the searched element is 'smaller' or 'larger' than the element in the middle.
if it is smaller then go to step 1 using only the lower/first half of the array instead of the whole.
else go to step 1 using only the upper/last half of the array instead of the whole.
As the array is continuously divided in 2 it will eventually reach the size of 1 giving the result.
Now, suppose you are looking for an integer in an int array. Here is what the code would be like:
public static int binarySearch(int number, int[] array)
{
boolean isHere = false;
Integer index =0;
for(int i=0;i<array.length;i++)
{
if(array[i] == number)
{
isHere = true;
i = array.length;
}
}
if(!isHere)
{
index = -1;
}
else
{
int arrayStart = 0;
int arrayEnd = array.length;
index = binarySearch(number, arrayStart, arrayEnd, array);
}
return index;
}
private static int binarySearch(int number, int start, int end, int[] array)
{
// this formula ensures the index number will be preserved even if
// the array is divided later.
int middle = (start+ end)/ 2;
if(array[middle] == number)
{
return middle;
}
else
{
if(number < array[middle])
{
//searches the first half of the array
return binarySearch(number, start, middle, array);
}
else
{
// searches the last half of the array
return binarySearch(number, middle, end, array);
}
}
}
You can use the compareTo() method instead of <,>, & == operators in your example. The logic should still be the same.