无序数组:删除出现超过k次的元素
相对顺序可以和原数组不同
assert:当 i 指i针指向第 i 个元素的时候,保证前 i -1 个元素都是满足条件的,即,需要保留的。
所以:假如前 i - 1 个元素中有与第 i 个元素相同的,保留第 i 个元素,否则扫描数组,数数。
方法一:
若出现次数小于k次,将所有和当前元素相等的元素都移到数组的末尾,当然,和a[i]相等的只可能在 i 后面出现。
void filter_1(int *a, int& n, int countThreshold)
{
int i = 0;
while (i < n)
{
// assert:第0-i-1个元素都是要保留的元素
int count = 1;
for (int j = 0; j < i; j++)
{
if (a[j] == a[i])
{
count = countThreshold;
break;
}
}
//若i在前面出现过,count=countThreshold-->不会进入这个循环
//数数
for (int j = i + 1; j < n && count < countThreshold; j++)
if (a[j] == a[i])
count++;
// count += a[j] == a[i];
if (count < countThreshold)
//去掉所有的该元素
{
int j = i;
int m = n - 1;//保留当前n的值
n -= cnt;
while (cnt > 0) {
while (a[m] == a[i])m--;
//m指向最后一个不等于a[i]的下标
while (a[j] != a[i])j++;
//寻找下一个等于a[i]的元素
//j指向下一个等于a[i]的元素
a[j] = a[m--];
cnt--;
}
}
else
i++;//否则,检查下一个元素
}
}
方法二:
新建一个大小为 k 的数组
locs[k] = j;表示,当前a[i]这个元素,出现第k次,是在位置j。
比如:当前 i = 3, a[i] = 4, 且第 5,7 个元素都是4,则
locs[0] = 3, locs[1] = 5, locs[2] = 7。
void filter_1B(int *a, int& n, int countThreshold)
{
int *locs = new int[countThreshold];
int i = 0;
while (i < n)
{
// assert: elements in a[0..i-1] appear >= countThreshold times in a[]
// Determine how many times a[i] appears in a[] and save the indexes in locs[].
locs[0] = i;
int count = 1;
for (int j = 0; j < i; j++)
{
if (a[j] == a[i])
{
count = countThreshold;
break;
}
}
for (int j = i + 1; j < n && count < countThreshold; j++)
if (a[j] == a[i])
{
locs[count] = j;
count++;
}
//到此为止与上一种相同
if (count < countThreshold)
{
int t = a[i];
int j = n - 1; // j是最后一个部等于t的元素的下标
for (int k = 0; k < count && locs[k] < j; k++)
{
while (j > locs[k] && a[j] == t)
//1. j是最后一个不等于 a[i] 的
//2. 当前正在移除第k个a[i],所以j应在该元素的后面
//即:locs[k],所以j>locs[k]
j--;//找j
a[locs[k]] = a[j--];
}
n -= count;
}
else
i++;
}
delete[] locs;
}
这两种方法虽然代码比较复杂,但比单纯的两重循环要少一些扫描