简体   繁体   中英

How can I find the second most common character in an array, where in the case of a tie, the second most frequent characters appearing from the left

I have already written a program that can find the most frequent (furthest left) and second most frequent character that is furthest to the right, but I cannot figure out how to make the second most frequent method insist on the first character that appears from the left.

Here's what I need the method to do:

secondMostFrequent("abcdbcd") = 'c'

secondMostFrequent("abbcdbcd") = 'c'

Whereas my method does this:

secondMostFrequent("abcdbcd") = 'd'

mostFrequent("abcdbcd") = 'b'

And here's the code:


public static Character mostFrequent(String s) {   
        String sHold = s;
        Character[] arrayHold = toCharacterArray(sHold);    //toCharacterArray() converts the string into an array of characters

        char element = ' ';
        int count = 0;

        for(int i = 0; i < arrayHold.length; i++){
            char tempElement = arrayHold[i];
            int tempCount = 0;

            for(int j = 0; j < arrayHold.length; j++){
                if(arrayHold[j] == tempElement){
                    tempCount++;

                    if(tempCount > count) {
                        element = tempElement;
                        count = tempCount;
                    }
                }
            }
        }
        return element;
    }


    public static Character[] toCharacterArray(String s ) {      //serves to convert a string into an array of characters

        if (s == null) {
            return null;
        }
        int len = s.length();
        Character[] array = new Character[len];
        for (int i = 0; i < len ; i++) {
            array[i] = s.charAt(i);
        }

        return array;
    }

    public static Character secondMostFrequent(String s){
        String sHold = s;
        Character[] arrayHold = toCharacterArray(sHold);
        char element = ' ';
        char element2 = ' ';
        int count = 0;
        int count2 = 0;

        for(int i = 0; i < arrayHold.length; i++){
            char tempElement = arrayHold[i];
            char tempElement2 = arrayHold[i];
            int tempCount = 0;
            int tempCount2 = 0;


            for(int j = 0; j < arrayHold.length; j++){
                if(arrayHold[j] == tempElement){
                    tempCount++;


                    if(tempCount > count) {
                        element = tempElement;
                        count = tempCount;
                    }
                    else{
                        element2 = tempElement2;
                        count2 = tempCount2;
                    }

                }
            }
        }

        return element2;

    }

        public static void main (String[]args){

            System.out.println("The most common character is: " + "'" + mostFrequent("abcdbcd") + "'"); 

            System.out.println("The second most common character is: " + "'" + secondMostFrequent("abcdbcd") + "'"); 

        }
}



That's the solution to your task.

Time complexity: O(n)

It does two iterations over the given string:

  • first - to create a histogram of frequencies for each character - Map<Character, Integer> hist;
  • second - to find the two most frequent characters according to the histogram.
    public static Character secondMostFrequent(String source) {
        Map<Character, Integer> hist = getHistogram(source);
        char firstChar = 0;
        char secondChar = 0;
        int firstCount = 0;
        int secondCount = 0;

        for (int i = 0; i < source.length(); i++) {
            char next = source.charAt(i);
            if (firstCount == 0 || hist.get(next) > firstCount) {
                secondChar = firstChar;
                secondCount = firstCount;
                firstChar = next;
                firstCount = hist.get(next);
            }
            else if (next != firstChar && 
                    (secondCount == 0 || hist.get(next) > secondCount)) {
                secondChar = next;
                secondCount = hist.get(next);
            }
        }
        return secondChar;
    }

    private static Map<Character, Integer> getHistogram(String source) {
        Map<Character, Integer> hist = new HashMap<>();
        for (int i = 0; i < source.length(); i++) {
            hist.merge(source.charAt(i), 1, Integer::sum);
        }
        return hist;
    }

output

for these calls:

        System.out.println(secondMostFrequent("abcdbcd"));
        System.out.println(secondMostFrequent("abbcdbcd"));
        System.out.println(secondMostFrequent("abcccdeeeaaa")); 

        c
        c
        e

If you've got a function that returns the most common character, you can use it to make a function that returns the second most common character by removing the most frequent character:

public static Character secondMostFrequent(String source) {
    return mostFrequent(source.replaceAll(Character.toString(mostFrequent(source)), ""));
}

Alternatively if you have a working function that returns the second most frequent character from right to left and you want it from left to right, reverse the string before calling the function.

These are admittedly "quick fixes" to get it going. But sometimes, that's all you need.

You should use PriorityQueue to find out nth most frequent character. Just remember every time you insert new element to the priority queue, all existed elements in the queue are sorted according to the given comparator.

public static char findMostFrequent(String str, int num) {
    class Pair {

        final char ch;
        int count;

        public Pair(char ch) {
            this.ch = ch;
        }

    }

    Map<Character, Pair> map = new HashMap<>();

    for (int i = 0; i < str.length(); i++)
        map.computeIfAbsent(str.charAt(i), Pair::new).count++;

    final Comparator<Pair> sortByCountAsc = Comparator.comparingInt(one -> one.count);
    final Comparator<Pair> sortByCharacterAsciiAsc = Comparator.comparingInt(one -> one.ch);
    final Comparator<Pair> sortByMostCommonDesc = sortByCountAsc.reversed().thenComparing(sortByCharacterAsciiAsc);

    Queue<Pair> queue = new PriorityQueue<>(sortByMostCommonDesc);
    queue.addAll(map.values());
    int i = 1;

    while (!queue.isEmpty()) {
        Pair pair = queue.remove();

        if (i++ == num)
            return pair.ch;
    }

    return '\0';
}

Demo:

public static void main(String[] args) {
    System.out.println(findMostFrequent("abbcdbcd", 1));    // b
    System.out.println(findMostFrequent("abcdbcd", 2));     // c
    System.out.println(findMostFrequent("abbcdbcd", 2));    // c
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM