排序算法,不允许计算元素

时间:2011-07-12 07:41:35

标签: java sorting

我在公司面试中看到了这个问题,但我不清楚这个问题。你能否澄清我的疑问?

  

问题:编写一个程序来对整数数组进行排序,该数组只包含0,1和2。计算不允许的元素,你应该在O(n)时间复杂度下进行。

     

Ex数组:{2,0,1,2,1,2,1,0,2,0}

9 个答案:

答案 0 :(得分:10)

输出到链接列表。

  • 请记住列表的开头。
  • 记住1开始的位置。
  • 请记住列表的结尾。

贯穿整个阵列。

  • 如果遇到0,请将其添加到链接列表的第一个位置。
  • 如果遇到1,请在1的位置后添加。
  • 如果您遇到2,请将其添加到列表末尾。

HTH

答案 1 :(得分:4)

我没有用另一个难以理解的伪代码给你爆炸,而是给你问题的名称:这个问题被称为Dutch national flag problem(由Edsgar Dijkstra首先提出)并且可以通过三方合并(请参阅第一个解决此问题的PHP代码,尽管效率非常低)。

Bentley和McIlroy的开创性论文Engineering a Sort Function中描述了三通合并的更有效的就地解决方案。它使用四个索引来划分中间数组的范围,中间数组的中间值为未排序值,两端为1,中间为0和2:

threeways merge

在建立了这个不变量之后,=部分(即1s)被换回中间。

答案 2 :(得分:3)

这取决于你的意思是“不计算允许”。

执行此操作的一种简单方法是使用新的空数组,然后查找0,将它们附加到新数组。重复1次,然后是2次,并在O(n)时间内进行排序。

但这或多或少是基数排序。这就像我们计算的是0然后是1然后是2,所以我不确定这是否符合你的标准。


编辑:我们只需要O(1)额外内存就可以通过保持指针插入点(从数组的开头开始),并在数组中扫描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++;
                }
            }
        }
    }
}

提供输出:

  

[0,0,0,1,1,1,2,2,2,2]

答案 3 :(得分:1)

对不起,这是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]));

答案 4 :(得分:1)

在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

这基本上是在源列表上迭代三次,如果它们匹配此传递所需的值,则添加元素。但它仍然是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

但严重的是,如果一个潜在的雇主给了我这个问题,我会直截了当地表示如果他们愿意我可以回答这个问题,但正确的答案就是使用{{1 }}。然后,如果,,如果该方法和特定数据集存在性能问题,您可以更快地调查。

这种更快的方式几乎肯定会涉及计数,尽管有这些要求。您不会因任意限制而妨碍您的开发人员。要求应指定所需的,而不是如何。

如果你以这种方式回答我这个问题,我会当场雇用你。

答案 5 :(得分:0)

此答案计算元素。

由于数组中的值太少,只需计算每种类型的数量,并使用它来重新填充数组。我们还利用了值从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));
}

输出:

[0, 0, 0, 1, 1, 1, 2, 2, 2, 2]

答案 6 :(得分:0)

Push and Pull具有持续的复杂性!

  1.   

    将每个元素推送到优先级队列

  2.   

    将每个元素拉到索引0 ... n

  3. (:

答案 7 :(得分:0)

你可以一次完成,将每个遇到的元素放在它的最终位置:

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++;
        }
    }
}

答案 8 :(得分:-4)

由于数组中的值太少,只需 count 每种类型有多少,并使用它来重新填充数组。我们还利用了值从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));
}

输出:

[0, 0, 0, 1, 1, 1, 2, 2, 2, 2]

我们在任何时候都没有提到array.length,也没关心数组的长度。它迭代遍历每个元素触摸一次的数组,使得该算法O(n)成为必需。