Java 不允许元素计数的排序算法

Java 不允许元素计数的排序算法,java,sorting,Java,Sorting,我曾在一次公司面试中见过Across测试这个问题,但我首先不清楚这个问题。你们能澄清我的疑问吗 问题:编写一个程序对只包含0、1和2的整数数组进行排序。计算不允许的元素,您需要在O(n)时间复杂度内完成 Ex数组:{2,0,1,2,1,2,1,0,2,0} 抱歉,这是php,但它似乎是O(n),可以很容易地用java编写:) 这取决于你所说的“不允许计数”是什么意思 一种简单的方法是创建一个新的空数组,然后查找0,将它们附加到新数组中。重复1次,然后重复2次,然后按O(n)时间排序 但这或多或少

我曾在一次公司面试中见过Across测试这个问题,但我首先不清楚这个问题。你们能澄清我的疑问吗

问题:编写一个程序对只包含0、1和2的整数数组进行排序。计算不允许的元素,您需要在O(n)时间复杂度内完成

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


抱歉,这是php,但它似乎是O(n),可以很容易地用java编写:)


这取决于你所说的“不允许计数”是什么意思

一种简单的方法是创建一个新的空数组,然后查找0,将它们附加到新数组中。重复1次,然后重复2次,然后按O(n)时间排序

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


编辑:我们只需要0(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++;
                }
            }
        }
    }
}
导入java.util.array;
公共类排序
{
公共静态void main(字符串[]args)
{
int[]数组={2,0,1,2,1,2,1,0,2,0};
排序(数组);
System.out.println(array.toString(array));
}
公共静态无效排序(int[]数组)
{
int指针=0;
对于(int i=0;i<3;i++)
{
对于(int j=0;j
给出输出:

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


输出到链接列表

  • 记住列表的开头
  • 记住1开始的位置
  • 记住列表的末尾
运行整个阵列

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

O(n)中的Raku,伪代码:

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.sort
。然后,如果且仅当该方法和特定数据集存在性能问题时,您可以研究一种更快的方法

而这种更快的方法几乎肯定会涉及计数,不管要求是什么。你不会用任意的限制束缚你的开发者。需求应该指定需要什么,而不是如何指定


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

因为数组中的值太少了,只需计算每种类型的值的数量,然后用它来重新填充数组。我们还利用了值从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));
}
整个排序算法只需要三行代码:

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));
}
我们从来没有提到过数组。长度,不管数组有多长。它在数组中只接触每个元素一次,使该算法按要求为O(n)。

此答案不计算元素


因为数组中的值太少,所以只需计算每种类型的值的数量,然后使用这些值重新填充数组。我们还利用了值从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…n


  • (:

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

    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++;
            }
        }
    }
    
    void sort012(int*数组,int len){
    int*p0=数组;
    int*p2=数组+len;
    
    对于(int*p=array;p而不是用另一个无法理解的伪代码来攻击您,我将给您问题的名称:这个问题称为(Edsgar Dijkstra首次提出),可以通过三种方式合并来解决(请参阅第一个答案中的PHP代码,它解决了这个问题,尽管效率非常低)

    在宾利和麦基尔罗伊的开创性论文中描述了一种更有效的三路合并解决方案,它使用了四个索引来界定中间数组的范围,中间数组的中间值没有排序,两个边的1s,在两个边之间的0和2s:


    建立该不变量后,
    =
    部分(即1s)你必须计算元素,否则你就不知道什么时候完成了?最适合的排序算法是什么?是的。我也是这么想的。但是想想看,排序通常意味着重新排列,而不是复制到新的数组。当然你也可以这么说将副本复制回原始数组仍会使其为O(n)。添加了一个编辑以使用交换和O(1)额外内存来完成此操作,而不是新的数组。问题被标记为[java].用java来表达怎么样?@Bohemian,我通常对算法比对特定代码翻译更感兴趣,特别是因为这个问题只针对那些不太了解如何评估潜在c
    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]
    
    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++;
            }
        }
    }