7

我有一个字符串,我想计算所有字母和数字的出现次数,并想创建一个图表,以便以图形方式查看出现次数。

例如:

String sentence = "ABC ABC ABC 123"

A (3) * * *
B (3) * * *
C (3) * * *
D
.
.

我的思路:

  1. 计算字符串中的所有数字和字母
  2. 打印所有星号乘以这个数字(不幸的是,我不能将字符串与 Java 中的 int 相乘)

我认为有两种计算字符的方法。我可以使用该charAt()方法或toCharArray()循环遍历字符串或数组并计算字母。

例如:

aCounter = 0;
bCounter = 0;
char ch = sentence.charAt(i);

for (i = 0; i < sentence.length(); ++i) {
    if (ch == 'a') {
        aCounter++;
    }
    if (ch == 'b') {
        bCounter++;
    }
}

但是,我对这种方法有多个问题:

  • 我会做很多反变量 -aCounter通过zCounter0counter通过9counter
  • 我将不得不制作另一个 for 循环来打印星号!

我不是在这里要求一个固定的答案,我只是在寻找一些好的方向,因为我被卡住了。

4

12 回答 12

8

没有必要为此做一个HashTable/HashMap/HashSet

你知道哪些字符被提前跟踪,所以你可以使用一个数组。

我想计算所有字母和数字的出现次数

制作一个要跟踪的字符字符串,然后初始化一个数组。

String sentence = "ABC ABC ABC 123";

//Make a map of all the characters you want to track.
String indexes = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

//Initialize an array to the size of the possible matches.
int[] count = new int[indexes.length()];

//Loop through the sentence looking for matches.
for (int i = 0; i < sentence.length(); i++) {
    //This will get the index in the array, if it's a character we are tracking
    int index = indexes.indexOf(sentence.charAt(i));

    //If it's not a character we are tracking, indexOf returns -1, so skip those.
    if (index < 0)
        continue;

    count[index]++;
}

然后你可以用这个把它们全部打印出来:

for (int i = 0; i < count.length; i++) {
    if (count[i] < 1)
        continue;

    System.out.println(String.format("%s (%d) %s",
            indexes.charAt(i),
            count[i],
            //This little bit of magic creates a string of nul bytes, then replaces it with asterisks.
            new String(new char[count[i]]).replace('\0', '*')));
}

如果您对该位不满意new String(new char[count[i]]).replace('\0', '*')),则可以在尝试输出之前使用 aStringBuilder来构建星号。String您可以在下面查看@mike的示例,这是一个很好的示例。

输出

1 (1) *
2 (1) *
3 (1) *
A (3) ***
B (3) ***
C (3) ***

注意事项

以下是在决定如何解决此问题时需要考虑的一些事项。

  • 您是否总是知道需要提前跟踪哪些角色,或者有时您想跟踪任何角色?在后一种情况下,数组不适合你;您将需要使用像 TreeMap 或 HashMap 这样的高级数据结构。
  • 你会一直计算特定chars 的出现次数,而不是Strings 吗?如果您必须将其修改为 count Strings,那么使用String indexesmap 技巧也不适合您。
  • 您目前是否在课程中学习特定的数据结构?通常,诸如此类的问题会分配给学生,以了解如何应用特定概念。正如@kyle建议的那样,您应该尝试使用您在课堂上学习或已经学习的数据结构。有时使用你还没有学过的结构会给你带来麻烦,或者至少会降低成绩。
于 2013-09-12T14:24:24.043 回答
2

Here an OOP approach, that uses StringReader and a Map. I used TreeMap to have the ouput sorted.

public class StringHistogram
{
  public static void main(String[] args) throws IOException
  {
    Scanner sc = new Scanner(System.in);
    System.out.print("Please insert string: ");
    String s = sc.nextLine();
    sc.close();
    System.out.println(s);

    StringReader r = new StringReader(s);

    Map<Character, Integer> histogram = new TreeMap<Character, Integer>();
    int c;
    while ((c = r.read()) != -1) {
      Integer count = histogram.get((char) c);
      if (count == null)
        count = 0;
      histogram.put((char) c, count + 1);
    }
    r.close();
    for (Entry<Character, Integer> entry : histogram.entrySet())
      System.out.println(entry.getKey() + " (" + entry.getValue()
          + ") " + createAsterisk(entry.getValue()));
  }

  private static String createAsterisk(int number) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < number; i++)
      sb.append("*");
    return sb.toString();
  }
}
于 2013-09-12T15:03:05.243 回答
2

以下是一些帮助您入门的提示:

  1. 不要为每个计数器使用单独的变量。使用数组(或某种集合类型......如果你被教导过......)。

  2. 您可以使用字符作为数组索引。

  3. 在开始打印任何内容之前累积所有计数。

于 2013-09-12T14:26:58.863 回答
2

您可以使用另一种方法,而不是循环一次来计算金额,然后再循环一次来打印星号:

Map<Character,String> results = new HashMap<Character, String>();

然后,每次迭代时,检查地图是否包含角色的数据,如果没有,则初始化它。在伪代码中:

If the map contains data for the key
    Obtain the data for the character
    append a new asterisk
Else
    Create a String with an asterisk
    Append an asterisk
    Put the String with the character as key

如果您需要星号的数量作为数字,您总是可以获得它的大小String(假设您没有放置任何空格)。


更新

作为一项改进,考虑到我与@crush 分享的评论,两个调整可以改进逻辑:

  • StringBuilder而不是String避免不必要的文字创建。
  • TreeMap而不是HashMap它将为地图提供正确的顺序,允许对其内容进行排序打印。

如果有足够的空间(和知识)来证明它们的使用是合理的,则由 OP 添加这些额外的东西。

于 2013-09-12T14:27:27.057 回答
1

创建一个哈希表并遍历字符串,每次将当前字符添加到哈希表中

     String str = "abc abc abc 123";
     Hashtable numbers = new Hashtable();
     int size = str.length();
     for(int i = 0 ; i< size ; i++)
     {
         char curr = str.charAt(i);
         if(numbers.contains(curr) == false)
         {
             numbers.put(curr, 1);
         }
         else
         {
             numbers.put(curr, ((int)numbers.get(curr)) + 1);
         }
     }

     Enumeration names = numbers.keys();
     char c;

     while(names.hasMoreElements()) {
        c = (char) names.nextElement();
        System.out.println(c + ": " +
        numbers.get(c));
     }
于 2013-09-12T14:26:16.160 回答
1

使用数组来存储计数器。可以直接使用 char 作为数组索引,不需要复杂的逻辑。

要打印给定数量的星号,for 循环是最简单的方法。

于 2013-09-12T14:26:52.277 回答
1

由于您是新手并且还没有解决方案(每个人都从这里开始),因此正确的答案是使用您在课堂上学习的数据结构。

如果你正在学习地图

  • TreeMap 排序但键的自然顺序(适合打印)
  • HashMap 没有非常可预测的排序

如果你正在学习数组,这个线程中已经有很好的例子。粉碎的回应

于 2013-09-12T15:18:09.693 回答
0

所以我使用了一个双循环来计算字符数。如果字符在其中一个数组中匹配,则将计数添加到第三个数组中。

for (int i = 0; i < zinArray.length; i++) {
    char c = zinArray[i];
    for (int j = 0; j < controleArray.length; j++) { 
        char d = controleArray[j];      
        if (c == d) {
        letterCount[j]++;
        break; 
        }
    }
}
于 2013-09-14T12:21:46.713 回答
0

将其分为两种方法——一种是在给定一个字符和一个字符串的情况下创建一个字符串“行”,另一种是为 36 个字母数字字符中的每一个调用第一个方法。

public static String alphNum = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";

    public static String count(char c, String str) {
        String stringToReturn = Character.toString(c);
        for(char ch : str.toCharArray()) {
            if (ch == c) {
                stringToReturn += " *";
            }
        }
        return stringToReturn;
    }

    public static void countAndPrintAlphNum(String str) {
        String stringToTest = str.toUpperCase();
        Set<String> rows = new HashSet<String>();
        char[] alphNumArray = alphNum.toCharArray();
        for(char c : alphNumArray) {
            rows.add(count(c, stringToTest));
        }
        for(String row : rows) {
            System.out.println(row);
        }

    }

    public static void main(String[] args) {
        countAndPrintAlphNum("Hi There 123!");
    }

注意:如果要确保以字母数字顺序(数字在前)打印行,请对行使用 TreeSet 而不是 HashSet。

于 2013-09-12T14:43:53.303 回答
0

在这里,最后一个简约而不是面向对象的答案。有效地使用 3 行。它有效,因为字符可以解释为整数。

我有点担心不关闭扫描仪。但是由于 javadoc 说System.in
is already open and ready to supply input data. 我假设资源的关闭也由系统处理。

public class MinimalisticHistogram
{
  public static void main(String[] args)
  {
    int[] occurrences = new int[(int) Math.pow(2, 16)]; // 256 KB
    for (char c : new Scanner(System.in).nextLine().toCharArray()) occurrences[c]++;
    for (int i = 0; i < occurrences.length; i++) if (occurrences[i] != 0) System.out.println(String.format("%c %4s %s", i, "(" + occurrences[i] + ")", new String(new char[occurrences[i]]).replace('\0', '*')));
  }
}
于 2013-09-13T13:11:38.620 回答
0

因为,我们生活在云计算和并行时代。这里有另一种方法。

public class DistributedHistogram
{
  private static final int PORT = 1337;
  private static final String LOOPBACK = "127.0.13.37";

  public static final byte[] DATA = new byte[] {(byte) 0xFF, (byte) 0xFF};
  public static final byte[] STOP = new byte[] {(byte) 0xDE, (byte) 0xAD};

  public static void main(String[] args) throws IOException, InterruptedException
  {
    ExecutorService se = Executors.newSingleThreadExecutor();
    se.submit(new Server(PORT, 16));

    System.out.print("Please insert string: ");
    Scanner s = new Scanner(System.in);
    String input = s.nextLine();
    s.close();
    System.out.println(input);

    ExecutorService ce = Executors.newFixedThreadPool(16);
    List<Future<Void>> futures = new ArrayList<Future<Void>>();
    for (char c : input.toCharArray())
      futures.add(ce.submit(new Client(new Character[]{c}, DATA, LOOPBACK, PORT)));

    /* wait for the clients to complete before we send stop to server */
    for (Future<Void> f : futures)
    {
      try
      {
        @SuppressWarnings ("unused")
        Void v = f.get();
      }
      catch (ExecutionException e)
      {
        //...
      }
    }
    ce.submit(new StopClient(LOOPBACK, PORT)); // sends stop signal

    ce.shutdown();
    se.shutdown();
  }
}

class Client implements Callable<Void>
{
  private final Character[] chars;
  private final String ip;
  private final int port;
  private final byte[] type;

  public Client(Character[] chars, byte[] type, String ip, int port)
  {
    this.chars = chars;
    this.type = type;
    this.ip = ip;
    this.port = port;
  }

  @Override
  public Void call() throws Exception
  {
    Socket s = new Socket(ip, port);
    DataOutputStream out = new DataOutputStream(s.getOutputStream());
    for (Character c : chars) {
      out.write(type);
      out.writeChar(c);
    }
    out.flush();
    out.close();
    s.close();
    return null;
  }
}

class StopClient extends Client {

  public StopClient(String ip, int port)
  {
    super(new Character[]{' '}, DistributedHistogram.STOP, ip, port);
  }
}

class Server implements Callable<Void>
{
  private final int port;
  private ServerSocket ss;
  private final ExecutorService e;

  private final ConcurrentHistogram ch = new ConcurrentHistogram();
  private final AtomicInteger client = new AtomicInteger();

  private AtomicBoolean quit = new AtomicBoolean(false);

  public Server(int port, int clients)
  {
    this.port = port;
    this.e = Executors.newFixedThreadPool(clients);
  }

  public ConcurrentHistogram getHistogram() { return ch; }

  public void stop()
  {
    quit.set(true);
    e.submit(new Callable<Void>()
    {
      @Override
      public Void call() throws Exception
      {
        Thread.sleep(250);
        ss.close();
        return null;
      }
    });
  }

  @Override
  public Void call() throws Exception
  {
    ss = new ServerSocket(port);
    while (!quit.get() && !ss.isClosed())
    {
      try
      {
        e.submit(new ClientHandler(client.getAndIncrement(), ss.accept(), this));
      }
      catch (SocketException se)
      { continue; }
    }
    e.shutdown();
    System.out.println(ch.toString());
    while (!e.isTerminated()) { /* wait */ }
    return null;
  }
}

class ConcurrentHistogram
{
  private final ConcurrentMap<Character, AtomicInteger> histogram = new ConcurrentHashMap<Character, AtomicInteger>();
  private static final String HISTOGRAM_CHAR = "*";

  public ConcurrentMap<Character, AtomicInteger> getHistogram() { return histogram; }

  private String createAsterisk(int number)
  {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < number; i++)
      sb.append(HISTOGRAM_CHAR);
    return sb.toString();
  }

  @Override
  public String toString()
  {
    StringBuilder sb = new StringBuilder();
    List<Entry<Character, AtomicInteger>> data = new ArrayList<Entry<Character, AtomicInteger>>(histogram.entrySet());
    Collections.sort(data, new Comparator<Entry<Character, AtomicInteger>>()
    {
      @Override
      public int compare(Entry<Character, AtomicInteger> o1, Entry<Character, AtomicInteger> o2)
      {
        return o1.getKey().compareTo(o2.getKey());
      }
    });
    for (Entry<Character, AtomicInteger> entry : data)
    {
      int value = entry.getValue().get();
      sb.append(entry.getKey() + " " + String.format("%4s", "(" + value + ")") + " " + createAsterisk(value) + "\n");
    }
    return sb.toString();
  }

  public void addChar(Character c)
  {
    AtomicInteger value = histogram.get(c);
    if (value == null)
    {
      histogram.putIfAbsent(c, new AtomicInteger());
      value = histogram.get(c);
    }
    value.incrementAndGet();
  }
}

class ClientHandler implements Callable<Void>
{
  @SuppressWarnings ("unused")
  private final int client;
  private final Socket s;

  private final Server server;

  public ClientHandler(int client, Socket s, Server server)
  {
    this.client = client;
    this.s = s;
    this.server = server;
  }

  @Override
  public Void call() throws Exception
  {
    DataInputStream in = new DataInputStream(s.getInputStream());
    int c;
    int i = 0;
    byte[] bytes = new byte[2];
    while ((c = in.read()) != -1)
    {
      if (i < 2)
      { bytes[i++] = ((byte) c); }
      else if (Arrays.equals(bytes, DistributedHistogram.DATA))
      {
        i = 0;
        char ch = (char) (((c & 0x00FF) << 8) + (in.read() & 0x00FF));
        server.getHistogram().addChar(ch);
      }
      else if (Arrays.equals(bytes, DistributedHistogram.STOP))
      {
        i = 0;
        server.stop();
      }
      else
      { i = 0; }
    }
    in.close();
    s.close();
    return null;
  }
}
于 2013-09-13T10:12:22.507 回答
0

这就是我用 StringBuffer 做算法的方式

public class StringManipulation {


public static void main(String[] args) {
    int occurrences = 0;
    int count = 0;
    int firstLoc = 0;
    int lastLoc = 0;
    boolean countedMulti = false;

    StringBuffer sb = new StringBuffer();

    String a = new String("ABC ABC ABC 123");
    String lastStrChar = null;

    char tempChar = 'z';

    while (count <= a.length()-1) 
    {
        for (int scanner = 48; scanner <= 90; scanner++) 
        {

            if (a.charAt(count) == scanner) {
                tempChar = (char)scanner;

                for (int i = 0; i <= a.length() - 1; i++) 
                {

                    if (tempChar == a.charAt(i)) {

                        if (count == 0) 
                        {
                            occurrences += 1;

                            sb.append(tempChar);
                        }

                        if (count > 0) {
                            if (a.charAt(count) != a.charAt(count - 1)) 
                            {
                                occurrences += 1;
                            }
                        }
                    }

                    if (count == i + 1) 
                    {
                        sb.append(tempChar);
                        occurrences = 0;
                    }

                    if ((sb.length() - 1) >= 0) 
                    {
                        lastStrChar = sb.substring(sb.length() - 1);

                        firstLoc = sb.indexOf(sb.substring(sb.length() - 1));
                        lastLoc = sb.length() - 1;

                        if (count>0 && sb.lastIndexOf(lastStrChar,firstLoc) != sb.lastIndexOf(lastStrChar, lastLoc)) 
                        {
                            countedMulti = true; //if the index is different for the same character, a duplicate char is found
                        } 
                        else 
                        {                           
                            countedMulti = false;               
                        }
                    }
                }

                if (!countedMulti) 
                {
                    System.out.print(lastStrChar + " appeared " + occurrences + " times\n");    
                }
            }
        }

        count++;
    }
  }
}

输出:

A appeared 3 times
B appeared 3 times
C appeared 3 times
1 appeared 1 times
2 appeared 1 times
3 appeared 1 times
于 2015-11-18T15:45:02.267 回答