我有一个未排序的数组(生成的掷骰子),我想选择它的前N个元素(这对于sort-and-snip是微不足道的),但是在保持它们的顺序的同时标记它们。
E.g:
mark([1,2,3,4], 3) ==> [[1, false], [2, true], [3, true], [4, true]]
mark([3,5,2,6,2], 3) ==> [[3, true], [5, true], [2, false], [6, true], [2, false]]
数组可以包含1个以上的任何值,并且可以是任意长度,并且标记元素的数量是可变的。
我可以和
一起生活mark([3,5,2,6,2], 3) ==> [[3, true], [5, true], 2, [6, true], [2, true]]
(即,那些被标记为假的数字没有标记),但我宁愿避免使用它。
强制要求数组的顺序保持不变。
如果重复顶部元素(例如:[6,6,6,6,6,6]的前3个),请标记前3个元素。
(N对于复杂性来说足够小而不重要。)
编辑:加分点:添加一个参数以在“顶部”和“底部”模式之间切换。
答案 0 :(得分:2)
我假设我们在这里谈论PHP,因为问题是标记为PHP。您尝试实现的任何聪明算法都比使用内置函数慢。这就是PHP的工作方式,它不擅长处理用户空间中的数字。
所以你需要做的是对数组的副本进行排序并保存前N个元素的键,然后遍历数组并标记所述元素。但有一个问题:PHP的排序并不稳定。这意味着如果你想在关系中使用元素的位置,你必须自己做。因此,您不必使用asort()
或arsort()
等功能,而是使用array_multisort()
。
结果如下:
function mark(array $arr, $n, $order = SORT_DESC)
{
$keys = $values = $position = array();
$i = 0;
foreach ($arr as $k => $v)
{
$keys[] = $k;
$values[] = $v;
$position[] = $i;
++$i;
}
// sort values in given $order, use their position as tiebreaker (always in ascending order)
array_multisort($values, $order, $position, SORT_ASC, $keys);
$mark = array_flip(array_slice($keys, 0, $n));
$ret = array();
foreach ($arr as $k => $v)
{
$ret[] = array($v, isset($mark[$k]));
}
return $ret;
}
哪个产生
SORT_DESC
[3,6,6,6,6,6,2] => [[3,false],[6,true],[6,true],[6,true],[6,false],[6,false],[2,false]]
[3,5,2,6,2] => [[3,true],[5,true],[2,false],[6,true],[2,false]]
SORT_ASC
[3,6,6,6,6,6,2] => [[3,true],[6,true],[6,false],[6,false],[6,false],[6,false],[2,true]]
[3,5,2,6,2] => [[3,true],[5,false],[2,true],[6,false],[2,true]]
答案 1 :(得分:1)
当前接受的答案扫描输入列表m次。这个只扫描两次。 O(n)vs O(n * m)。您需要堆数据结构。这是在Python中。
import heapq
def mark(data, n):
top = heapq.nlargest(n, ((value, index) for index, value in enumerate(data)))
indexes = set(value[1] for value in top)
return [[value, index in indexes] for index, value in enumerate(data)]
print mark([1, 2, 3, 4], 3)
print mark([3, 5, 2, 6, 2], 3)
输出:
[[1, False], [2, True], [3, True], [4, True]]
[[3, True], [5, True], [2, False], [6, True], [2, False]]
答案 2 :(得分:0)
如果它足够小以致复杂性无关紧要:(伪代码)
for(int m = 0; m < mark_count; m++) {
highest = MIN_INT;
highestindex = -1;
foreach i in array:
if array[i] > highest && is_unmarked(i)
highest = array[i]
highestindex = i;
mark(i)
}
编辑:如果你想找到底部的,请在MAX_INT
开始我们的计数器并检查数组中的值是否小于它。
如果您想要mark()
和is_unmarked
的示例实现:
function mark(i) {
array[i] = [array[i], true];
}
function is_unmarked(i) {
if (array[i] is array & array[i][1] == true)
return false;
return true;
}
(不确定is
是否按预期工作 - 但意思很明确,我希望如此)
答案 3 :(得分:0)
您可以将quick select算法与索引数组一起使用。你不是操纵传入的数组,而是操纵索引的顺序,然后标记顶部的N.这将需要线性时间。
答案 4 :(得分:0)
如果需要,我会以下列方式对其进行优化。如果不需要进行优化,那就按照自己的意愿行事。
所以整体成本是O( N + arr.size())