-1

I need to print all the letters that appear in the word, rearranged so that the letters that appear most frequently are found at the beginning.

If there are letters that appear the same number of times, the smallest ones will be displayed first after alphabetical sorting.

I've tried something but I'm getting output limit exceed.

How can I use BufferedWriter to make this solution run faster? Should I use HashMap?

Input:intructions

Output:iinnssttcoru

public class Main {
public static void main(String[] args) throws IOException {
String inputString;
BufferedReader reader = new BufferedReader(new 
InputStreamReader(System.in));
inputString = reader.readLine();
if(reader != null)
reader.close();
int[] letterArray = new int[26];
char[] letters = new char[26];
for(int i=0;i<=25;i++)
    letters[i] = (char)(97+i);
for(int i=0; i< inputString.length(); i++)
    letterArray[inputString.charAt(i) - 97] +=1 ;
for(int i=0; i<=24;i++)
    for(int j=i+1;j<=25;j++){
        if(letterArray[i] <letterArray[j] || (letterArray[i] == 
           letterArray[j] && letters[i] > letters[j])){
            int temp = letterArray[i];
            letterArray[i] = letterArray[j];
            letterArray[j] = temp;
            char temp2;
            temp2 = letters[i];
            letters[i] = letters[j];
            letters[j] = temp2;}
    }
  String outputString = new String();
    for(int i=0;i<=25;i++){
        for(int j = 0; j<letterArray[i]; j++)
            outputString += letters[i];}
  System.out.println(outputString);}
  }

My second idea:

public class Main {
public static void main(String[] args) throws IOException {
String line;
BufferedReader reader = new BufferedReader(new 
InputStreamReader(System.in));
line = reader.readLine();

StringBuilder sb = new StringBuilder();
Map<Character, Integer> charCountMap = new HashMap<>();

int len = line.length();

for (int i = 0; i < len; i++) {
    char ch = line.charAt(i);
    charCountMap.put(ch, charCountMap.getOrDefault(ch, 0) + 1);
}
  • 2
    `HashMap` is the most obvious solution for this problem – HomeIsWhereThePcIs Jun 06 '20 at 09:46
  • 1
    `BufferedReader` is used for Input/Output, I don't see how it can help you make your algorithm faster. – Harshal Parekh Jun 06 '20 at 09:48
  • Without streams, I wasn't able to find a solution... – George Chetan Jun 06 '20 at 09:48
  • 1
    @HomeIsWhereThePcIs Or `Map` is you want to support Unicode Code Points from the supplemental planes, e.g. Emojis. – Andreas Jun 06 '20 at 09:49
  • 1
    @GeorgeChetan Which part is stopping you? Building a `Map` of character frequencies, or sorting the map entries by frequency? – Andreas Jun 06 '20 at 09:50
  • @Andreas Both. I've updated my post with my idea of building the map. – George Chetan Jun 06 '20 at 09:59
  • 4
    Also note: read about proper Java code indentation. You should put your { braces at the end of a line, and be consistent about using always the same indentation. Such things matter. They make your code easy to read, or hard to read. – Harshal Parekh Jun 06 '20 at 10:05
  • @HarshalParekh You are right. I was doing that because I wanted to remove empty lines . – George Chetan Jun 06 '20 at 10:08
  • 1
    `getOrDefault` is not Java 7 – Holger Jun 06 '20 at 12:00
  • 1
    @Andreas since you opened the can of worms, [as said recently](https://stackoverflow.com/questions/55129028/java8-create-hashmap-with-character-count-of-a-string/55129227#comment109725512_55129227), even storing codepoints is not sufficient for all logical characters, like emojis… – Holger Jun 06 '20 at 12:46

1 Answers1

2

You may implement a wrapper class implementing Comparable interface:

class CharFreq implements Comparable<CharFreq>{

    final char c;
    int f = 0;

    CharFreq(char cc) {this.c = cc;}

    @Override
    public int compareTo(CharFreq that) {
        return this.f == that.f ? this.c - that.c : that.f - this.f;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        int ff = f;
        while (ff-- > 0) sb.append(c);
        return sb.toString();
    }
}

Then you fill the map and sort the values using quick Collections.sort:

String s = "instructions";
Map<Character, CharFreq> map = new HashMap<>();
for (char c : s.toCharArray()) {
    if (map.get(c) == null) {
        map.put(c, new CharFreq(c));
    }
    CharFreq cf = map.get(c);
    cf.f++;
}

List<CharFreq> list = new ArrayList<>(map.values());
Collections.sort(list);

for (CharFreq cf : list) {
    System.out.print(cf);
}

Output

iinnssttcoru

Or it is possible to use a sorted set without explicit call to Collections.sort:

Set<CharFreq> sorted = new TreeSet<>(map.values());
for (CharFreq cf : sorted) {
    System.out.print(cf);
}
Nowhere Man
  • 19,170
  • 9
  • 17
  • 42
  • 1
    This `return this.f == that.f ? this.c - that.c : that.f - this.f;` is a terrible habit to get into. It is best to take the time and return -1, 1, or 0 as appropriate. Lest the inexperienced will adopt this and ultimately get into trouble. – WJS Jun 06 '20 at 14:27
  • 1
    What kind of trouble is possible in this specific case of subtracting two chars or two non-negative integers? Similar approach is used in [JDK7](https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/lang/String.java#L1175-L1205) and [JDK8](https://github.com/mynawang/Java8-Source-Code/blob/master/src/main/jdk8/java/lang/String.java#L1166-L1183) implementations of `String::compareTo` method. – Nowhere Man Jun 06 '20 at 19:02
  • 1
    In this case, none. It is still considered a bad practice regardless of its use in the JDK. – WJS Jun 06 '20 at 21:27