[英]Sorting algorithm which does not allow counting of elements
我在公司面试中看到过交叉测试这个问题,但我首先不清楚这个问题。 你们能澄清我的疑问吗?
问题:编写程序对仅包含 0、1 和 2 的 integer 数组进行排序。 不允许计算元素,您应该以 O(n) 时间复杂度进行计算。
例如数组:{2, 0, 1, 2, 1, 2, 1, 0, 2, 0}
Output 到一个链表。
遍历整个数组。
高温高压
乐
与其用另一个难以理解的伪代码爆破你,我会给你问题的名称:这个问题被称为荷兰国旗问题(由 Edsgar Dijkstra 首次提出),可以通过三路合并来解决(请参阅解决此问题的第一个答案中的 PHP 代码,尽管效率很低)。
Bentley 和 McIlroy 的开创性论文Engineering a Sort Function中描述了一种更有效的三路合并解决方案。 它使用四个索引来划分中间数组的范围,中间数组的值是未排序的,两边是 1,中间是 0 和 2:
在建立了这个不变量之后, =
部分(即 1)被交换回中间。
这取决于您所说的“不允许计数”是什么意思。
一种简单的方法是创建一个新的空数组,然后查找 0,将它们附加到新数组中。 重复 1 和 2,它在 O(n) 时间内排序。
但这或多或少是一种基数排序。 就像我们在数 0 和 1 和 2 一样,所以我不确定这是否符合您的标准。
编辑:我们可以只用 O(1) 额外的 memory 来做到这一点,方法是为我们的插入点保留一个指针(从数组的开头开始),并在数组中扫描 0,将每个 0 与指针所在的元素交换,并增加指针。 然后重复1,2,它仍然是O(n)。
Java实现:
import java.util.Arrays;
public class Sort
{
public static void main(String[] args)
{
int[] array = {2, 0, 1, 2, 1, 2, 1, 0, 2, 0};
sort(array);
System.out.println(Arrays.toString(array));
}
public static void sort(int[] array)
{
int pointer = 0;
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < array.length; j++)
{
if(array[j] == i)
{
int temp = array[pointer];
array[pointer] = array[j];
array[j] = temp;
pointer++;
}
}
}
}
}
给出 output:
[0, 0, 0, 1, 1, 1, 2, 2, 2, 2]
抱歉,它是 php,但它似乎 O(n) 并且可以很容易地用 java 编写:)
$arr = array(2, 0, 1, 2, 1, 2, 1, 0, 2, 0);
$tmp = array(array(),array(),array());
foreach($arr as $i){
$tmp[$i][] = $i;
}
print_r(array_merge($tmp[0],$tmp[1],$tmp[2]));
在 O(n) 中,伪代码:
def sort (src):
# Create an empty array, and set pointer to its start.
def dest as array[sizeof src]
pto = 0
# For every possible value.
for val in 0, 1, 2:
# Check every position in the source.
for pfrom ranges from 0 to sizeof(src):
# And transfer if matching (includes update of dest pointer).
if src[pfrom] is val:
dest[pto] = val
pto = pto + 1
# Return the new array (or transfer it back to the source if desired).
return dest
这基本上是在源列表上迭代 3 次,如果元素与此遍所需的值匹配,则添加元素。 但它仍然是 O(n)。
等效的 Java 代码将是:
class Test {
public static int [] mySort (int [] src) {
int [] dest = new int[src.length];
int pto = 0;
for (int val = 0; val < 3; val++)
for (int pfrom = 0; pfrom < src.length; pfrom++)
if (src[pfrom] == val)
dest[pto++] = val;
return dest;
}
public static void main(String args[]) {
int [] arr1 = {2, 0, 1, 2, 1, 2, 1, 0, 2, 0};
int [] arr2 = mySort (arr1);
for (int i = 0; i < arr2.length; i++)
System.out.println ("Array[" + i + "] = " + arr2[i]);
}
}
输出:
Array[0] = 0
Array[1] = 0
Array[2] = 0
Array[3] = 1
Array[4] = 1
Array[5] = 1
Array[6] = 2
Array[7] = 2
Array[8] = 2
Array[9] = 2
但说真的,如果潜在雇主给了我这个问题,我会直接提出 state 如果他们愿意,我可以回答这个问题,但正确的答案是只使用Array.sort
。 然后,当且仅当该方法和特定数据集存在性能问题时,您可以调查更快的方法。
尽管要求是什么,但这种更快的方法几乎肯定会涉及计数。 您不会因任意限制而束缚您的开发人员。 需求应该指定需要什么,而不是如何。
如果你这样回答我这个问题,我会当场录用你。
这个答案不计算元素。
因为数组中的值很少,只需计算每种类型的数量,然后使用它来重新填充数组。 我们还利用值从 0 开始连续的事实 - 使其匹配典型的 java int 循环。
public static void main(String[] args) throws Exception
{
Integer[] array = { 2, 0, 1, 2, 1, 2, 1, 0, 2, 0 };
List<Integer>[] elements = new ArrayList[3]; // To store the different element types
// Initialize the array with new lists
for (int i = 0; i < elements.length; i++) elements[i] = new ArrayList<Integer>();
// Populate the lists
for (int i : array) elements[i].add(i);
for (int i = 0, start = 0; i < elements.length; start += elements[i++].size())
System.arraycopy(elements[i].toArray(), 0, array, start, elements[i].size());
System.out.println(Arrays.toString(array));
}
Output:
[0, 0, 0, 1, 1, 1, 2, 2, 2, 2]
Push 和 Pull 具有恒定的复杂性!
将每个元素推入优先级队列
将每个元素拉到索引 0...n
(:
您可以一次性完成,将遇到的每个元素放入最终的 position:
void sort012(int* array, int len) {
int* p0 = array;
int* p2 = array + len;
for (int* p = array; p <= p2; ) {
if (*p == 0) {
std::swap(*p, *p0);
p0++;
p++;
} else if (*p == 2) {
std::swap(*p, *p2);
p2--;
} else {
p++;
}
}
}
因为数组中的值很少,只需计算每种类型的数量,然后使用它来重新填充数组。 我们还利用值从 0 开始连续的事实 - 使其匹配典型的 java int 循环。
整个排序算法只需要三行代码:
public static void main(String[] args)
{
int[] array = { 2, 0, 1, 2, 1, 2, 1, 0, 2, 0 };
// Line 1: Define some space to hold the totals
int[] counts = new int[3]; // To store the (3) different totals
// Line 2: Get the total of each type
for (int i : array) counts[i]++;
// Line 3: Write the appropriate number of each type consecutively back into the array:
for (int i = 0, start = 0; i < counts.length; start += counts[i++]) Arrays.fill(array, start, start + counts[i], i);
System.out.println(Arrays.toString(array));
}
Output:
[0, 0, 0, 1, 1, 1, 2, 2, 2, 2]
我们从来没有提到array.length
,不在乎数组有多长。 它遍历数组只接触每个元素一次,使这个算法 O(n) 符合要求。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.