【问题标题】:Calculate the frequency of characters in a string (Java, Performance)计算字符串中字符的频率(Java,Performance)
【发布时间】:2019-08-07 18:30:15
【问题描述】:

问题

我编写了这个程序来检查每个字母在用户输入的字符串中出现的次数。它工作得很好,但是有没有比为每个字符重复一个 26 元素长的数组更有效或替代的解决方案来完成这项任务?

代码

import java.util.Scanner;
public class Letters {
    public static void main(String[] args) {
        @SuppressWarnings("resource")
        Scanner sc = new Scanner(System.in);
        char[] c = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
        int[] f = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
        System.out.println("Enter a string.");
        String k = sc.nextLine();
        String s = k.toUpperCase();
        s = s.trim();
        int l = s.length();
        System.out.println("Checking string = " + s);
        char ch;
        for (int i = 0; i < l; i++) {
            ch = s.charAt(i);
            for (int j = 0; j < c.length; j++) {
                if (ch == c[j]) {
                    f[j]++;
                }
            }
        }
        System.out.println("Char\tFreq");
        for (int i = 0; i < c.length; i++) {
            if (f[i] != 0) {
                System.out.println(c[i] + "\t" + f[i]);
            }
        }
    }
}

【问题讨论】:

  • 我相信这个问题更适合-> codereview.stackexchange.com
  • 一种简单的方法是使用 Stream API。见here
  • 你可以使用f[ch - 'A']++来避免低效的迭代。
  • @Emma 感谢您的提示。我会这样做的。

标签: java arrays string performance char


【解决方案1】:

您不需要显式初始化频率数组中的 26 个条目(默认值为零);您也不需要保留字符表(知道偏移量就足够了)。也就是说,您的代码可以完全消除c 并计算每个字母;喜欢,

Scanner sc = new Scanner(System.in);
int[] f = new int[26];
System.out.println("Enter a string.");
String orig = sc.nextLine();
String k = orig.trim().toUpperCase();
System.out.println("Checking string = " + orig);
for (char ch : k.toCharArray()) {
    f[ch - 'A']++;
}
System.out.println("Char\tFreq");
for (int i = 0; i < f.length; i++) {
    if (f[i] != 0) {
        System.out.println((char) ('A' + i) + "\t" + f[i]);
    }
}

【讨论】:

  • 在 for (char ch : k.toCharArray()) 行中,我从未见过具有这种构造的 for 循环定义(我的一直是初始化、条件和增量),所以可以请你为我澄清一下?
  • @ArtemisHunter for-each char chchar[] 中由 String k 返回 toCharArray()...
  • @ElliottFrisch 这说明了这一点。谢谢!
【解决方案2】:

通过使用字母 A-Z 在 ASCII 中连续出现这一事实,您可以避免内部循环。因此,您不必在数组c 中搜索字符,只需计算索引即可。见以下代码:

for (int i = 0; i < l; i++) {
    char ch = s.charAt(i);
    if (ch >= 'A' && ch <= 'Z') {
        int j = (int)(ch - 'A'); // j will be in the range [0, 26)
        f[j]++;
    } 
}

我们同样可以去掉数组c

for (int i = 0; i < 26; i++) {
    if (f[i] != 0) {
        System.out.println((char)('A' + i) + "\t" + f[i]);
    }
}

【讨论】:

  • 那么,本质上就是确认字符是一个字母,然后用它的ASCII码来引用对应的索引?对于 (ch - 'A') 的工作,我仍然有些困惑。
  • 是的,就是这个想法。 (ch-'A') 基本上采用 ASCII(实际上是 UTF16,但对于英文字母它们是相同的)并减去 'A' 的 ASCII 给你索引。顺便说一句,@ElliottFrisch 上面的代码不会检查 char 是否为 A-Z,所以如果输入中有非字母字符,它会抛出 ArrayIndexOutOfBoundsException
【解决方案3】:

你可以通过写来找到字符串中字符的频率

String.length() - String.replace("character", "").length();

【讨论】:

    【解决方案4】:

    如果我们有像“romeo@alphacharlie.com”这样的字符串,那么具有 freqArr[c - 'A']++ 的示例将不起作用。你可以试试这个。

    private static int[] decodeFrequency(char[] array) {
        int[] freqArr = new int[127];
        for ( char c : array ) {
            freqArr[(int)c]++;
        }
        return freqArr;
    }
    

    【讨论】:

    【解决方案5】:

    有很多方法可以解决这个问题,我鼓励你遵循一种你容易理解的方法。我下面的解决方案基本上初始化了一个26的数组来计算所有字符的频率。

    重要部分:

    每个字母都有一个 ASCII 值,因此,通过类型转换,您最终会得到整数值,然后减去 65 (对于大写字母),您将获得数组的索引来存储频率各自的性格。

    您修改后的 Java 代码

    import java.util.Scanner;
    public class Letters{
        public static void main(String[] args) {
            @SuppressWarnings("resource")
            Scanner sc = new Scanner(System.in);
            int[] f = new int[26];
            System.out.println("Enter a string.");
            String k = sc.nextLine();
            String s = k.toUpperCase();
            s = s.trim();
            int l = s.length();
            System.out.println("Checking string = " + s);
            char ch;
            for (int i = 0; i < l; i++) {
                ch = s.charAt(i);
    
                //This will give the ASCII value of the character i.e. ch
                int temp=(int)ch;
                if(temp>=65 && temp<=90){
                  //subtract 65 to get index 
                  //add 1 to increase frequency 
                  f[temp - 65]+=1;
                }
                
            }
            System.out.println("Char\tFreq");
            for (int i = 0; i < 26; i++) {
                if (f[i] != 0) {
                    //Add 65 to get respective character
                    System.out.println((char)(i+65) + "\t" + f[i]);
                }
            }
        }
    }
    

    例如:

    f[0] 代表“A”

    f[1] 代表“B”

    ....

    f[25] 代表“Z”

    (记住索引从 0 开始)

    这样你可以消除内部for循环并使用单个整数数组获取频率。

    【讨论】:

    • Java 使用 UTF-16,而不是 ASCII。 (不过,您的算法恰好对所讨论的字符同样有效。)而且,'A' 比 65 等更受欢迎。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-18
    • 2017-04-19
    • 2017-06-12
    • 2011-10-06
    • 1970-01-01
    • 1970-01-01
    • 2023-03-25
    相关资源
    最近更新 更多