我有一个C ++面试问题:
根据学生分数列表,按顺序获得分数的频率。 [使用c ++容器图]
我的想法:将得分列表放入地图中,其中得分为关键,频率为值。在添加密钥之前,请进行搜索。如果某个键是新的,请添加它并将其freq设置为1.如果不是,请按++ 1更新其频率。 O(nlgn)
然后,在新地图中反转键和值,其中将freq设置为键,将其得分设置为值。 O(nlgn)因为地图会自行排序。
记忆:O(n)
效率不高,因为我必须使用2张地图并进行2次排序。
欢迎提出任何意见或建议。
由于
My code
#include <iostream>
#include <map>
#include <algorithm>
#include <time.h>
using namespace std;
const int M =10;
int A[M] ;
bool myFunc(pair<int, int> p1 , pair<double, int> p2)
{
//return p1.second > p2.second;
}
int scoreMap(int *A, const int& N)
{
if (A == NULL)
return 1;
map<int, int> map1;
map<int, int>::iterator itr;
int j = 0 ;
while(j < N)
{
int myKey = (A[j]) ;
itr = map1.find(myKey);
if (itr == map1.end())
{
map1.insert(pair<int, int>(myKey, 1));
}
else
{
++(itr->second);
}
++j;
}
// print map1
cout << "socre \t freq " << endl;
for(itr = map1.begin(); itr != map1.end(); ++itr )
{
cout << itr->first << "\t" << itr->second << endl;
}
// use multimap
multimap<int, int> map2;
multimap<int, int>::iterator itr2;
for (itr = map1.begin() ; itr != map1.end() ; ++itr )
{
map2.insert(pair<int, int>((*itr).second, (*itr).first)) ;
}
// print map2
cout << "after sort " << endl;
cout << "freq \t socre " << endl;
for(itr2 = map2.begin(); itr2 != map2.end(); ++itr2 )
{
cout << (double)(itr2->first)/N << "\t" << itr2->second << endl;
}
return 0;
}
int main()
{
int N = 10; int range=10;
for (int i = 0 ; i < M ; ++i)
{
srand(time(NULL)+rand());
//A[i] = rand()%range + (double)rand()/INT_MAX;
A[i] = rand()%range ; // + (double)rand()/INT_MAX;
// sleep(1);
}
scoreMap(A, M);
return 0;
}
// EOF
时间O(nlgn),空间O(n),是否有更有效的解决方案? 感谢
答案 0 :(得分:3)
假设得分是一个窄整数范围(1-100)
分数累积存储在成对的数组[得分范围]中,具有您的++ [得分]想法。
通过以迭代方式向下移动得分列表来进行频率的提取。 O(N + M)N得分范围+ M个结果/得分数。对结果进行排序。
Sample pseudo:
const size_t MAX_SCORE = 100; // Min is assumed 0.
void scoreFrequencies(int [] scores, size_t N){
pair<int,int> score_counts[MAX_SCORES];
for(size_t i = 0; i < N; i++){
score_counts[i].first++;
score_counts[i].second = i;
}
sort( score_counts, score_counts+MAX_SCORES );
for(size_t score_decreasing = MAX_SCORES-1;
score_counts[score_decreasing].first!=0 && score_decreasing >=0;
score_decreasing--)
cout<< (score_counts.second) <<": " <<
( score_counts.first*1.0/N ) << endl;
}
可以看出,通过这种方法,sort(...)之前的score_counts顺序是不必要的,因此map
无济于事。
答案 1 :(得分:2)
不使用地图,只需使用数组类型std::pair<int, int>
定义所有分数的直方图。一个成员将是分数,另一个成员是频率。最初得分与它们所在的数组索引的值相同,但是当您尝试访问每个特定的得分索引时,您应该只进行初始化,否则您将最终初始化一堆不存在的得分。然后在填写分数的直方图后,根据分数的频率对数组进行排序。由于直方图中的分数基本上就像非常简单的散列查找,因此总体时间应该非常快(每个分数查找和相关频率增量为... O(1),并且排序为O(n log n))
这里有一些代码可以帮助解释:
std::pair<int, int> scores[SCORE_RANGE] = {0}; //zero-out the entire array
//...iterate through your score data
for (int i=0; i < SCORE_DATA_SIZE; i++)
{
int score_val = raw_score_data[i];
if (scores[score_val].first == 0)
{
scores[score_val].first = score_val;
}
scores[score_val].second++;
}
//now sort your scores array based on the frequency which is stored in the second
//member of the std::pair structure
答案 2 :(得分:0)
我的想法:将得分列表放入地图中,其中得分为关键,频率为值。
到目前为止,很好,
在添加密钥之前,请进行搜索。如果某个键是新的,请添加它并将其freq设置为1.如果不是,请按++ 1更新其频率。 O(nlgn)
检查现有的地图条目是不必要的,也是浪费的。该条目将在第一次引用时弹出(具有默认值)。就这样做:
++scoreMap[score];
这不会改变你的大O,但确实会让它变得更快。
然后,在新地图中反转键和值,其中将freq设置为键,将其得分设置为值。 O(nlgn)因为地图会自行排序。
您无法使用新地图,因为分数不一定是唯一的。但您可以使用set<pair>
。
为了纪念Ubuntu 11.10(和g ++ 4.6!)在我的电脑上正确安装,以下是如何使用lambdas来解决这个问题:
map<int, int> scoreMap;
for_each(istream_iterator<int>(cin), istream_iterator<int>(),
[&scoreMap] (int i) { ++scoreMap[i]; } );
set<pair<int,int>> freqMap;
for_each(scoreMap.begin(), scoreMap.end(),
[&freqMap] (const pair<int,int>& p) {
freqMap.insert(make_pair(p.second, p.first));
} );
for_each(freqMap.rbegin(), freqMap.rend(),
[] (const pair<int,int>& p) {
cout << p.second << "/" << p.first << "\n";
} );