Given an unsorted array of n integers which can contain integers from 1 to n. Some elements can be repeated multiple times and some other elements can be absent from the array. Count frequency of all elements that are present and print the missing elements.
Examples:
Input: arr[] = {2, 3, 3, 2, 5}
Output: Below are frequencies of all elements:
1 -> 0
2 -> 2
3 -> 2
4 -> 0
5 -> 1
Input: arr[] = {4, 4, 4, 4}
Output: Below are frequencies of all elements
1 -> 0
2 -> 0
3 -> 0
4 -> 4
对于统计数字出现频率的题目,最直接的思路就是使用一个hashmap,但是使用hashmap需要用到O(n)的辅助存储空间;同时我们注意到数组中数值的范围是1到n,所以就很容易想到Leetcode中的一道题,First Missing Positive的解题思路:扫描数组,并且把A[i]放到A[A[i]-1]的位置上,但是由于我们需要记录每个数值出现的次数,仅仅这样做是不够的,所以我们决定用A[i]记录数值i+1出现的次数,但是由于要将数值出现的次数与未扫描的数值区分开,所以可以用负数来保存出现的次数。另外一个细节是,假设数组A[i] = k,则应该把k放到A[k-1]的位置上,但是A[k-1]的位置上的数值放到哪里去呢?最开始的处理是继续将A[k-1]中的数字也放到正确的位置上,直到一个轮换周期结束;但是这样的处理增加了程序的复杂度和可读性,容易想不清楚怎么写或者出错,所以一个简单的处理方法就是先把A[k-1]中的数字保存在A[i]中,然后先不要修改i,直到A[i]中的数值为负数或者0为止,所以程序如下:
class solution{
public void count(int[] A){
if((A == null)||(A.length == 0)){
return;
}
//preprocess the array, if A[i] == i+1, A[i] = -1; if(A[i] < 1)||(A[i] > A.length) A[i] = 0;
for(int i=0; i<A.length; i++){
if(A[i] == (i+1)){
A[i] = -1;
} else {
if((A[i] < 1)||(A[i] > A.length)){
A[i] = 0;
}
}
}
int i=0;
while(i < A.length){
if(A[i] <= 0){
i++;
} else {
int j = A[i];
if(A[j-1] <= 0){
A[i] = 0;
A[j-1] -= 1;
i++;
} else {
A[i] = A[j-1];
A[j-1] = -1;
}
}
}
}
}