I want to go through a map with integer keys that are in the range of 14000-18000. I want to go through them and print the relative difference between them. So if there were three keys 14152 and 14153, 14159, the print output would be 0, 1, 7.
I have put my keys and values into a TreeMap, since it stores things in order.
However, with my implementation:
int dayCounter = 0;
for (Entry<Integer, String> entry : map.entrySet())
{
builder.append(dayCounter);
dayCounter = entry.getKey();
}
I am going through the map but know no way of getting the "previous" entry. If I was using an array I could get the (i-1)th value and subtract from i to get the relative value. Is there any way to get thie functionality with java maps?
simplest and fastest way I think: you can store the last entry in a temp variable, and use it only on next iteration. Don't forget to modify it at the end of each iteration.
When you iterate, you know you have the smallest value first. So I would do something like this:
Integer first = null;
for(Integer i : map.keySet()) {
if(first == null) first = i; // save the first value
builder.append(i - first);
}
Using your example:
import java.util.TreeMap;
class Eggonlegs {
public static void main(String[] args) {
TreeMap<Integer,String> map = new TreeMap<Integer,String>();
map.put(14152,"First");
map.put(14153,"Second");
map.put(14159,"Third");
Integer first = null;
for(Integer i : map.keySet()) {
if(first == null) first = i; // save the first value
System.out.println(i - first);
}
}
}
Results in
c:\files>javac Eggonlegs.java
c:\files>java Eggonlegs
0
1
7
c:\files>
Now, it's possible that's not what you intended. Perhaps you want the difference between each node, which not what your example shows. In that case, I would leverage the Collections library like this:
List<Integer> list = new ArrayList<Integer>(map.keySet());
for(int i = 0; i < list.size(); i++) {
if(i == 0) builder.append(0);
else builder.append(list.get(i) - list.get(i-1));
}
Here's an example of that, incase it's what oyu actually intended:
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;
class Eggonlegs {
public static void main(String[] args) {
TreeMap<Integer,String> map = new TreeMap<Integer,String>();
map.put(14152,"First");
map.put(14153,"Second");
map.put(14159,"Third");
List<Integer> list = new ArrayList<Integer>(map.keySet());
for(int i = 0; i < list.size(); i++) {
if(i == 0) System.out.println(0);
else System.out.println(list.get(i) - list.get(i-1));
}
}
}
Results in
c:\files>javac Eggonlegs.java
c:\files>java Eggonlegs
0
1
6
c:\files>
Why don't you hold it in a separate variable?
//Warning : Notepad coding.
int hold_prev_key = -1;
for(int key : map.keySet())
{
if(hold_prev_key != -1)
builder.append(key - hold_prev_key);
hold_prev_key = key;
}
I didn't explain my question well. Sorry guys. I wanted a cumulative running total. This is what I did by combining your answers:
int hold_previous_key = 0;
int difference = 0;
int running_total = 0;
for (int key : map.keySet())
{
if (hold_previous_key != 0)
{
difference = key - hold_previous_key;
running_total += difference;
builder.append(key + " - " + hold_previous_key + " = " + difference + " RUNNING TOTAL = " + running_total + "; ");
}
else
{
// print 0 for the first date in the data set
builder.append("first val = 0" + "; ");
}
hold_previous_key = key;
}
Related
I have a method which accepts two parameters. 1st is an integer array and 2nd is an integer.
The method should print the index pairs where sum of the values present in those indexes equal to the 2nd input parameter.
The brute force approach is to have two loops which will take O(n^2) time. But I need to solve this in O(n) time.
The array can have repetitions and can have negative numbers also.
If it prints one pair then the reverse pair is not allowed. For example in the below sample if it should not print (4,0), (3,2), (5,3)
int[] arr = {3,4,-1,6,2,-1};
int sum = 5;
The method signature is :findPairs(int[] arr, int sum);
The output of this method will be : (0,4), (2,3), (3,5)
Explanation:
Element present at index 0 + Element present at index 4 = 3+2 = 5
Element present at index 2 + Element present at index 3 = -1+6 = 5
Element present at index 3 + Element present at index 5 = 6+-1 = 5
To clarify some confusions, I have tried using HashMap<Integer, List<Integer>>. Here, key is the elements of array and value is their respective indices. As repetition is allowed, so for a given element there can be multiple index locations. So value in the map is a list.
Your approach is absolutely correct.
Using Map does solve this problem in O(n).
We will capitalise the benefits provided to us by JAVA by making use of TreeMap in this case. (Not necessary to be used).
Also, the problem of repeated pairs of indices in the final answer can be solved using a visited map. This map checks if I have visited the particular index before. If yes, then I will not include it in my answer.
Have a look the implementation below:
import java.io.*;
import java.math.*;
import java.security.*;
import java.text.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.regex.*;
public class Solution {
private static SortedMap<Integer, List<Integer>> map = new TreeMap<Integer, List<Integer>>();
private static final Scanner scanner = new Scanner(System.in);
String findPairs(int[] arr, int sum){
for(int i=0;i<arr.length;i++){
List<Integer> indexList = map.get(arr[i]);
if(indexList == null){
List<Integer> newIndexList = new ArrayList<Integer>();
newIndexList.add(i);
map.put(arr[i], newIndexList);
}else{
indexList.add(i);
}
}
Set s = map.entrySet();
HashMap<Integer, Boolean> visited = new HashMap<Integer, Boolean>();
// Using iterator in SortedMap
Iterator it = s.iterator();
String finalOutput = "";
while (it.hasNext())
{
Map.Entry m = (Map.Entry)it.next();
int key = (Integer)m.getKey();
List<Integer> indexList1 = (List<Integer>)m.getValue();
if(map.containsKey(sum-key)){
List<Integer> indexList2 = (List<Integer>)map.get(sum-key);
for(int i=0;i<indexList1.size();i++){
if(!visited.containsKey(indexList1.get(i))){
for(int j=0;j<indexList2.size();j++){
if(!(finalOutput.equals("") || finalOutput==null)){
finalOutput += ", ";
}
finalOutput += "(" + indexList1.get(i) + "," + indexList2.get(j) + ")";
visited.put(indexList2.get(j), true);
}
visited.put(indexList1.get(i), true);
}
}
}
}
return finalOutput;
}
public static void main(String[] args) throws IOException {
int[] arr = {3,4,-1,6,2,-1};
int sum = 5;
Solution obj = new Solution();
System.out.println(obj.findPairs(arr, sum));
}
}
Kindly feel free to ask doubts.
public HashMap<Integer, ArrayList<List<Integer>>> getPairs(int[] input, int targetSum) {
HashMap<Integer, ArrayList<List<Integer>>> hashMapList = new HashMap<Integer, ArrayList<List<Integer>>>();
for(int i=0;i< input.length;i++) {
//int j=i+1;
for(int j=1;j< input.length;j++) {
//if(j < input.length && input[i] + input[j] == targetSum ) {
if(i != j && input[i] + input[j] == targetSum ) {
ArrayList<List<Integer>> indexPairList = new ArrayList<List<Integer>>();
List<Integer> listPairs = new ArrayList<Integer>();
if( null != hashMapList.get(targetSum)) {
indexPairList = hashMapList.get(targetSum);
for(List<Integer> listPair : indexPairList) {
listPairs.addAll(listPair);
}
}
//System.out.println("listPairs - "+listPairs);
if(!listPairs.contains(i)
|| (!listPairs.contains(j))) {
List<Integer> pair = new ArrayList<Integer>();
pair.add(i);
pair.add(j);
indexPairList.add(pair);
hashMapList.put(targetSum, indexPairList);
}
}
}
}
return hashMapList;
}
I have a question from a quiz :
If input data of randomList are 4 5 1 2 3 4
Results are:
pick(4) -> 4 4
pick(1) -> 1
pick(2) -> 2
pick(6) -> there is no value
These are the default codes, and we're free to place any codes anywhere:
public static void main(String[] args){
List<Integer> randomList = new ArrayList<>();
for(int i = 0; i < 100000000; i++) {
randomList.add(new Random().nextInt());
}
.....
System.out.println("result = " + pick(new Random().nextInt()));
The Question is, what is the most efficient method for function pick() which is better than O(n) ?
This is my version of O(n) :
static List<Integer> list2 = new ArrayList<>();
public static void main(String[] args){
List<Integer> randomList = new ArrayList<>();
for(int i = 0; i < 10; i++) {
randomList.add(new Random().nextInt(5)+1);
}
list2 = randomList;
System.out.println("result = " + pick(new Random().nextInt(5)+1));
}
public static String pick(int rand) {
String result = "";
System.out.println("search = " + rand);
for(Integer s : list2) {
if(s == rand) {
result = result + " " + rand;
}
}
return result;
}
Given your constraints, there is no better searching algorithm besides O(n). The reason for this:
Your data contains "randomized" values between 0 and 100,000,000
You want to collect all values which match a given number (in your example, 4)
You have no ability to sort the list (which would incur an additional O(n*log(n)) overhead)
The only way this could get better is if you could move your data set to a different data structure, such as a Map. Then, you would incur an O(n) penalty for loading the data, but you'd be able to find the values in constant time after that.
If you use a Map in which key is your input value and a value is the frequency then Map will find a key in O(1) time. The string constructing will be proportional to the frequency of a key though. So, the code could be as follows:
Map<Integer, Integer> mapList = new HashMap<>();
public static void main(String[] args){
for(int i = 0; i < 10; i++) {
int key = new Random().nextInt(5)+1;
if (mapList.contains(key)) {
mapList.put(key, mapList.get(key) + 1);
} else {
mapList.put(key, 1);
}
}
System.out.println("result = " + pick(new Random().nextInt(5)+1));
}
public static String pick(int rand) {
Integer count = mapList.get(rand);
if (count == null) {
return "";
}
StringJoiner sj = new StringJoiner(" ");
for (int i = 0; i < count; i++) {
sj.add(rand);
}
return sj.toString();
}
Edit
As suggested by #Pshemo, StringJoiner is used instead of StringBuilder as it's more compact and doesn't add a redundant space for the last character.
Below is the code I have implemented. My doubt here is: when I am trying to print the first biggest and second Biggest values in the string, the output I get is in the order of [second biggest, first biggest].
Here is the output of what I got for the below code:
The output of the map is: real--6
The output of the map is: to--2
The output of the map is: world--1
The output of the map is: hello--0
The list after insertion is: [to, real]
The list inserted as [biggest,secondBiggest] after calling main is: [to, real]
......
but, I want The list after insertion to be: [real, to].
public class ReadString {
static String input = "This is a real project with real code to do real things to solve real problems in real world real";
public static void main(String[] args) {
List<String> lst = ReadString.RepeatedString("This is a real project with real "
+ "code to do real things to solve real " + "problems in real world real");
System.out.println("The list inserted as [biggest,secondBiggest] after calling main is: " + lst);
}
public static List<String> RepeatedString(String s) {
String[] s2 = input.split(" ");
String[] key = { "real", "to", "world", "hello" };
int count = 0;
Integer biggest = 0;
Integer secondBiggest = 1;
Map<String, Integer> map = new HashMap<String, Integer>();
for (int j = 0; j < key.length; j++) {
count = 0;
for (int i = 0; i < s2.length; i++) {
if (s2[i].equals(key[j])) {
count++;
}
}
map.put(key[j], count);
System.out.println("The output of the map is: " +key[j] + "--" + count);
}
/*
* To find the top two most repeated values.
*/
List<Integer> values = new ArrayList<Integer>(map.values());
Collections.sort(values);
for (int n : map.values()) {
if (biggest < n) {
secondBiggest = biggest;
biggest = n;
} else if (secondBiggest < n)
secondBiggest = n;
}
/* To get the top most repeated strings. */
List<String> list = new ArrayList<String>();
for (String s1 : map.keySet()) {
if (map.get(s1).equals(biggest))
list.add(s1);
else if (map.get(s1).equals(secondBiggest))
list.add(s1);
}
System.out.println("The list after insertion is: " +list);
return list;
}
}
The problem appears to be when you are adding items to the list. As you are iterating through the map.keySet(), there is no guarantee that you will get the biggest item first. The smallest change I would make would be to add the biggest item first in the list.
for (String s1 : map.keySet()) {
if (map.get(s1).equals(biggest))
list.add(0, s1);
else if (map.get(s1).equals(secondBiggest))
list.add(s1);
}
This way, if secondBiggest is added first, biggest will be at the top of the list.
We can simplify your approach quite a bit if we extract the word and count into a simple POJO. Something like,
static class WordCount implements Comparable<WordCount> {
String word;
int count;
WordCount(String word, int count) {
this.word = word;
this.count = count;
}
#Override
public int compareTo(WordCount o) {
return Integer.compare(count, o.count);
}
}
Then we can use that in repeatedString. First, count the words in the String; then build a List of WordCount(s). Sort it (since it's Comparable it has natural ordering). Then build the List to return by iterating the sorted List of WordCount(s) in reverse (for two items). Like,
static List<String> repeatedString(String s) {
Map<String, Integer> map = new HashMap<>();
for (String word : s.split("\\s+")) {
map.put(word, !map.containsKey(word) ? 1 : 1 + map.get(word));
}
List<WordCount> al = new ArrayList<>();
for (Map.Entry<String, Integer> entry : map.entrySet()) {
al.add(new WordCount(entry.getKey(), entry.getValue()));
}
Collections.sort(al);
List<String> ret = new ArrayList<>();
for (int i = al.size() - 1; i >= al.size() - 2; i--) {
ret.add(al.get(i).word);
}
return ret;
}
Finally, your main method should use your static input (or static input should be removed)
static String input = "This is a real project with real code to do "
+ "real things to solve real problems in real world real";
public static void main(String[] args) {
List<String> lst = repeatedString(input);
System.out.println("The list inserted as [biggest,"
+ "secondBiggest] after calling main is: " + lst);
}
And I get (as requested)
The list inserted as [biggest,secondBiggest] after calling main is: [real, to]
If you are only concerned about biggest and secondbiggest,
you can refer to the code below.
Instead of creating the list directly, I created an array, added required elements on specified positions. (This way it becomes more readable)
and finally convert the array to a list.
/* To get the top most repeated strings. */
String[] resultArray = new String[2];
for (String s1 : map.keySet()) {
if (map.get(s1).equals(biggest))
resultArray[0]=s1;
else if (map.get(s1).equals(secondBiggest))
resultArray[1]=s1;
}
List<String> list = Arrays.asList(resultArray);
The code below counts how many times the words and letters appeared in the string. How do I sort the output from highest to lowest? The output should be like:
the - 2
quick - 1
brown - 1
fox - 1
t - 2
h - 2
e - 2
b - 1
My code:
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
public class Tokenizer {
public static void main(String[] args) {
int index = 0;
int tokenCount;
int i = 0;
Map<String, Integer> wordCount = new HashMap<String, Integer>();
Map<Integer, Integer> letterCount = new HashMap<Integer, Integer>();
String message = "The Quick brown fox the";
StringTokenizer string = new StringTokenizer(message);
tokenCount = string.countTokens();
System.out.println("Number of tokens = " + tokenCount);
while (string.hasMoreTokens()) {
String word = string.nextToken().toLowerCase();
Integer count = wordCount.get(word);
Integer lettercount = letterCount.get(word);
if (count == null) {
// this means the word was encountered the first time
wordCount.put(word, 1);
} else {
// word was already encountered we need to increment the count
wordCount.put(word, count + 1);
}
}
for (String words : wordCount.keySet()) {
System.out.println("Word : " + words + " has count :" + wordCount.get(words));
}
for (i = 0; i < message.length(); i++) {
char c = message.charAt(i);
if (c != ' ') {
int value = letterCount.getOrDefault((int) c, 0);
letterCount.put((int) c, value + 1);
}
}
for (int key : letterCount.keySet()) {
System.out.println((char) key + ": " + letterCount.get(key));
}
}
}
You have a Map<String, Integer>; I'd suggest something along the lines of another LinkedHashMap<String, Integer> which is populated by inserting keys that are sorted by value.
It seems that you want to sort the Map by it's value (i.e., count). Here are some general solutions.
Specifically for your case, a simple solution might be:
Use a TreeSet<Integer> to save all possible values of counts in the HashMap.
Iterate the TreeSetfrom high to low.
Inside the iteration mentioned in 2., use a loop to output all word-count pairs with count equals to current iterated count.
Please see if this may help.
just use the concept of the list and add all your data into list and then use sort method for it
I have a slight problem with a programme im working on, I need to be able to look through an array in Java and find the number of different duplicates in that array, for example if the array have the values 4, 6, 4 I need to be able to display:
There are:
2 words of length 1 (4 characters)
1 word of length 2 (6 characters)
What I've currently got is -
public class wordLength {
public static void main(String[] args) {
String userInput1 = "Owen Bishop Java ";
String [] inputArray = userInput1.split(" ");
for (int i = 0; i < inputArray.length; i++) {
int length = inputArray[i].length();
int inputArray2 = length;
System.out.println(inputArray2);
}
}
}
This currently will split the string into an array whenever there is a space, and then find and print the length of each of the words in the array, I need to show the amount of words that are the same length.
I'm really new to Java and appreciate this is probably an incredibly easy problem but any help would be hugely appreciated, thanks.
Without writing the whole thing (or making use of 3rd party libraries - I note you're new to Java so let's not complicate things), I would consider the following.
Make use of a Map<Integer,Integer> which would store the number of words of a particular length. e.g. to populate:
Map<Integer, Integer> counts = new HashMap<Integer, Integer>();
for (String word : words) {
Integer current = counts.get(word.length());
if (current == null) {
current = 0;
}
current++;
counts.put(word.length(), current);
}
and then iterate through that to output the number of words per word count. Note that the above makes use of boxing.
The advantage of using a Map is that (unlike your array) you don't need to worry about empty counts (e.g. you won't have an entry if you have no words of length 5). That may/may not be an issue depending on your use case.
You can create an int array of length 20(or the maximum word length in English) and increase the index value before you print that value.
arr[length]++;
public class wordLength {
public static void main(String[] args) {
String userInput1 = "Owen Bishop Java ";
String [] inputArray = userInput1.split(" ");
Map<Integer,Integer> wordLengths = new HashMap<Integer,Integer>();
for (int i = 0; i < inputArray.length; i++) {
int length = inputArray[i].length();
if (wordLengths.containsKey(length))
wordLengths.put(length, wordLengths.get(length) + 1);
else
wordLengths.put(length, 1);
}
for (Integer length : new TreeSet<Integer>(wordLengths.keySet()))
System.out.println("Length: " + length + " Count: " + wordLengths.get(length));
}
}
}
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class wordLength {
public static void main(String[] args) {
String userInput1 = "Owen Bishop Java ";
String [] inputArray = userInput1.split(" ");
HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
for (int i = 0; i < inputArray.length; i++) {
int length = inputArray[i].length();
if(map.get(length)==null){
map.put(length, 1);
}
else map.put(length, map.get(length)+1);
}
Iterator it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pairs = (Map.Entry)it.next();
System.out.println("words of length " +pairs.getKey() + " are " + pairs.getValue());
}
}
}
The output will be:
words of length 4 are 2
words of length 6 are 1
The beginning of your code looks good. What you basically want to keep track of (in my understanding) is a mapping from String length to the number of times this occurred.
public class wordLength {
public static void main(String[] args) {
String userInput1 = "Owen Bishop Java ";
String [] inputArray = userInput1.split(" ");
Map<Integer, Integer> counter = new HashMap<>(); //please note that I use the 'diamond' notation here, this is for Java 1.7 and higher.
for (int i = 0; i < inputArray.length; i++) {
int length = inputArray[i].length();
Integer num = counter.get(length);
if (num == null) {
num = 1;
}
else {
num++;
}
counter.put(length, num);
//or counter.put(length, num == null ? 1 : num++); instead of the if-else
}
//print the results
for (Entry<Integer, Integer> entry : map.entrySet()) {
System.out.println("There are " + entry.getValue() + " words with length " + entry.getKey() + ".");
}
}
}
The previous submitted method of arr[length]++; does work, but uses way to many space. Say you have only words of length 20 and beyond, then the first 20 elements of this arr are useless...
Please also note that you can use the map.entrySet() method from the Map interface. It is a better coding practice to use this method than using map.keySet() and after that looking up the associated value. This saves you much look up time. (especially with large user inputs!!!)
I have 2 solutions for above problem
Using extra space i.e. Map to store unique value. Complexity O(n) + Space(N) [N= #unique value]
No extra space. Sorts the input and counts values. Complexity nLog(n) + n
First Solution
Create a map to store each unique value as key and its count as value
Iterate over input array
If value exists as key then increment counter
ELSE if value does exist in map, then put value and set counter as 1
private static void UniqueValueUsingMap(int[] a){
//Map with
// key: unique values
// Value: count as each value (key)
Map<Integer, Integer> store = new HashMap<Integer, Integer>();
for (int i : a) {
Integer key = Integer.valueOf(i);
Integer count = null;
if(store.containsKey(key)){
count = store.get(key);
}else{
count = 0;
}
count++;
store.put(key, count);
}
}
Second solution
Sort the given Array nlog(n). All same values will be together after sort.
Iterate over sorted array
Maintain 2 variable i.e. previous value and counter for current value
When value change, print the value/count and reset the counter
Note: : int defaults to 0 inital value. Need to add additional check for it.
private static void CountValueUsingSort(int[] a){
//Sort the input array
Arrays.sort(a);
int currentValue = 0;
int counter =0;
for (int curr : a) {
if(curr != currentValue){
System.out.println("Value: " + currentValue + " Count:" + counter);
//Reset counter
counter = 0;
}
currentValue = curr;
//increment Count
counter++;
}
}