【问题标题】:Counting Selection Algorithm计数选择算法
【发布时间】:2012-06-01 08:16:22
【问题描述】:

我已经获得了算法的基本代码,该算法选择未排序数组中的第 k 个最小元素(或排序,我不确定)。通常,我们会为此使用 quickselect,但我们得到了另一个选择,它被标记为“countingselect”作为函数名称。

“计数选择使用类似的方法来计数排序。列表中的项目用作计数数组的索引。然后,从数组的低值端开始,项目计数累加,直到总数超过所需价值。”

// return the kth smallest item
int countingSelect(int items[], int first, int last, int k) {
    int counts[cap];
    for (int c = 0; c < cap; c++) {
        counts[c] = 0;
    }
    for (int i = first; i < last; i++) {
        counts[items[i]] += 1;
    }
    int c = 0;
    while (k >= 0) {
        k -= counts[c++];
    }
    return c-1;
}

我在将其分解为伪代码时遇到了巨大的麻烦,以便我可以准确地理解该函数的工作原理。对于我们得到的代码,我的第一个困惑是“cap”的值是什么,它的功能是什么。上限通常是什么值?我没有得到这些信息。

将算法分解为伪代码是理解它的好方法,我请求一些帮助来分解它并逐步执行代码。

提前谢谢你。

【问题讨论】:

  • cap 是您在items[] + 1 中可以拥有的最大数字。
  • 显然 cap 的值是序列中找到的最高数字的值。对吗?
  • @Andrew,差不多。 cap 是最大的数字 +1,因为数组 counts 的索引是从 0highest_number,包括最大数字本身。
  • "cap" 这里使用的意思是"upper limit", "ceiling"。有了这些知识,自己在纸上运行算法应该会向您解释它是如何工作的。
  • 好的,我会拆开 A4 纸和铅笔。谢谢大家,我可能会回来报告。

标签: algorithm select counting


【解决方案1】:

如果你了解计数排序,这应该很简单。如果不是,那么让我简单介绍一下计数排序的工作原理。

假设您有 10 个数字,范围为 [0, 15]。由于您知道数据的范围,因此您可以检查输入,并标记您看到每个数字的次数。然后你遍历计数并按顺序检索数字:

input numbers
counts[0..15] = 0
for i in numbers
    ++counts[numbers[i]]
for i in counts
    counts[i] times
        print i

我们来看一个例子:

numbers: 1 5 3 13 5 2 0 1 14 2

第一个循环创建计数:

(index) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
counts: 1 2 2 1 0 2 0 0 0 0 0  0  0  1  1  0

第二个循环给你:

1 times 0
2 times 1
2 times 2
1 time 3
0 times 4
2 times 5
0 times 6
...

即:

0 1 1 2 2 3 5 5 13 14

您的算法基本相同,只是它不是输出排序列表,而是查找第 k 个最小的项目。

在上面的例子中,你可以看到:

0th smallest numbers is 0
1st and 2nd smallest numbers are 1
3rd and 4th smallest numbers are 2
5th smallest number is 3
6th and 7th smallest numbers are 5
...

如果你仔细观察,你会看到这种模式:

if      k <= 0 => 0
else if k <= 2 => 1
else if k <= 4 => 2
else if k <= 5 => 3
else if k <= 7 => 5
else if k <= 8 => 13
else if k <= 9 => 14

但是,数字

0 2 4 5 7 8 9

实际上是counts 本身的总和!

所以,你的算法的底部是一个运行总和,除了它检查总和何时大于k。当它出现时,之前的数字就是你的答案。请注意,counts 的索引是数字本身。

int c = 0;
int sum = 0;
while (k >= sum) {
    sum += counts[c++];
}
return c-1;

您的算法试图避免 sum 变量,而是从 k 本身中减去运行总和,这具有相同的效果。

【讨论】:

    【解决方案2】:

    我会假设 cap 是列表中最大的数字(因此您可以分配足够的内存)。

    Counts 是每个数字在列表中出现的次数。例如,count[n] 表示列表中 n 的数量。

    第一个循环初始化计数值。

    第二个循环通过增加计数中的适当位置来调整计数。此循环完成后,counts 是列表中出现的每个数字的计数。例如,count[n] 表示列表中 n 的数量。

    最后一位遍历并遍历列表,将前几个索引的元素相加,直到该数字大于 k。那么之前的数字就是我们超过 k 的地方,所以我们返回那个数字。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-30
      相关资源
      最近更新 更多