how to print last element after deleting odd indices continuously - java

I am trying to delete the odd positions starting from 1 continuously and to get the last remaining element;
e.g.:
n=6;
1 2 3 4 5 6
first: removing odd indices will get (2 4 6);
second: removing odd indices will get (4) which is the answer...
here is my code:
import java.util.HashMap;
public class Odd_Deletions {
public static void oddDeletions(HashMap<Integer, Integer> hm) {
int j = 1;
for (int i = 1; i < hm.size(); i++) {
if (hm.get(i) % 2 != 0) {
continue;
} else {
hm.put(j, i);
j++;
}
}
//System.out.println(hm);
while (true) {
if (hm.size() == 1) {
System.out.println(hm);
break;
} else
oddDeletions(hm);
}
}
public static void main(String args[]) {
int n = 6;
HashMap<Integer, Integer> hm = new HashMap<>();
for (int i = 1; i <= n; i++) {
hm.put(i, i);
}
//System.out.println(hm);
oddDeletions(hm);
}
}
why I am getting StackOverflow Error and what is wrong with this logic?
can anyone fix it?
Thanks and regards;

Perhaps the HashMap is not the right class to use here. Anyway, as #Welbog pointed out, you never remove anything from your table. Also, why use recursion?
Try something like this:
while (hm.size() > 1) {
int j = 1;
int last = hm.size();
for (int i = 1; i <= last; i++) {
int value = hm.remove(i);
if (i % 2 == 0) {
hm.put(j, value);
j++;
}
}
}

There are 3 possible solutions.
(1) I don't know why you need to apply that "removal process" on the HashMap with that logic, anyway a possible solution could be the following one. But use it only if you need to apply to a Map where you need to remove its entries by, for some reasons, applying that logic on the Map keys.
public class RemovingOddIndexes {
public static void main(String[] args) {
// initialize
int n = 6;
HashMap<Integer, Integer> hm = new HashMap<>();
for (int i = 1; i <= n; i++) {
hm.put(i, i);
}
//
oddDeletions(hm);
// print result
hm.forEach((k, v) -> System.out.println(String.format("%s:%s, ", k, v)));
}
public static void oddDeletions(HashMap<Integer, Integer> hm) {
while (hm.size() > 1) {
hm.keySet()
.stream()
.sorted()
.forEach(new Consumer<Integer>() {
int i = 1;
#Override
public void accept(Integer n) {
if (i % 2 == 1)
hm.remove(n);
++i;
}
});
}
}
}
(2) Otherwise use a simple LinkedList that you can skim recursively. I prefer to use a LinkedList over an ArrayList because the alg need to remove elements at every iteration, till the last one. And the remove operation on a LinkedList performs better.
public class RemovingOddIndexes {
public static void main(String[] args) {
// initialize
int n = 6;
List<Integer> list = new LinkedList<>();
for (int i = 1; i <= n; i++) {
list.add(i);
}
//
oddDeletions(list);
// print result
list.forEach(i -> System.out.println(String.format("%s, ", i)));
}
public static void oddDeletions(List<Integer> list) {
while (list.size() > 1) {
int i = 1;
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
it.next();
if (i++ % 2 == 1) {
it.remove();
}
}
}
}
}
(3) Last option, the fastest way possible
int lastOdd = 1 << (int)(Math.log(n) / Math.log(2))

Related

Java Top-Down Merge Sort - Stackoverflow Error

I am trying to implement the top-down merge sort algorithm in Java, using the pseudocode from Wikipedia.
My problem is that my code sometimes throws a StackOverflowError, but not always. I have checked that my code matches the pseudocode several times and cannot find what is wrong with it.
Here is my Java code:
import java.util.ArrayList;
import java.util.Random;
public class Main {
public static void main(String[] args) {
Random r = new Random();
ArrayList<Integer> numbers = new ArrayList<Integer>();
for (int i = 1; i <= 15; i++) {
numbers.add(r.nextInt(100));
}
numbers = mergeSort(numbers);
System.out.println(numbers);
}
public static ArrayList<Integer> mergeSort(ArrayList<Integer> m) {
if (m.size() <= 1) {
return m;
}
ArrayList<Integer> left = new ArrayList<Integer>();
ArrayList<Integer> right = new ArrayList<Integer>();
for (Integer x : m) {
if (m.indexOf(x) < (m.size()) / 2)
left.add(x);
else {
right.add(x);
}
}
left = mergeSort(left);
right = mergeSort(right);
return merge(left, right);
}
private static ArrayList<Integer> merge(ArrayList<Integer> l, ArrayList<Integer> r) {
ArrayList<Integer> result = new ArrayList<Integer>();
while (l.size() > 0 && r.size() > 0) {
if (l.get(0) <= r.get(0)) {
result.add(l.get(0));
l.remove(0);
}
else {
result.add(r.get(0));
r.remove(0);
}
}
while (l.size() > 0) {
result.add(l.get(0));
l.remove(0);
}
while (r.size() > 0) {
result.add(r.get(0));
r.remove(0);
}
return result;
}
}
Your algorithm encounters issues when there are duplicate elements, as indexOf will only return the index of the first one. Use a index-based for loop instead. Demo
for (int i = 0; i < m.size(); i++) {
if (i < (m.size()) / 2)
left.add(m.get(i));
else {
right.add(m.get(i));
}
}
In the mergeSort method need to change the for loop little bit and try again.
for (int i=0;i< m.size()/2;i++)
left.add(m.get(i));
for (int i=m.size()/2;i< m.size();i++)
right.add(m.get(i));

Sort the array elements based on the elements frequency in java

I have written code to sort the array in java based on the frequency of the elements in it. I need better code or pseudo code (without collection framework).Please help with links or code.
public class SortByFreq1 {
public static void main(String[] args) {
int arr[] = { 2, 5, 2, 8, 5, 6, 8, 8, 0, -8 };
int nArr[] = new int[arr.length];
Map<Integer,Integer> map = new HashMap<Integer, Integer>();
Map<Integer,Integer> sortmap = new HashMap<Integer, Integer>();
ArrayList<Integer> arrList = new ArrayList<Integer>();
for (int i = 0; i < arr.length; i++) {
arrList.add(arr[i]);
}
Set<Integer> set = new HashSet<Integer>(arrList);
for (Integer i : set) {
map.put(i, Collections.frequency(arrList, i));
}
// System.out.println(map.keySet());
// sort map by value
Set<Entry<Integer,Integer>> valList=map.entrySet();
ArrayList<Entry<Integer, Integer>> tempLst = new ArrayList<Map.Entry<Integer, Integer>>(valList);
Collections.sort(tempLst, new Comparator<Entry<Integer, Integer>>() {
#Override
public int compare(Entry<Integer, Integer> o1, Entry<Integer, Integer> o2) {
return o2.getValue().compareTo(o1.getValue());
}
});
int k = 0;
for (Entry<Integer, Integer> entry : tempLst) {
int no = entry.getKey();
int noOfTimes = entry.getValue();
int i = 0;
while (i < noOfTimes) {
nArr[k++] = no;
i++;
}
}
for (int i = 0; i < nArr.length; i++)
System.out.print(nArr[i] + " ");
}
}
The logic behind it is quite similar to Counting Sort.
ATTENTION: We are NOT to modify the array passed in.
There are two different methods while having almost the same time and space complexity.
Time complexity: max(n, O(klogk));
Space complexity: O(n) - the array to be returned;
k mentioned above is the amount of distinct numbers in the array.
Built-in Collection Method
Using Stream perhaps we can make the process a little bit cleaner though OP is not asking for this:
/**
* 1. count the frequency and sort the entry based on the frequency while using LinkedHashMap to retain the order;
* 2. fill up the new array based on the frequency while traversing the LinkedHashMap;
* #param arr
* #return
*/
private static int[] sortByCounting(int[] arr) {
Map<Integer, Long> countMap = Arrays.stream(arr).boxed()
.collect(Collectors.groupingBy(Integer::intValue, Collectors.counting()))
.entrySet().stream()
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldV, newV) -> oldV, LinkedHashMap::new));
int[] newArr = new int[arr.length];
int i = 0;
for (Map.Entry<Integer, Long> entry : countMap.entrySet()) {
Arrays.fill(newArr, i, i += entry.getValue().intValue(), entry.getKey());
}
return newArr;
}
Custom Method
Since we cannot use built-in collection methods, meantime we have to record the count for the number.
Instinctively, we could introduce a custom pair to record the number and its related frequency (or count we could say) as our custom method.
private static int[] sortByPlainCounting(int[] arr) {
if (arr.length < 1) throw new IllegalArgumentException("Array cannot be empty");
MyPair[] pairs = prepareMyPairs(arr);
Arrays.sort(pairs, Comparator.comparing(MyPair::getCount).reversed());
int[] newArr = new int[arr.length];
int i = 0;
for (MyPair pair : pairs) {
Arrays.fill(newArr, i, i += pair.count, pair.key);
}
return newArr;
}
static class MyPair {
int key;
int count;
public MyPair(int theKey) {
this.key = theKey;
this.count = 1;
}
public void inc() {
this.count++;
}
public int getCount() {
return this.count;
}
}
static MyPair[] prepareMyPairs(int[] arr) {
Integer[] tmpArr = Arrays.stream(arr).boxed().toArray(Integer[]::new);
Arrays.sort(tmpArr, Comparator.reverseOrder());
int count = 1;
int prev = tmpArr[0];
for (int i = 1; i < tmpArr.length; i++) {
if (tmpArr[i] != prev) {
prev = tmpArr[i];
count++;
}
}
MyPair[] pairs = new MyPair[count];
int k = 0;
for (int i = 0; i < tmpArr.length; i++) {
if (pairs[k] == null) {
pairs[k] = new MyPair(tmpArr[i]);
} else {
if (pairs[k].key == tmpArr[i]) {
pairs[k].inc();
} else {
k++; i--;
}
}
}
return pairs;
}
A comparison and demonstration
Make a final comparison, we can prove that:
average time cost of custom is a little bit worse (1.4 times worse) while the worst case is far better (4 times better) than the built-in collection method;
the custom method is correct;
public static void main(String[] args) {
int N = 10_000 + new Random().nextInt(100);
Long start;
List<Long> list0 = new ArrayList<>();
List<Long> list1 = new ArrayList<>();
for (int i = 0; i < 100; ++i) {
int[] arr = RandomGenerator.generateArrays(N, N, N / 10, N / 5, false);
start = System.nanoTime();
int[] arr0 = sortByCounting(arr);
list0.add(System.nanoTime() - start);
start = System.nanoTime();
int[] arr1 = sortByPlainCounting(arr);
list1.add(System.nanoTime() - start);
System.out.println(isFrequencyEqual(arr0, arr1));
}
System.out.println("Collection time cost: " + list0.stream().collect(Collectors.summarizingLong(Long::valueOf)));
System.out.println("Custom time cost: " + list1.stream().collect(Collectors.summarizingLong(Long::valueOf)));
}
private static boolean isFrequencyEqual(int[] arr0, int[] arr1) {
Map<Integer, Long> countMap0 = getCountMap(arr0);
Map<Integer, Long> countMap1 = getCountMap(arr1);
boolean isEqual = countMap0.entrySet().size() == countMap1.entrySet().size();
if (!isEqual) return false;
isEqual = countMap0.values().containsAll(countMap1.values()) &&
countMap1.values().containsAll(countMap0.values());
if (!isEqual) return false;
List<Long> countList0 = countMap0.values().stream().collect(Collectors.toList());
List<Long> countList1 = countMap1.values().stream().collect(Collectors.toList());
for (int i = 0; i < countList0.size(); i++) {
if (countList1.get(i) != countList0.get(i)) return false;
}
return true;
}
private static Map<Integer, Long> getCountMap(int[] arr) {
return Arrays.stream(arr).boxed()
.collect(Collectors.groupingBy(Integer::intValue, Collectors.counting()))
.entrySet().stream()
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldV, newV) -> oldV, LinkedHashMap::new));
}
The helper util method:
public static int[] generateArrays(int minSize, int maxSize, int low, int high, boolean isUnique) {
Random random = new Random(System.currentTimeMillis());
int N = random.nextInt(maxSize - minSize + 1) + minSize;
if (isUnique) {
Set<Integer> intSet = new HashSet<>();
while (intSet.size() < N) {
intSet.add(random.nextInt(high - low) + low);
}
return intSet.stream().mapToInt(Integer::intValue).toArray();
} else {
int[] arr = new int[N];
for (int i = 0; i < N; ++i) {
arr[i] = random.nextInt(high - low) + low;
}
return arr;
}
}
And the test output:
Sorted by frequency: true
// ... another 98 same output
Sorted by frequency: true
Collection time cost: LongSummaryStatistics{count=100, sum=273531781, min=466684, average=2735317.810000, max=131741520}
Custom time cost: LongSummaryStatistics{count=100, sum=366417748, min=1733417, average=3664177.480000, max=27617114}
Can be done in O(n) using pigeonhole sort. Pseudo code:
counts = new HashMap<Item, int>(),;
foreach item in elements:
counts[item] += 1;
buckets = new List<Item>[elements.length+1];
foreach item in counts:
buckets[counts[item]].Append(item)
for i from 1 to elements.length:
bucket = buckets[i]; /* edit: looping over i instead over bucket */
for item in bucket:
/* edit: if the out has to preserve the original number of elements
such as [1,5,5,0,1,9,1] will print
9,0,5,5,1,1,1 instead of 9,0,5,1, then the next line
has to be repeated i times*/
System.out.println(item)
edit: The same can be written without collection framework, by implementing a hash table and a linked list:
class Node {
public Node next;
public int value;
};
log2count = Math.ceil(Math.log(elements.length) / Math.log(2));
hashSize = (int) Math.Round(Math.Pow(2, log2count) * 2);
/* countsQuadraticProbing[i] is 0 if the hash entry is empty,
otherwise it contains the frequency of the element in
elementsQuadraticProbing[i].
Note that quadratic probing over a hash table of size 2**k,
and probing of (0.5 i + 0.5 i**2) is guaranteed to find an empty
entry if the hash table is not full.
*/
countsQuadraticProbing = new int[hashSize];
elementsQuadraticProbing = new int[hashSize];
foreach item in elements:
for i from 0 to hashSize-1:
index = (item + (i * (i + 1) / 2)) % hashSize;
if countsQuadraticProbing[index] == 0:
countsQuadraticProbing[index] = 1;
elementsQuadraticProbing[index] = item;
break;
if elementsQuadraticProbing[index] == item:
countsQuadraticProbing[index]++;
break;
buckets = new Node[elements.length+1];
for i from 0 to hashSize-1:
count = countsQuadraticProbing[index];
if count != 0:
Node topNode = new Node();
topNode.next = buckets[count];
topNode.value = elementsQuadraticProbing[i];
buckets[count] = topNode;
/* there are O(N) buckets, total of elements in all buckets O(N),
overall complexity of the nested loop O(N)
*/
for i from 1 to elements.length:
node = buckets[i] /* edit: using i for iteration */
while node != null:
/* edit: if the out has to preserve the original number of elements
such as [1,5,5,0,1,9,1] will print
9,0,5,5,1,1,1 instead of 9,0,5,1, then the next line
has to be repeated i times*/
System.out.println(node.value);
node = node.next;
I'm just curious why couldn't you use the good old bubble sort in the case and just customize a bit the Bubble In worst case scenario the time complexity is going to be O(n*n) and space complexity will be O(3n) :)
Pure arrays implementation is going to be something like:
private static void bubbleSortByOccurrences(int[] arr) {
int[][] counter = new int[2][arr.length];
int counterIndex = -1;
for (int value : arr) {
int idx = 0;
for (; idx <= counterIndex; idx++) {
if (counter[0][idx] == value) {
counter[1][idx]++;
while (idx > 0 && counter[1][idx] > counter[1][idx-1]) {
int temp = counter[1][idx];
counter[0][idx] = counter[0][idx-1];
counter[1][idx] = counter[1][idx-1];
counter[0][idx-1] = value;
counter[1][idx-1] = temp;
idx--;
}
break;
}
}
if (idx > counterIndex) {
counter[0][idx] = value;
counter[1][idx]++;
counterIndex = idx;
}
}
fillArrayBackwards(arr, counter, counterIndex);
}
private static void fillArrayBackwards(int[] buf, int[][] counter, int counterIndex) {
for (int i = counterIndex, j = buf.length - 1; i >=0; i--) {
for (int k = 0; k < counter[1][i]; k++) {
buf[j--] = counter[0][i];
}
}
}
And the same algorithm implemented by using a Bubble class will look something like:
private static void bubbleSortByOccurrences(int[] arr) {
Bubble head = null;
for (int value : arr) {
if (head == null) {
head = new Bubble(value);
} else {
Bubble currentHead = null;
Bubble current = head;
for (; current != null && !(current.getValue() == value); current = current.getTail()) {
currentHead = current;
}
if (current == null) {
current = new Bubble(value);
current.setTail(head);
head = current;
} else {
current.incrementOccurrences();
while (current.getTail() != null && current.getOccurrences() > current.getTail().getOccurrences()) {
Bubble currentTail = current.getTail();
current.setTail(currentTail.getTail());
currentTail.setTail(current);
if (currentHead != null) {
currentHead.setTail(currentTail);
currentHead = currentTail;
} else {
head = currentTail;
}
}
}
}
}
fillArrayBackwards(arr, head);
}
private static void fillArrayBackwards(int[] buf, Bubble head) {
int i = buf.length - 1;
for (Bubble current = head; current != null; current = current.getTail()) {
for (int j = 0; j < current.getOccurrences(); j++) {
buf[i--] = current.getValue();
}
}
}
Where the custom defined bubble is as follows:
class Bubble {
private int value;
private int occurrences;
private Bubble tail;
public Bubble(int value) {
this.value = value;
this.occurrences = 1;
}
public int getValue() {
return value;
}
public int getOccurrences() {
return occurrences;
}
public void incrementOccurrences() {
this.occurrences++;
}
public Bubble getTail() {
return tail;
}
public void setTail(Bubble tail) {
this.tail = tail;
}
}
your solution is better but since am not using any collections it is gonna be huge.
1.sort the list
2. get the frequency of every element
3. create a new arraylist/ array and store the elements that have higher frequency to lower frequency.
sort the list using any sort algorithm
get the frequency
class CountFrequencies
{
// Function to find counts of all elements present in
// arr[0..n-1]. The array elements must be range from
// 1 to n
void findCounts(int arr[], int n)
{
// Traverse all array elements
int i = 0;
while (i < n)
{
// If this element is already processed,
// then nothing to do
if (arr[i] <= 0)
{
i++;
continue;
}
// Find index corresponding to this element
// For example, index for 5 is 4
int elementIndex = arr[i] - 1;
// If the elementIndex has an element that is not
// processed yet, then first store that element
// to arr[i] so that we don't loose anything.
if (arr[elementIndex] > 0)
{
arr[i] = arr[elementIndex];
// After storing arr[elementIndex], change it
// to store initial count of 'arr[i]'
arr[elementIndex] = -1;
}
else
{
// If this is NOT first occurrence of arr[i],
// then increment its count.
arr[elementIndex]--;
// And initialize arr[i] as 0 means the element
// 'i+1' is not seen so far
arr[i] = 0;
i++;
}
}
System.out.println("Below are counts of all elements");
for (int j = 0; j < n; j++)
System.out.println(j+1 + "->" + Math.abs(arr[j]));
}
above code should give you an output:
1 -> 3
2 -> 0
3 -> 2
4 -> 0
5 -> 2
6 -> 0
7 -> 2
8 -> 0
9 -> 2
10 -> 0
11 -> 0
Now you can easily use the array that stored the frequency of every element to create a new array that contains the most frequent elements in the array
Note that the list is sorted so that arr[0] is the frequency of 1 arr[1] is the frequency of 2 and so on. Again The code is not efficient as yours it is better to use the collection framework.
or if you are familiar use binary trees you can add the element into the tree and use inorder traversal !
Hope you find my answer helpful

Searching a value using for-loop and

The question is this:
Given a non-empty array of integers, every element appears twice except for one. Find that single one.
Input: [4,1,2,1,2]
Output: 4
my code is:
public static int singleNumber(int[] nums) {
int answer = 0;
for (int i =0; i<nums.length-1; i++) {
for(int j = i+1; j<nums.length; j++) {
if(nums[i] != nums[j]) {
answer = nums[i]; //this should be where I am wrong.
}
}
}
return answer;
}
I know that the output was 4 and then now it will be changed to 1.
I'm trying to figure out how not to change the found value once found.
The logic is wrong - your inner loop finds every number that isn't the only number in the array.
I'd maintain a Set to track the numbers I've encountered. The first time you encounter a number, you add it to the Set. The second time you encounter it, you remove it from the Set. Once you're done going over the array, you'd have a Set with a single element, which is your answer:
public static int singleNumber(int[] nums) {
Set<Integer> unique = new HashSet<>();
for (int num : nums) {
// add returns true if num is indeed new to unique
if (!unique.add(num)) {
unique.remove(num);
}
}
return unique.iterator().next();
}
For this problem, i would to bitwise XOR of the numbers. The numbers that are equal will cancel one another and only single integer will be the final value.
public static int singleNumber(int[] nums) {
int answer = 0;
for (int i =0; i<nums.length; i++) {
answer = answer ^ nums[i];
}
return answer;
}
below changes to your method will give you the expected answer
public static int singleNumber(int[] nums) {
int temp = 0;
int answer = 0;
for (int i = 0; i < nums.length; i++) {
boolean flag = true;
temp = nums[i];
for (int j = 0; j < nums.length; j++) {
if (temp == nums[j]) {
if (i != j) {// if a match found then the loop will terminate
flag = false;
break;
}
}
}
if (flag == true) {
answer = temp;
}
}
return answer;
}
Here is another solution using Collectors.groupingBy from Java 8 :
public static int singleNumber(int[] nums) {
return Arrays.stream(nums).boxed()
.collect(Collectors.groupingBy(a -> a, Collectors.counting()))
.entrySet().stream().filter(e -> e.getValue() == 1).findFirst().get().getKey();
}
the idea is :
group by number of occurence
then find the one which is repeated just one time
Note I assume that your array contain at least one element, else you can check the length before you search then throw an exception like this :
public static int singleNumber(int[] nums) throws IllegalArgumentException{
if(nums.length == 0){
throw new IllegalArgumentException("empty array");
}
return Arrays.stream(nums).boxed()
.collect(Collectors.groupingBy(a -> a, Collectors.counting()))
.entrySet().stream().filter(e -> e.getValue() == 1).findFirst().get().getKey();
}
More deeper, If you want to avoid the situation where there are multiple number repeated just one time you can use :
public static int singleNumber(int[] nums) throws IllegalArgumentException {
if (nums.length == 0) {
throw new IllegalArgumentException("empty array");
}
Map<Integer, Long> grouping = Arrays.stream(nums).boxed()
.collect(Collectors.groupingBy(a -> a, Collectors.counting()));
if (grouping.values().stream().filter(c -> c == 1).count() > 1) {
throw new IllegalArgumentException("more than one element is repeated one time");
}
return grouping.entrySet().stream()
.filter(e -> e.getValue() == 1).findFirst().get().getKey();
}
Here is a solution which uses ArrayList.indexOf and ArrayList.lastIndexOf. If they are same, you have your answer.
public static int singleNumber(int[] nums) {
int answer = 0;
//ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(nums));
ArrayList al = new ArrayList();
for (int i =0; i < nums.length; i++) {
al.add(nums[i]);
}
for (int i =0; i < nums.length; i++) {
int test = nums[i];
if(al.indexOf(test) == al.lastIndexOf(test)){
answer = nums[i];
}
}
return answer;
}
Try this:
int[] nums = new int[] {4,2,1,2,1};
int answer = 0;
for (int i =0; i<nums.length-1; i++) {
int times = 0;
int target = nums[i];
for(int j : nums) {
if(j == target) {
times++;
if(times == 2) {
break;
}
}
}
if(times == 1) {
answer = target;
break;
}
}
System.out.println(answer);
you have to go into every number and count how meny are in the array if there is only 1 you instantly stop the loop end set the answer

Median of Medians algorithm not working consistently

I have implemented the select/median of medians algorithm using the following as a reference http://www.ics.uci.edu/~eppstein/161/960130.html (this has previously been linked here Median of Medians in Java).
My code seems to work for small arrays (~100) and even works for arrays of size 100001 http://pastebin.com/mwRc4Hig (answer 5008), but then fails on an input array of size 10001 http://pastebin.com/YwVBmgDk (answer 4960, my code outputs 4958).
Note that the correct answers for the texts above are equivalent to sorting the array and returning the element at array[array.length / 2], regardless of whether the array size is even or odd.
I'm not sure how to debug this issue. The functionality seems arbitrary and I'm just lost. Here below is my code:
public class MedianOfMedians {
public static void main(String[] args) {
MedianOfMedians mds = new MedianOfMedians();
mds.run();
}
private void run() {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[] numArray = new int[n];
for (int i = 0; i < n; i++) {
numArray[i] = in.nextInt();
}
int median = select(numArray, numArray.length / 2);
System.out.print(median);
}
private int select(int[] numArray, int k) {
if (numArray.length <= 10) {
int[] sorted = insertionSort(numArray);
return sorted[k];
}
int divCount = (numArray.length % 5 == 0) ? numArray.length / 5 - 1 : numArray.length / 5;
int[] medOfMed = new int[divCount + 1];
int counter = 0;
int[] subArray;
while (counter <= divCount) {
subArray = splitByFive(counter, divCount, numArray);
medOfMed[counter] = select(subArray, subArray.length / 2);
counter++;
}
int M = select(medOfMed, numArray.length / 10);
List<Integer> lt = new ArrayList<>();
List<Integer> eq = new ArrayList<>();
List<Integer> gt = new ArrayList<>();
for (int i : numArray) {
if (i < M) {
lt.add(i);
} else if (i == M) {
eq.add(i);
} else {
gt.add(i);
}
}
if (k < lt.size()) {
return select(createArray(lt), k);
} else if (k > lt.size() + eq.size()) {
return select(createArray(gt), k - lt.size() - eq.size());
} else {
return M;
}
}
private int[] splitByFive(int splitIter, int divisions, int[] toSplit) {
int numToCopy;
if (splitIter == divisions) {
numToCopy = toSplit.length - (5 * splitIter);
} else {
numToCopy = 5;
}
int[] subArray = new int[numToCopy];
System.arraycopy(toSplit, splitIter * 5, subArray, 0, numToCopy);
return subArray;
}
private int[] createArray(List<Integer> list) {
int[] result = new int[list.size()];
for (int i = 0; i < list.size(); i++) {
result[i] = list.get(i);
}
return result;
}
private int[] insertionSort(int[] numArray) {
for (int i = 1; i < numArray.length; i++) {
int j = i;
while (j - 1 >= 0 && numArray[j] < numArray[j - 1]) {
int temp = numArray[j];
numArray[j] = numArray[j - 1];
numArray[j - 1] = temp;
j--;
}
}
return numArray;
}
}
I don't have time to debug your code, but maybe I can offer a debugging technique for you to try yourself that's useful for recursive algorithms like this.
If there is an input that the algorithm fails on (and there is, as you found) then there is a smallest such input -- and the smaller this input, the easier it is to figure out what's going wrong. Because the algorithm is recursive, you have a nice way to isolate the first place that things go wrong: you can test that the result you are about to return from select() is correct (using a slow, trusted method like copying the data to a temporary buffer, sorting it and then grabbing the half-way element) just before returning the value. Doing this will be much easier if you rearrange the function to use just a single return statement, e.g.:
private int select(int[] numArray, int k) {
int knownCorrectAnswer = selectSlowlyButDefinitelyCorrectly(numArray, k);
int willReturn;
if (numArray.length <= 10) {
int[] sorted = insertionSort(numArray);
willReturn = sorted[k]; // Just remember what we will return
} else { // Need to add else branch here now
...
if (k < lt.size()) {
willReturn = select(createArray(lt), k);
} else if (k > lt.size() + eq.size()) {
willReturn = select(createArray(gt), k - lt.size() - eq.size());
} else {
willReturn = M;
}
} // End of inserted else branch
if (willReturn == knownCorrectAnswer) {
return willReturn;
} else {
yell("First problem occurs with numArray=<...> and k=<...>!");
}
}
yell() should print out the entire problem instance and halt the program (e.g. by throwing an exception). The nice thing about this setup is that you know that when yell() gets called, every call to select() that has already completed was correct -- since if it wasn't, yell() would have already been called and the program would have halted before now. So the output produced by yell() is guaranteed to be the first (not necessarily the smallest, but often that also) subproblem in which things went wrong.

Suggest some more optimised solution for this rand generator

I have a function 'generateRan' that generates random numbers. This function can not be changed.
int generateRan() {
Random num = new Random();
return (1 + num.nextInt(100));
}
I have to write code that will:
Print numbers 1-20 randomly.
Print numbers 1-200 randomly.
Each number should be printed only once.
The function can be used any number of times. But it is a bit heavy so I want to make the code more optimized.
Here is what I've coded:
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList();
Test t = new Test();
iniList(list, 20);
for (Integer i = ((t.generateRan()) % 20); list.size() > 0; i = 1+((t
.generateRan()) % 20)) {
if (list.contains(i.toString())) {
list.remove(i.toString());
System.out.println(i);
}
}
System.out.println("********");
iniList(list, 200);
for (Integer i = ((t.generateRan()%2)*100 + t.generateRan()) ; list.size() > 0; i = ((t.generateRan()%2)*100 + t.generateRan())) {
if (list.contains(i.toString())) {
list.remove(i.toString());
System.out.println(i);
}
}
}
private static void iniList(List list, int i) {
for (Integer k = 1; k <= i; k++) {
list.add(k.toString());
}
}
int generateRan() {
Random num = new Random();
return (1 + num.nextInt(100));
}
}
Currently the code for 1-200 is incorrect.
Each numbers should print only once
Then all you need to to is create a List<Integer> of the entire range, then call Collections.shuffle.
private static void displayNumbers(int minInclusive, int maxInclusive) {
List<Integer> list = new ArrayList<Integer>();
for (int i = minInclusive; i <= maxInclusive; i++) {
list.add(i);
}
Collections.shuffle(list);
for (int value : list) {
System.out.println(value);
}
}
Personally I'd normally use parameters of minInclusive, maxExclusive or minInclusive, count, but it looks like it may be more readable this way for your situation.
Assuming you have to use your generateRan() function, otherwise use Collections.shuffle as indicated.
public static void main(String[] args) {
List<Integer> list = new ArrayList();
initList(list, 200);
while (list.size() > 0) {
int index = generateRan() % list.size();
System.out.println(list.remove(index));
}
}
public static void initList(List<Integer> s, int size) {
for (int i = 1; i <= size; i ++)
s.add(i);
}
public static int generateRan() {
Random num = new Random();
return (1 + num.nextInt(100));
}
You add all the ints you want to print to your list, then only use random to choose which one of these to print. Your function is called n times.
I would recomment using a Set rather than a List, for it's faster in searching for duplicates
Set<Integer> set = new HashSet<Integer>();
for(int i = 0; i < 20;) {
Integer r = generateRan();
if(set.add(r)) {
System.out.println(r);
++i;
}
}

Categories