第一个版本 复杂度O(m*n)
主要思路
先用一个数组读入所有输入
再遍历该数组
用一个数组记录遍历的数字是否出现过,利用剩余集控制空间
易错点
- 如何控制空间,尤其是超限后如何覆盖
#include <iostream>
using namespace std;
int fun(const int* p, int m, int n) {
int ret = 0;
int capacity[m]; //定义一个数组用来记录读入的数是否出现过
for(int i = 0; i < m; i++)
capacity[i] = -1;
int index = 0;
for(const int* s = p; s < p + n; s++) { //遍历所有读入的数
for(int i = 0; i < m; i++) { //遍历有限的内存空间
if(*s == capacity[i]) { //如果读入的数之前读到过
continue;
}
}
//如果读入的数之前没读过
capacity[index % m] = *s; //这步是这个方法的灵魂,因为内存有限,内存中数不需要考虑顺序,所以利用模的剩余集的知识,即任意数 %m 结果均在0 ~ m-1范围
index++;
ret++;
}
return ret;
}
int main() {
int capacity, VocabularyNumber;
cin >> capacity >> VocabularyNumber;
int article[VocabularyNumber];
for(int i = 0; i < VocabularyNumber; i++)
cin >> article[i];
cout << fun(article, capacity, VocabularyNumber) << endl;
return 0;
}
第二版,涉及到队列思想,复杂度为O(n)
主要思路
- 如果前面n个数字没有重复,那么有限内存中最终留下的是最后输入的m个数字,所以可以想象一大一小两张纸条,“大纸条”里有新数字进来则且“小纸条”已满则“小纸条”向后移动
- haveThisNumberAppear数字的下标记录的是读入的数字,内部存储的值(0或1)表示的是该数字是否在内存中
易错点
- 要先判断数字是否重复
- 只有++count才能把读入的数字放到下一位,count++会覆盖当前位数字
- 将最早进入内存的数字从内存中移除时,应该
#include <iostream>
using namespace std;
int main() {
int m, n;
cin >> m >> n;
int x;
int haveThisNumberAppear[1010] = {0}; //记录读入的数字是否在内存中
int internalStorage[1010]; //有限内存
int count = 0;
for(int i = 0; i < n; i++) {
scanf("%d",&x);
if(haveThisNumberAppear[x] == 1) //先判断这个数字有没有出现过
continue;
else {
if(count < m) { //如果内存没有满
haveThisNumberAppear[x] = 1; //先记录该数字已在内存
internalStorage[++count] = x; //再在下一位记录该数字,注意是下一位!!!若是count++则会覆盖当前位置
}
else { //如果内存满了
haveThisNumberAppear[x] = 1; //先记录该数字已在内存
haveThisNumberAppear[internalStorage[count - m + 1]] = -1; //将最早进入内存的数字从内存中移除,与下一步共同实现了将“小纸条”往后移一位
internalStorage[++count] = x; //再在下一位记录该数字
}
}
}
cout << count << endl;
return 0;
}