How would I go about reversing the order from ascending to descending? I am a bit stumped because I cannot use array.reverse, etc because on our tests we won't be allowed to use java built-in methods. Which is a bummer, arrays.sort is amazing. But as of now, it does print in ASCENDING ORDER. I am relatively new to coding and this site so if my format is bad. I apologise.
public static int[] sort(int a, int b, int c) {
int[] j = {a, b ,c};
int i, k, temp = 0;
for(i = 0; i < j.length; i++) {
for(k = i + 1; k < j.length; k++) {
temp = 0;
if(j[i] > j[k]) {
temp = j[i];
j[i] = j[k];
j[k] = temp;
}
}
}
}
public static void main (String[] args) {
System.out.println(Arrays.toString((sort(6,3,11))));
Simply switch this line:
if(j[i] > j[k]) {
to this:
if(j[i] < j[k]) {
This is because in the existing code, you are swapping the elements if the first one is larger (thus moving the larger one to later in the list).
When you use the second code snippet, it swaps the elements if the first one is smaller, thus moving the smaller one to later in the list.
You can use Java 8 streams to simplify this function. Add a parameter to your sort function, and it becomes easy:
private static int[] sort(int a, int b, int c, boolean ascending) {
Comparator<? super Integer> comparator = (ascending) ? Integer::compareTo : Comparator.reverseOrder();
return Stream.of(a, b, c).sorted(comparator).mapToInt(t -> t).toArray();
}
Related
I'm trying to write a class named Range, that takes an array of integers (unsorted) of length n, containing only numbers in the range are from 0 to k.
At first, I declare a constructor which will preprocess the array via Counting Sort algorithm.
Then I want to write query() method that takes two integer arguments: a and b, which form a range of numbers from a to b and returns the total frequency of all the elements in the array having the values within the given range.
My code:
import java.util.Arrays;
import java.util.HashMap;
public class Range {
private int[] a;
private int k;
public Range(int[] a, int k) {
int index = 0;
int[] counterArray = new int[k + 1];
for (int i : a)
counterArray[i]++; // initialize counterArray
for (int i = 0; i < counterArray.length; i++)
while (0 < counterArray[i]) {
a[index++] = i;
counterArray[i]--;
} // end while()
this.a = a;
this.k = k;
} // end constructor()
public int query(int a, int b) {
HashMap<Integer, Integer> map = new HashMap<>(a);
} // end query()
#Override
public String toString() {
return Arrays.toString(a);
} // end toString()
}
I chose HashMap data structure because I need query() method to be executed in constant time O(1).
So my question is: Is it possible to implement the method query() via HashMap?
If not, what are the alternatives? (Note: the time complexity should be O(1) for query(), not bothering about the space complexity).
Code in the main() :
int[] a = {13,12,13,1,2,0,0,1,3,4};
Range range = new Range(a, 13);
System.out.print(range); // prints [0,0,1,1,2,3,4,12,13,13] because array has been sorted
System.out.print(range.query(1, 4)); // calculating number of elements in the range [1, 4]
Expected Output:
5 // elements 1,1,2,3,4 are within the range [1, 4]
Explanation: provided arguments of the query() are: a=1 and b=4, hence, values to be tested are 1,2,3,4. The output should be 5 because there are 5 elements: 1,1,2,3,4.
To obtain the number of elements in the given range (from a to b inclusive) in O(1) time after the array has been sorted, you don't need to use HashMap. Instead, you can reuse the countingArray by making it an instance variable.
This approach also requires a slight modification of the sorting in order to retain the values in the countingArray intact. It's done by introducing one additional variable.
Note that it's a good practice to avoid mutating the input, that why in the code I've used Arrays.copyOf() (you can remove it, if you consider it irrelevant for this exercise).
I've extracted the logic responsible for sorting from the constructor into a separate method. And introduced a method which is meant to calculate the cumulative count for every number in the array (i.e. a number of element having values from 0 up to the current number inclusive).
So, after invoking method init() on the instance of Range we would be able to find the number of elements in the range from a to b by looking at the values stored in the countingArray at corresponding indices. And that would have a cost O(1).
public class Range {
private int[] arr;
private int[] counterArray;
private int k;
private Range(int[] arr, int k) { // constructor is not exposed
this.arr = Arrays.copyOf(arr, arr.length);
this.counterArray = new int[k + 1];
this.k = k;
}
public static Range getInstance(int[] arr, int k) {
Range range = new Range(arr, k);
range.init();
return range;
}
private void init() {
sort();
sumUpCount();
}
private void sort() {
for (int i : arr) {
counterArray[i]++;
}
int index = 0;
int copy;
for (int i = 0; i < counterArray.length; i++) {
copy = counterArray[i];
while (0 < counterArray[i]) {
arr[index++] = i;
counterArray[i]--;
}
counterArray[i] = copy;
}
}
private void sumUpCount() {
for (int i = 1; i < counterArray.length; i++) {
counterArray[i] += counterArray[i - 1];
}
}
public int query(int a, int b) {
return a == 0 ? counterArray[b] : counterArray[b] - counterArray[a - 1];
}
}
main()
public static void main(String[] args) {
int[] a = {13,12,13,1,2,0,0,1,3,4};
Range range = Range.getInstance(a, 13);
System.out.print(range.query(1,4));
}
Output:
5
Yes, in order to cache/precompute the return values of query(), you need to create a composite key, that holds both values. The easiest way to do that is to use a string that holds both numbers divided by a separator. Separator is important otherwise composite key(21, 5) = "215" and key(2, 15) = "215". With separator that would be "21;5" and "2;15" respectivly.
private String key(int a, int b) {
return String.format("%d;%d", a, b);
}
Then for each composite key possible you put the value into HashMap. In the query(a, b) method you just get the value from the Map.
public query(int a, int b) {
return map.get(key(a, b));
}
The downside of this approach is that creation of this object is pretty expensive.
I'm trying to implement the merge sort algorithm on a list of strings of size N, and I've managed to get it to sort, but for some reason the original values are being added onto the end of the sorted list.
I'm quite new to implementing sorting algorithms (read: very new), so would really appreciate anyone letting me know if I've missed something.
public static void mergeSortWords(int n, List<String> words) {
if (n < 2) {
return;
}
int mid = n / 2; // Getting the mid-point of the array
List<String> l = new ArrayList<String>(mid); // Left side of array
List<String> r = new ArrayList<String>(n-mid); // Right side of array
for (int i = 0; i < mid; i++) {
l.add(i, words.get(i));
}
for (int j = mid; j < n; j++) {
r.add(j - mid, words.get(j));
}
mergeSortWords(mid, l); // recursively sort the left side
mergeSortWords(n-mid, r); // recursively sort the right side
mergeWords(n, words, l, r, mid, n-mid); // merge the sorted arrays back together
}
public static void mergeWords(int n, List<String> words, List<String> l, List<String> r, int left, int right) {
if (words.size() > n) {
return;
}
int i = 0, j = 0, k = 0;
while (i < left && j < right) {
if (l.get(i).compareToIgnoreCase(r.get(j)) < 0) { // comparing the strings alphabetically
words.add(k++, l.get(i++));
}
else {
words.add(k++, r.get(j++));
}
}
while (i < left) {
words.add(k++, l.get(i++));
}
while (j < right) {
words.add(k++, r.get(j++));
}
}
I unit tested like so:
#Test
public void mergeSortWordsTest() {
List<String> actual = new ArrayList<String>();
List<String> expected = new ArrayList<String>();
actual.add("hello");
actual.add("yo");
actual.add("hi");
actual.add("what");
actual.add("bottle");
expected.add("bottle");
expected.add("hello");
expected.add("hi");
expected.add("what");
expected.add("yo");
mergeSortWords(actual.size(), actual);
Assert.assertEquals(expected, actual);
And I receive:
java.lang.AssertionError:
Expected :[bottle, hello, hi, what, yo]
Actual :[bottle, hello, hi, what, yo, hello, yo, hi, what, bottle]
Thank you for any pointers!
Because the words list you pass to mergeWords is never cleared. mergeWords will just add new elements to this list without caring about the elements that it already contains. Simply do a
words.clear();
at the beginning of mergeWords.
Alternatively, you can overwrite the existing elements with .set(int index, E element) instead of .add(). But you need to make sure that the list is of the correct size.
A few unrelated comments:
In your function calls, you are always passing the size of the lists as an additional parameter (n, left, right). This is redundant (you can get the size with list.size()). Anything that is redundant can easily become inconsistent (i.e., what happens if you pass a wrong size?). So it is better to remove those parameters.
When you add an element to a list, you use the overload add(int index, E element). This is perfectly fine, but I think using the overload add(E element) is much easier to handle as you don't need to keep track of where to add the elements. The overload will just append the new element to the end of the list.
While implementing improvements to quicksort partitioning,I tried to use Tukey's ninther to find the pivot (borrowing almost everything from sedgewick's implementation in QuickX.java)
My code below gives different results each time the array of integers is shuffled.
import java.util.Random;
public class TukeysNintherDemo{
public static int tukeysNinther(Comparable[] a,int lo,int hi){
int N = hi - lo + 1;
int mid = lo + N/2;
int delta = N/8;
int m1 = median3a(a,lo,lo+delta,lo+2*delta);
int m2 = median3a(a,mid-delta,mid,mid+delta);
int m3 = median3a(a,hi-2*delta,hi-delta,hi);
int tn = median3a(a,m1,m2,m3);
return tn;
}
// return the index of the median element among a[i], a[j], and a[k]
private static int median3a(Comparable[] a, int i, int j, int k) {
return (less(a[i], a[j]) ?
(less(a[j], a[k]) ? j : less(a[i], a[k]) ? k : i) :
(less(a[k], a[j]) ? j : less(a[k], a[i]) ? k : i));
}
private static boolean less(Comparable x,Comparable y){
return x.compareTo(y) < 0;
}
public static void shuffle(Object[] a) {
Random random = new Random(System.currentTimeMillis());
int N = a.length;
for (int i = 0; i < N; i++) {
int r = i + random.nextInt(N-i); // between i and N-1
Object temp = a[i];
a[i] = a[r];
a[r] = temp;
}
}
public static void show(Comparable[] a){
int N = a.length;
if(N > 20){
System.out.format("a[0]= %d\n", a[0]);
System.out.format("a[%d]= %d\n",N-1, a[N-1]);
}else{
for(int i=0;i<N;i++){
System.out.print(a[i]+",");
}
}
System.out.println();
}
public static void main(String[] args) {
Integer[] a = new Integer[]{17,15,14,13,19,12,11,16,18};
System.out.print("data= ");
show(a);
int tn = tukeysNinther(a,0,a.length-1);
System.out.println("ninther="+a[tn]);
}
}
Running this a cuople of times gives
data= 11,14,12,16,18,19,17,15,13,
ninther=15
data= 14,13,17,16,18,19,11,15,12,
ninther=14
data= 16,17,12,19,18,13,14,11,15,
ninther=16
Will tuckey's ninther give different values for different shufflings of the same dataset? when I tried to find the median of medians by hand ,I found that the above calculations in the code are correct.. which means that the same dataset yield different results unlike a median of the dataset.Is this the proper behaviour? Can someone with more knowledge in statistics comment?
Tukey's ninther examines 9 items and calculates the median using only those.
For different random shuffles, you may very well get a different Tukey's ninther, because different items may be examined. After all, you always examine the same array slots, but a different shuffle may have put different items in those slots.
The key here is that Tukey's ninther is not the median of the given array. It is an attempted appromixation of the median, made with very little effort: we only have to read 9 items and make 12 comparisons to get it. This is much faster than getting the actual median, and has a smaller chance of resulting in an undesirable pivot compared to the 'median of three'. Note that the chance still exists.
Does this answer you question?
On a side note, does anybody know if quicksort using Tukey's ninther still requires shuffling? I'm assuming yes, but I'm not certain.
I am working on getting the counts of comparisons and movers when merge sorting. I think I have the recursion I need thanks to this Sort Comparisons Counter but I can not get it to print out. I am obviously very new at programming so I'd be appreciative if you could explain what it is that I am missing.
import java.util.Arrays;
public class MergeSort {
int count = 0;
/**
* #param args
*/
// Rearranges the elements of a into sorted order using
// the merge sort algorithm (recursive).
public int mergeSort(int[] a, int howMany) {
if (a.length >= 2) {
// split array into two halves
int[] left = Arrays.copyOfRange(a, 0, a.length/2);
int[] right = Arrays.copyOfRange(a, a.length/2, a.length);
// sort the two halves
howMany = mergeSort(left,howMany);
howMany = mergeSort(right, howMany);
// merge the sorted halves into a sorted whole
howMany = merge ( left, right, a, howMany);
}
return howMany;
}
// Merges the left/right elements into a sorted result.
// Precondition: left/right are sorted
public static int merge(int[] result, int[] left,
int[] right, int howMany) {
int i1 = 0; // index into left array
int i2 = 0; // index into right array
for (int i = 0; i < result.length; i++) {
if (i2 >= right.length ||
(i1 < left.length && left[i1] <= right[i2])) {
result[i] = left[i1]; // take from left
i1++;
} else {
result[i] = right[i2]; // take from right
i2++;
}
}
return howMany;
}
System.out.println(howMany); // ???
}
you need to call the method through its object wherever you wanna print. something like this (may be in your main method):
MergeSort mObj - new MergeSort();
int[] array = {1,2,3};
int count = mObj.mergeSort(array, 2);
System.out.println(count);
Basically, you need a driver method. When a java class is run, it will look for a public static void main(String[] args) method; if this method doesn't exist, nothing will happen. With what you have now, you should actually get a compile error from System.out.println(howMany); since the variable howMany only exists within the scope (brackets) of the merge method. To understand this better I'd review your notes on variable and method scope and class members. For a quick fix, remove the line at the bottom that I mentioned above, and place this method somewhere in your class:
public static void main(String[] args) {
int[] array = {2,5,8,1,3};
int howMany = mergeSort(array, 5);
System.out.println(howMany);
}
You also need to make your mergeSort method static, so change its definition to
public **static** int mergeSort(int[] a, int howMany)
I tested your code and I'm pretty sure that it doesn't give the answer you want, so be sure to check that. Best of luck learning object oriented programming!
I found similar question about interleaving two arraylists into one, but its in PHP. I was asked this question in interview as well but could'nt solve it, came back to SO to look if it was addressed already, but i could only find this paper
So any pointers to pseudo code or method definition ?
Big(O) restrictions : O(n) - time cost and O(1) - space cost
Example:
a[]= a1, a2, ..., an
b[]= b1, b2, ..., bn
Rearrange the arraylist to a1, b1, a2, b2, ..., an, bn
Editv1.0 : Arraylists a[] and b[] are of same size
Editv2.0 : What if the question is extended to rearrange in one of given two arrays, but not create a new array ?
For simplicity, assume that the arrays are the same length, and are int arrays.
int[] merge(int[] a, int[] b)
{
assert (a.length == b.length);
int[] result = new int[a.length + b.length];
for (int i=0; i<a.length; i++)
{
result[i*2] = a[i];
result[i*2+1] = b[i];
}
return result;
}
I think this is not doable with your given constraints (O(n) time and O(1) space, i.e. no additional space) for an array or array-based list. (Assuming of course, that we can't simply create a new List object delegating to the original ones.)
If you have two linked lists, this is doable - if we assume the garbage collector is fast enough, i.e. deleting an element from one list and adding it to another list does not violate the space limitation.
public <X> void interleaveLists(List<X> first, List<X> second)
{
ListIterator<X> firstIt = first.listIterator();
ListIterator<X> secondIt = second.listIterator();
while(secondIt.hasNext()) {
fistIt.next();
firstIt.add(secondIt.next());
secondIt.remove();
}
}
This method works for any pair of lists, but is only O(n) for linked lists.
For a custom linked list where we can modify the pointers, we don't have to rely on the garbage collector, we would simply change the nodes. Here for a singly-linked list:
public void interleaveLinkedLists(Node<X> firstList, Node<X> secondList) {
while(secondList != null) {
Node<X> nextFirst = firstList.next;
Node<X> nextSecond = secondList.next;
firstList.next = secondList;
secondList.next = nextFirst;
firstList = nextFirst;
secondList = nextSecond;
}
}
For a doubly-linked list, we would also have to adapt the prev-pointers.
Here the wrapping variant mentioned in the first paragraph:
public List<X> interleaveLists(final List<X> first, final List<X> second)
{
if (first.size() != second.size())
throw new IllegalArgumentException();
return new AbstractList<X>() {
public int size() {
return 2 * first.size();
}
public X get(int index) {
return index % 2 == 0 ? first.get(index / 2) : second.get(index / 2);
}
// if necessary, add a similar set() method. add/remove are not sensible here.
};
}
This is actually O(1) in time, too.
I've done up a small solution going on the assumption that you are talking about using the ArrayList (see my comment on the question). I may be oversimplifying the problem based on some of the responses here, but here goes anyway.
The below example takes a and b both of type ArrayList<Integer> and interleaves them by inserting b[0] after a[0], b[1] after a[1] etc. This snippet of course naively assumes that a and b are of the same size as per your Edit v1.0. It also does not create a new ArrayList as per your Edit v2.0.
//a and b are of type ArrayList<Integer>
for (int i = a.size(); i > 0; i--)
{
a.add(i, b.get(i - 1));
}
No matter what happens if you are combining the ArrayLists you're going to have twice the size.
I believe the mod (%) operations in Matt's answer are incorrect. Under the same assumption (that the arrays are the same length), I'd propose the following solution instead:
static int[] merge(final int[] a, final int[] b)
{
final int[] result = new int[a.length * 2];
for (int i=0; i < a.length; i++)
{
result[i << 1] = a[i];
result[(i << 1) + 1] = b[i];
}
return result;
}
I tested (very briefly), and it appears to work, but of course makes no attempt to handle error conditions such as null arguments or input arrays mismatched in size.
in the meantime lambda was introduced
O(n) time complexity
O(1) space complexity
int[] merge(int[] a, int[] b) {
return( IntStream.range( 0, a.length ).flatMap(
n -> IntStream.of( a[n], b[n] ) ).toArray() );
}
The lists don't have to be the same size:
public class InterleaveTwoLists<X> {
public List<X> interleaveLists(final List<X> first, final List<X> second) {
return new AbstractList<X>() {
private int minSize;
private int combinedMinSize;
private int size;
private List<X>largerList;
{{
minSize = Math.min(first.size(), second.size());
combinedMinSize = minSize*2;
size = first.size() + second.size();
largerList = first.size() > minSize ? first : second;
}}
public int size() {
return size;
}
public X get(int index) {
if (index < combinedMinSize) {
return index % 2 == 0
? first.get(index / 2)
: second.get(index / 2);
}
else {
return largerList.get(index-minSize);
}
}
};
}
}
To test this:
public class InterleaveTwoListsTest {
private static final Logger log =
LoggerFactory.getLogger(InterleaveTwoListsTest.class);
List<String> first = new ArrayList<String>() {
{
add("one"); add("three"); add("five");
add("seven"); add("eight"); add("nine");
}};
List<String> second = new ArrayList<String>() {
{
add("two"); add("four"); add("six");
}};
private InterleaveTwoLists<String> interleaveTwoLists;
#Before
public void setUp() throws Exception {
interleaveTwoLists = new InterleaveTwoLists<>();
}
#Test
public void test() {
List<String> combinedList = interleaveTwoLists.interleaveLists(first, second);
for( int i = 0; i < first.size() + second.size(); i++) {
log.debug("{}: {}", i, combinedList.get(i));
}
}
}