Подсчет вхождений целых чисел в массив

Я пишу программу, которая подсчитывает количество вхождений целых чисел, введенных в массив, например, если вы введете 1 1 1 1 2 1 3 5 2 3, программа распечатает различные числа, за которыми следуют их вхождения, например:

1 встречается 5 раз, 2 встречается 2 раза, 3 встречается 2 раза, 5 встречается 1 раз.

И это почти закончено, за исключением одной проблемы, которую я не могу понять:

import java.util.Scanner;
import java.util.Arrays;
public class CountOccurrences
{
   public static void main (String [] args)
   { 

    Scanner scan = new Scanner (System.in);

    final int MAX_NUM = 10;  

    final int MAX_VALUE = 100;

    int [] numList;

    int num;

    int numCount;

    int [] occurrences; 

    int count[];

    String end;

    numList = new int [MAX_NUM];

    occurrences = new int [MAX_NUM];

    count = new int [MAX_NUM];

 do
  {
     System.out.print ("Enter 10 integers between 1 and 100: ");

     for (num = 0; num < MAX_NUM; num++)
     {
        numList[num] = scan.nextInt();
     }

     Arrays.sort(numList);

     count = occurrences (numList); 

     System.out.println();   

     for (num = 0; num < MAX_NUM; num++)
     {
        if (num == 0)
        {
           if (count[num] <= 1)
              System.out.println (numList[num] + " occurs " + count[num] + " time");

           if (count[num] > 1)
              System.out.println (numList[num] + " occurs " + count[num] + " times");
        } 

        if (num > 0 && numList[num] != numList[num - 1])
        {
           if (count[num] <= 1)
              System.out.println (numList[num] + " occurs " + count[num] + " time");

           if (count[num] > 1)
              System.out.println (numList[num] + " occurs " + count[num] + " times");
        }   
     }          

     System.out.print ("\nContinue? <y/n> ");
     end = scan.next(); 

  } while (!end.equalsIgnoreCase("n"));
}


 public static int [] occurrences (int [] list)
 {
      final int MAX_VALUE = 100;

      int num;

      int [] countNum = new int [MAX_VALUE];

      int [] counts = new int [MAX_VALUE];

      for (num = 0; num < list.length; num++)
  {
     counts[num] = countNum[list[num]] += 1;
  }

  return counts;
 } 
}

Проблема, с которой я сталкиваюсь, заключается в том, что независимо от текущего значения для «num», «count» выводит только 1, и проблема не в методе, который подсчитывает вхождения, потому что когда вы вводите числа вместо переменной значение меняется.

Есть ли способ изменить это, чтобы он правильно распечатывал вхождения, или я должен попробовать что-то еще? И чем проще решение, тем лучше, так как я еще не прошел мимо одномерных массивов.

Спасибо за помощь!


person Len    schedule 01.11.2015    source источник
comment
Пожалуйста, используйте это как возможность научиться отладке. Все IDE (eclipse, Netbeans, IntelliJ,..) поставляются с удобными инструментами отладки.   -  person Jayan    schedule 01.11.2015
comment
Проблема в вашей функции вхождения. строка counts[num] = countNum[list[num]] += 1; не делает того, что вы думаете. Он перемещается по массиву, помещая количество чисел в отдельные индексы. Пример с вашим текущим вводом, массив counts имеет значение [1, 2, 3, 4, 5, 1, 2, 1, 2, 1, ... 0]. первый [1, 2, 3, 4, 5 связан с тем, что в вашем массиве есть 5 отсчетов 1, следующий 1, 2 связан с тем, что в вашем массиве есть 2 отсчета 2 и т. д. Вам нужно переписать свою функцию occurrences. Оставил бы это как ответ, но вопрос был закрыт.   -  person Shadow    schedule 01.11.2015


Ответы (5)


Я должен сказать, что мне потребовалось некоторое время, чтобы понять, что представляют собой две переменные count и countNum, возможно, необходимы некоторые комментарии. Но, наконец, я узнаю ошибку.

Предположим, что введите десять чисел: 5, 6, 7, 8, 5, 6, 7, 8, 5, 6

После сортировки numList равно: 5, 5, 5, 6, 6, 6, 7, 7, 8, 8

Массив count, возвращаемый occurrences(), должен быть: [1, 2, 3, 1, 2, 3, 1, 2, 1, 2]

Фактически единственными полезными числами в этом массиве результатов являются:

count[2]: 3     count number for numList[2]: 5
count[5]: 3     count number for numList[5]: 6
count[7]: 2     count number for numList[7]: 7
count[9]: 2     count number for numList[9]: 8

Другие числа, такие как первые два числа 1, 2 перед 3, используются только для постепенного вычисления суммы вхождений, верно? Итак, ваша логика цикла должна быть изменена следующим образом:

  1. удалить первый блок кода if:

    if (num == 0)
    {
       if (count[num] <= 1)
          System.out.println (numList[num] + " occurs " + count[num] + " time");
    
       if (count[num] > 1)
          System.out.println (numList[num] + " occurs " + count[num] + " times");
    } 
    
  2. Измените второе условие if на:

    if ((num + 1) == MAX_NUM || numList[num] != numList[num + 1]) {
        ......
    }
    

После этого ваш код должен работать хорошо, как и ожидалось.

Кстати, вам действительно не нужно делать это так сложно. Просто попробуйте HashMap :)

person JasonZhao    schedule 01.11.2015
comment
Это именно то, что мне было нужно, спасибо! И я обязательно добавлю комментарии в следующий раз, когда у меня возникнет вопрос :) - person Len; 01.11.2015

Попробуйте ХэшМап. Для этого типа задач хэши очень эффективны и быстры.

Я пишу эту функцию, которая берет массив и возвращает HashMap, ключом которой является число, а значением является появление этого числа.

public static HashMap<Integer, Integer> getRepetitions(int[] testCases) {    
    HashMap<Integer, Integer> numberAppearance = new HashMap<Integer, Integer>();

    for(int n: testCases) {
        if(numberAppearance.containsKey(n)) {
            int counter = numberAppearance.get(n);
            counter = counter+1;
            numberAppearance.put(n, counter);
        } else {
            numberAppearance.put(n, 1);
        }
    }
    return numberAppearance;
}

Теперь выполните итерацию по хэш-карте и напечатайте появление таких чисел:

HashMap<Integer, Integer> table = getRepetitions(testCases);

for (int key: table.keySet()) {
        System.out.println(key + " occur " + table.get(key) + " times");
}

Выход:

введите здесь описание изображения

person Irfan    schedule 01.11.2015
comment
Использование HashTable не рекомендуется. Вместо этого используйте HashMap. - person Ghazanfar; 01.11.2015
comment
@MohammadGhazanfar, когда вы говорите обескураженный, что вы имеете в виду? - person smac89; 01.11.2015
comment
@ Smac89 Пожалуйста, прочтите это. As of the Java 2 platform v1.2, this class was retrofitted to ... - person Ghazanfar; 01.11.2015

Я бы использовал Bag, коллекцию, которая подсчитывает, сколько раз элемент появляется в коллекции. Apache Commons имеет его реализацию. Вот их интерфейс, а вот реализация отсортированного дерева.

Вы бы сделали что-то вроде этого:

Bag<Integer> bag = new TreeBag<Integer>();
for (int i = 0; i < numList.length; i++) {
    bag.add(numList[i]);
}
for (int uniqueNumber: bag.uniqueSet()) {
    System.out.println("Number " + uniqueNumber + " counted " + bag.getCount(uniqueNumber) + " times");
}

В приведенном выше примере берутся элементы из вашего массива numList и добавляются к Bag для целей генерации счетчиков, но по умолчанию вам даже не нужен массив. Просто добавьте элементы в Bag напрямую. Что-то типа:

// Make your bag.
Bag<Integer> bag = new TreeBag<Integer>();

...

// Populate your bag.
for (num = 0; num < MAX_NUM; num++) {
    bag.add(scan.nextInt());
}

...

// Print the counts for each unique item in your bag.
for (int uniqueNumber: bag.uniqueSet()) {
    System.out.println("Number " + uniqueNumber + " counted " + bag.getCount(uniqueNumber) + " times");
}
person Andre Gregori    schedule 01.11.2015

Вы можете начать с инициализации массива значений между MIN и MAX. Затем вы можете добавить к каждому элементу массива, когда это значение встречается 1. Что-то типа,

Scanner scan = new Scanner(System.in);
final int MAX_NUM = 10;
final int MAX_VALUE = 100;
final int MIN_VALUE = 1;
final int SIZE = MAX_VALUE - MIN_VALUE;
int[] numList = new int[SIZE];
System.out.printf("Enter %d integers between %d and %d:%n", 
        MAX_NUM, MIN_VALUE, MAX_VALUE);
for (int i = 0; i < MAX_NUM; i++) {
  System.out.printf("Please enter number %d: ", i + 1);
  System.out.flush();
  if (!scan.hasNextInt()) {
    System.out.printf("%s is not an int%n", scan.nextLine());
    i--;
    continue;
  }
  int v = scan.nextInt();
  if (v < MIN_VALUE || v > MAX_VALUE) {
    System.out.printf("%d is not between %d and %d%n", 
            v, MIN_VALUE, MAX_VALUE);
    continue;
  }
  numList[v - MIN_VALUE]++;
}
boolean first = true;
for (int i = 0; i < SIZE; i++) {
  if (numList[i] > 0) {
    if (!first) {
      System.out.print(", ");
    } else {
      first = false;
    }
    if (numList[i] > 1) {
      System.out.printf("%d occurs %d times", 
              i + MIN_VALUE, numList[i]);
    } else {
      System.out.printf("%d occurs once", i + MIN_VALUE);
    }
  }
}
System.out.println();

1См. также сортировка по основанию сортировка подсчетом.

person Elliott Frisch    schedule 01.11.2015

Я думаю, что вы можете значительно упростить свой код, если будете использовать этот подход. Вам все еще нужно изменить, чтобы включить MAX_NUM и MAX_VALUE.

public static void main(String[] args) {

    Integer[] array = {1,2,0,3,4,5,6,6,7,8};
    Stack stack = new Stack();

    Arrays.sort(array, Collections.reverseOrder());

    for(int i : array){
        stack.push(i);
    }

    int currentNumber = Integer.parseInt(stack.pop().toString()) , count = 1;

    try {
        while (stack.size() >= 0) {
            if (currentNumber != Integer.parseInt(stack.peek().toString())) {
                System.out.printf("%d occurs %d times, ", currentNumber, count);
                currentNumber = Integer.parseInt(stack.pop().toString());
                count = 1;
            } else {
                currentNumber = Integer.parseInt(stack.pop().toString());
                count++;
            }
        }
    } catch (EmptyStackException e) {
         System.out.printf("%d occurs %d times.", currentNumber, count);
    }
}
person Alexander    schedule 01.11.2015