Suppose we have a list of integers. I would like to detect and print on the screen the most frequently repeated item . I know how to do it when the most common element is only one. However, if we have such a list which contains these elements:
10, 5, 2, 1, 2, 4, 6, 6, 6, 6, 10, 10, 10
i want to print a six and a ten so I mean I want to print all the most frequently repeated elements no matter how many there are..
You would need a map (aka dictionary) data structure to solve this problem. This is the fastest and most concise solution I could come up with.
public static void main(String[] args){
int[] arr = new int[]{10, 5, 2, 1, 2, 4, 6, 6, 6, 6, 10, 10, 10};
printMostFrequent(arr);
}
private static void printMostFrequent(int[] arr){
// Key: number in input array
// Value: amount of times that number appears in the input array
Map<Integer, Integer> counts = new HashMap<>();
// The most amount of times the same number appears in the input array. In this example, it's 4.
int highestFrequency = 0;
// Iterate through input array, populating map.
for (int num : arr){
// If number doesn't exist in map already, its frequency is 1. Otherwise, add 1 to its current frequency.
int currFrequency = counts.getOrDefault(num, 0) + 1;
// Update frequency of current number.
counts.put(num, currFrequency);
// If the current number has the highest frequency so far, store its frequency for later use.
highestFrequency = Math.max(currFrequency, highestFrequency);
}
// Iterate through unique numbers in array (remember, a Map in Java allows no duplicate keys).
for (int key : counts.keySet()){
// If the current number has the highest frequency, then print it to console.
if (counts.get(key) == highestFrequency){
System.out.println(key);
}
}
}
Output
6
10
nums = [10, 5, 2, 1, 2, 4, 6, 6, 6, 6, 10, 10, 10]
def most_recurrent_ints(arr):
log = {}
for i in arr:
if i in log:
log[i] += 1
else:
log[i] = 1
current_max = 0
for i in log.values():
if i > current_max:
current_max = i
results = []
for k, v in log.items():
if v == current_max:
results.append(k)
return results
print(most_recurrent_ints(nums))
I'm bad at Java but this is the Python solution, maybe someone can translate. I can do it in JS if you need.
Have you considered using a dictionary-type data structure? I am not a java person, so this may not be pretty, but it does what you are looking for:
final int[] array = new int[]{ 10, 5, 2, 1, 2, 4, 6, 6, 6, 6, 10, 10, 10 };
Map<Integer, Integer> dictionary = new HashMap<Integer,Integer>();
for(int i = 0; i < array.length; ++i){
int val = array[i];
if(dictionary.containsKey(val)){
dictionary.put(val, dictionary.get(val) + 1);
}else{
dictionary.put(val, 1);
}
}
for(Map.Entry<Integer,Integer> entry : dictionary.entrySet()){
System.out.println(entry.getKey() + ": " + entry.getValue());
}
This would output:
1: 1
2: 2
4: 1
5: 1
6: 4
10: 4
public static void main(String[] args) {
MostFrequent(new Integer[] {10, 5, 2, 1, 2, 4, 6, 6, 6, 6, 10, 10, 10});
}
static void MostFrequent(Integer[] arr) {
Map<Integer, Integer> count = new HashMap<Integer, Integer>();
for (Integer element : arr) {
if (!count.containsKey(element)) {
count.put(element,0);
}
count.put(element, count.get(element) + 1);
}
Map.Entry<Integer, Integer> maxEntry = null;
ArrayList<Integer> list = new ArrayList<Integer>();
for (Map.Entry<Integer, Integer> entry : count.entrySet()) {
if (maxEntry == null || entry.getValue() > maxEntry.getValue()) {
list.clear();
list.add(entry.getKey());
maxEntry = entry;
}
else if (entry.getValue() == maxEntry.getValue()) {
list.add(entry.getKey());
}
}
for (Integer item: list) {
System.out.println(item);
}
}
Output:
6
10
Related
I'm trying to count all the duplicate numbers in the list in my code. In addition, the list should be compared with a set; if there is a match, the respective value should be replaced by another.
I managed to program correctly, but can you get it into a single for loop? If I get it in the same loop, the map always gets the wrong value because the values are always overwritten.
Here is my code:
public static Map<Integer, Integer> replace(List<Integer> inputList, Set<Integer> numberSet, Integer newNumber) {
Map<Integer, Integer> map = new HashMap<>();
for (int i =0; i < inputList.size(); i++){
if(numberSet.contains(inputList.get(i))){
map.put(inputList.get(i), Collections.frequency(inputList, inputList.get(i)));
}
}
for (int i =0; i < inputList.size(); i++){
if(numberSet.contains(inputList.get(i))){
inputList.set(i, newNumber);
}
}
return map;
}
Using Collections.frequency while altering the list in the same loop is throwing off your numbers. Here is an approach that handles counting frequency with each iteration while at the same time updating the list values equal to newNumber.
If the code you displayed is giving you the output you desire, then here I have written the following single loop solution that does the same.
public static Map<Integer, Integer> replace(List<Integer> inputList, Set<Integer> numberSet, Integer newNumber) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < inputList.size(); i++){
Integer value = inputList.get(i);
// value already in map, count 1 more
if(numberSet.contains(value) && map.containsKey(value)) {
map.put(value, map.get(value) + 1);
// value not in map yet, count 1
} else {
map.put(value, 1);
}
// always set all list items to newNumber
inputList.set(i, newNumber);
}
return map;
}
Actually, Collections.frequency is not needed here at all because it will recalculate the frequency for each element of inputList from the start.
The frequency of elements is conveniently calculated using Map::merge method, so that each matching value is counted and replaced at the same iteration:
public static Map<Integer, Integer> replace(List<Integer> inputList, Set<Integer> numberSet, Integer newNumber) {
Map<Integer, Integer> map = new HashMap<>();
for (int i =0; i < inputList.size(); i++) {
Integer v = inputList.get(i);
if(numberSet.contains(v)) {
map.merge(v, 1, Integer::sum);
inputList.set(i, newNumber);
}
}
return map;
}
Test:
List<Integer> ints = Arrays.asList(1, 2, 3, 2, 3, 5, 3, 1, 4, 2);
System.out.println(ints);
System.out.println(replace(ints, Set.of(3, 4, 5), 9));
System.out.println(ints);
Output:
frequencies of 3, 4, 5 are calculated correctly
all occurrences of 3, 4, 5 set to 9
[1, 2, 3, 2, 3, 5, 3, 1, 4, 2]
{3=3, 4=1, 5=1}
[1, 2, 9, 2, 9, 9, 9, 1, 9, 2]
Assume some arbitrary array of ints, for example [3, 1, 1, 1, 13, 13, 13, 13, 13, 8, 8, 8, 8, 8, 8] (order doesnt matter, I just ordered them so its easier to read)
How can I go about creating a method which, when asked for a range, say [2,4], prints the 2nd most, 3rd most, and 4th most frequent integers in the given array? In this example array, it'd print in order of frequencies:
Value: 13. Frequency: 5
Value: 1. Frequency: 3
Value: 3. Frequency: 1
noting how the integer 8 is the most frequent integer, and 3 is the 4th most frequent integer.
You want to first fill a map with all integers, where each integer is the key and the frequency is the value. If you encounter an existing integer, you increase the frequency (value).
You then sort the entries in that map by their values (frequencies) and simply put the result into a list.
Now you have a sorted list of entrysets where the key is the integer and the value is the frequency, which you can simply access by index:
public static void main(final String[] args) {
final int integers[] = new int[] {3, 1, 1, 1, 13, 13, 13, 13, 13, 8, 8, 8, 8, 8, 8};
final Map<Integer, Integer> integerFrequencies = new HashMap<>();
// Create a map of all integers with their frequency:
for (final int i : integers) {
if (integerFrequencies.containsKey(i)) {
integerFrequencies.put(i, integerFrequencies.get(i) + 1);
}
else {
integerFrequencies.put(i, 1);
}
}
// Sort the map entries by their value (frequency) into a list:
final var integerFrequenciesSorted = integerFrequencies.entrySet().stream().sorted(Collections.reverseOrder(Map.Entry.comparingByValue())).collect(Collectors.toList());
// Print 2nd to 4th entry:
for (int i = 1; i < 4; i++) {
final var integerFrequency = integerFrequenciesSorted.get(i);
System.out.println("Value: " + integerFrequency.getKey() + ". Frequency: " + integerFrequency.getValue());
}
}
prints:
Value: 13. Frequency: 5
Value: 1. Frequency: 3
Value: 3. Frequency: 1
public static void main(String[] args) {
SomeClass obj = new SomeClass();
obj.printRange(new int[]{2, 4}, new int[]{1, 1, 1, 3, 4, 4, 8, 8});
}
public void printRange(int[] range, int[] arrayOfNumber) {
// Using HashMap so we can save frequency against each number
HashMap<Integer, Integer> hashMap = new HashMap<>();
for (int i = 0; i < arrayOfNumber.length; i++) {
if (hashMap.get(arrayOfNumber[i]) != null) {
hashMap.put(arrayOfNumber[i], hashMap.get(arrayOfNumber[i]) + 1);
} else {
hashMap.put(arrayOfNumber[i], 1);
}
}
//Sort the hashmap and convert it to linkedhashmap
LinkedHashMap<Integer, Integer> linkedHashMap = sortHashMapByValues(hashMap);
Iterator<Map.Entry<Integer, Integer>> itr = linkedHashMap.entrySet().iterator();
//iterate through the linkedhashmap
for (int i = 0; i < range[1]; i++) {
// print only your range: expecting range in array → [2,4]
// start with range[0]→2, range[1]→4
if (i >= range[0] && itr.hasNext()) {
Map.Entry<Integer, Integer> entry = itr.next();
System.out.println("Range at: " + i);
System.out.println("key: " + entry.getKey());
System.out.println("Frequency: " + entry.getValue());
}
}
}
public LinkedHashMap<Integer, Integer> sortHashMapByValues(
HashMap<Integer, Integer> passedMap) {
List<Integer> mapKeys = new ArrayList<>(passedMap.keySet());
List<Integer> mapValues = new ArrayList<>(passedMap.values());
Collections.sort(mapValues);
Collections.sort(mapKeys);
LinkedHashMap<Integer, Integer> sortedMap =
new LinkedHashMap<>();
Iterator<Integer> valueIt = mapValues.iterator();
while (valueIt.hasNext()) {
int val = valueIt.next();
Iterator<Integer> keyIt = mapKeys.iterator();
while (keyIt.hasNext()) {
Integer key = keyIt.next();
int comp1 = passedMap.get(key);
int comp2 = val;
if (comp1 == comp2) {
keyIt.remove();
sortedMap.put(key, val);
break;
}
}
}
return sortedMap;
}
So I came across this problem in Java that wants to remove a list of number that has occurred more than twice but keeping the order, and the first two occurrences.
For example, if the list is 2, 3, 5, 4, 5, 2, 4, 3, 5, 2, 4, 4, 2, 10
the expected output would be 2, 3, 5, 4, 5, 2, 4, 3, 10
I've tried several methods, including using a counter int to keep track of the occurrences and filter it out, but I am not sure how I can go about it
class DeDup {
// function to find the element occurring more than 3 times
static void get2Occurrences(int arr[]) {
int i;
int count = 0;
for (i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length; j++) {
if (arr[i] == arr[j])
count++;
}
if (count < 3 && count > 0) {
//return arr[i];
System.out.print(arr[i] + ", ");
} else {
for (int k = 2; k > 0; k--) {
System.out.print(arr[i] + ", ");
}
}
}
}
// driver code
public static void main(String[]args) {
int arr[] = new int[]{ 2, 3, 5, 4, 5, 2, 4, 3, 5, 2, 4, 4, 2, 10 };
//expected output: 2, 3, 5, 4, 5, 2, 4, 3, 10
//int n = arr.length;
get2Occurrences(arr);
}
}
the expected output would be 2, 3, 5, 4, 5, 2, 4, 3, 10
but i got 2, 2, 3, 3, 5, 5, 4, 4, 5, 5, 2, 2, 4, 4, 3, 3, 5, 5, 2, 2, 4, 4, 4, 4, 2, 2, 10, 10,
I would do it using a pair of Sets: Set.add returns a boolean indicating whether the element was added. Hence:
boolean b = set1.add(num) || set2.add(num);
will be true if it was added into either set - and it will only try to add it to set2 if it was already in set1 - and false otherwise, meaning it was present in both sets already.
Set<Integer> set1 = new HashSet<>();
Set<Integer> set2 = new HashSet<>();
for (Integer a : arr) {
if (set1.add(a) || set2.add(a)) {
System.out.print(a + ", ");
}
}
Ideone demo
Keep track of the number of occurrences found for each element.
Something like this:
static void get2Occurrences(int arr[])
{
// Initialize occurrences found
Hashtable<Integer, Integer> found_elms = new Hashtable<Integer, Integer>();
// Loop over all elements
int counter;
int number;
Integer found;
for (counter = 0; counter < arr.length; counter++)
{
number = arr[counter];
found = found_elms.get(number);
if (found == null)
found = 1;
else
found = found + 1;
found_elms.put(number, found);
if (found < 3)
System.out.print(number + ", ");
}
System.out.println();
} // get2Occurrences
In this solution, you can change the number of repetitions from three to another.
int[] arr = new int[]{2, 3, 5, 4, 5, 2, 4, 3, 5, 2, 4, 4, 2, 10};
Map<Integer, Integer> counting = new HashMap<>();
for (Integer current : arr) {
int count = counting.compute(current, (x, n) -> n == null ? 1 : n + 1);
if (count < 3) {
System.out.print(current + ", ");
}
}
I would propose an insert filter : go through each element with a for loop and insert it into a response array if the response arrays doesnt contain it 2 times yet
import java.util.ArrayList;
import java.util.List;
...
List<Integer> lst = new ArrayList<>();
for (int i : arr) {
if (lst.lastIndexOf(i) == lst.indexOf(i)) {
lst.add(i);
}
}
return lst.stream().mapToInt(i -> i).toArray();
So this is what I came up with in the end
import java.util.ArrayList;
import java.util.Arrays;
class DeDup {
// function to find the element occurring more than 3 times
static void get2Occurrences(ArrayList<Integer> arr)
{
for(int i=0; i<arr.size(); i++){
int count=0;
for(int j=0; j<i; j++){
if(arr.get(j)==arr.get(i)){
count++;
}
}
if (count>=2){
arr.remove(i);
i--;
}
}
System.out.println("Output: "+arr);;
}
// driver code
public static void main(String[] args)
{
ArrayList<Integer> arr = new ArrayList<Integer>(Arrays.asList( 2, 3, 5, 4, 5, 2, 4, 3, 5, 2, 4, 4, 2, 10));
//expected output: 2, 3, 5, 4, 5, 2, 4, 3, 10
//int n = arr.length;
System.out.println("Input: "+arr);
get2Occurrences(arr);
}
}
Solution using Iteartor:
List<Integer> array = Lists.newArrayList(2, 3, 5, 4, 5, 2, 4, 3, 5, 2, 4, 4, 2, 10);
Map<Integer, Integer> control = new HashMap<>();
Iterator<Integer> iterator = array.iterator();
while(iterator.hasNext()) {
Integer element = iterator.next();
Integer occurrences = control.getOrDefault(element, 0);
if (occurrences >= 2) {
iterator.remove();
} else {
control.put(element, occurrences + 1);
}
}
I'm trying to solve a problem on CodeFights called firstDuplicate, that states -
Given an array a that contains only numbers in the range from 1 to
a.length, find the first duplicate number for which the second
occurrence has the minimal index. In other words, if there are more
than 1 duplicated numbers, return the number for which the second
occurrence has a smaller index than the second occurrence of the other
number does. If there are no such elements, return -1.
Example
For a = [2, 3, 3, 1, 5, 2], the output should be firstDuplicate(a) =
3.
There are 2 duplicates: numbers 2 and 3. The second occurrence of 3
has a smaller index than than second occurrence of 2 does, so the
answer is 3.
For a = [2, 4, 3, 5, 1], the output should be firstDuplicate(a) = -1.
My solution -
public class FirstDuplicate {
private static HashMap<Integer, Integer> counts = new HashMap<>();
private static void findSecondIndexFrom(int[] num, int n, int i) {
// given an array, a starting index and a number, find second occurrence of that number beginning from next index
for(int x = i; x < num.length; x++) {
if(num[x] == n) {
// second occurrence found - place in map and terminate
counts.put(n, x);
return;
}
}
}
private static int firstDuplicate(int[] a) {
// for each element in loop, if it's not already in hashmap
// find it's second occurrence in array and place number and index in map
for(int i = 0; i < a.length; i++) {
if(!counts.containsKey(a[i])) {
findSecondIndexFrom(a, a[i], i+1);
}
}
System.out.println(counts);
// if map is empty - no duplicate elements, return -1
if(counts.size() == 0) {
return -1;
}
// else - get array of values from map, sort it, find lowest value and return corresponding key
ArrayList<Integer> values = new ArrayList<>(counts.values());
Collections.sort(values);
int lowest = values.get(0);
//System.out.println(lowest);
for(Map.Entry<Integer, Integer> entries: counts.entrySet()) {
if(entries.getValue() == lowest) {
return entries.getKey();
}
}
return -1;
}
public static void main(String[] args) {
// int[] a = new int[]{2, 3, 3, 1, 5, 2};
//int[] a = new int[]{2, 4, 3, 5, 1};
//int[] a = new int[]{8, 4, 6, 2, 6, 4, 7, 9, 5, 8};
//int[] a = new int[]{1, 1, 2, 2, 1};
int[] a = new int[]{10, 6, 8, 4, 9, 1, 7, 2, 5, 3};
System.out.println(firstDuplicate(a));
}
}
This solution passes only for about 4 of the 11 test cases on CodeFights. However, I manually executed each one of the test cases in my IDE, and each one produces the right result.
I can't figure out why this won't work in CodeFights. Does it have something to do with the use of the static HashMap?
Edited: Since adding and checking if element is present in Set can be done in one step, code can be simplified to:
public static int findDuplicateWithLowestIndex(int... a){
Set<Integer> set = new HashSet<>();
for(int num : a){
if(!set.add(num)){
return num;
}
}
return -1;
}
You're completly right Patrick.
Use this solution: here duplicateIndex should be very large number.
package sample;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Duplicate {
public static Integer secondIndex(Integer[] arr) {
List<Integer> arrlist = new ArrayList<>(Arrays.asList(arr));
int duplicateIndex = 999;
int ele = 0;
for (int i = 0; i < arrlist.size(); i++) {
int secondIndex = getSecondIndex(arrlist, arrlist.get(i));
if (secondIndex >= 0 && duplicateIndex > secondIndex) {
duplicateIndex = secondIndex;
ele = arrlist.get(i);
}
}
return duplicateIndex == 999 ? -1 : ele;
}
public static int getSecondIndex(List<Integer> arr, int ele) {
List<Integer> var0 = new ArrayList<>(arr);
var0.set(var0.indexOf(ele), -1);
return var0.indexOf(ele);
}
public static void main(String[] str) {
// Integer[] arr = new Integer[] { 2, 3, 3, 1, 5, 2 };
// Integer[] arr = new Integer[] { 2, 4, 3, 5, 1 };
// Integer[] arr = new Integer[] { 8, 4, 6, 2, 6, 4, 7, 9, 5, 8 };
// Integer[] arr = new Integer[]{1, 1, 2, 2, 1};
Integer[] arr = new Integer[] { 10, 6, 8, 4, 9, 1, 7, 2, 5, 3 };
System.out.println(secondIndex(arr));
}
}
Solution in Javascript
function solution(a) {
const duplicates = [];
for (const i of a) {
if (duplicates.includes(i))
return i;
else
duplicates.push(i);
}
return -1;
}
console.log(solution([2, 1, 3, 5, 3, 2])); // 3
console.log(solution([2, 2])); // 2
console.log(solution([2, 4, 3, 5, 1])); // -1
I am writing a method that segregates the array of integers so that all the even integers precede all the odd integers in the array. It must take linear time in the size of the array O(n) and operate in place with only a constant amount of extra space.
Input: {2, 4, 7, 6, 1, 3, 5, 4}
Output: 2, 4, 6, 4, 7, 1, 3, 5
Input: {5, 12, 3, 21, 8, 7, 19, 102, 201}
Output: 12, 8, 102, 5, 3, 21, 7, 19, 201
These were my solutions:
private static void segregateArray1(final int[] arr) {
if (arr != null) {
int leftIdx = 0;
int rightIdx = arr.length - 1;
while (leftIdx < rightIdx) {
if (arr[leftIdx] % 2 != 0 && arr[rightIdx] % 2 == 0) {
// swap immediately
int temp = arr[leftIdx];
arr[leftIdx] = arr[rightIdx];
arr[rightIdx] = temp;
leftIdx++;
rightIdx--;
} else {
if (arr[leftIdx] % 2 == 0) {
leftIdx++;
}
if (arr[rightIdx] % 2 == 1) {
rightIdx--;
}
}
}
}
}
Method 1 takes O(n) and does not take up extra space. However, it does not maintain order.
private static int[] segregateArray2(final int[] arr) {
List<Integer> evenArr = new ArrayList<Integer>();
List<Integer> oddArr = new ArrayList<Integer>();
for (int i : arr) {
if (i % 2 == 0) {
evenArr.add(i);
} else {
oddArr.add(i);
}
}
evenArr.addAll(oddArr);
return ArrayUtils.toPrimitive(evenArr.toArray(new Integer[0]));
}
Method 2 creates ArrayList. I am unsure if this is also O(n).
To test:
public static void main(String[] args) {
int[] arr = {2, 4, 7, 6, 1, 3, 5, 4};
segregateArray1(arr);
System.out.println(Arrays.toString(arr));
int[] arr = {2, 4, 7, 6, 1, 3, 5, 4};
// creates another array segragatedArr!
int[] segragatedArr = segregateArray2(arr);
System.out.println(Arrays.toString(segragatedArr));
}
I am not sure if there is a neater solution/simplicity which satisfies time-space complexity (O(n) and space constraint).
The simplest way to do this and keep the same time complexity and also that the size of the output array is the same size as the input array is to do a modulus check on each value and if it is positive that placed to to the front of the array and if negative then to the back. Please keep in mind that you will need two variables to know the next available locations for the positive and negative numbers
ArrayList numberList = new ArrayList<>(Arrays.asList(1,2,3,4,5,6));
numberList.stream().filter(i -> i % 2 == 0).forEach(System.out::println);