安排0& 1是数组

时间:2009-03-25 15:46:27

标签: algorithm language-agnostic

这是我最近的面试问题之一。我想知道别人对这个问题的看法。

问题:

您将获得一个结构,其中包含两个元素int部门和string名称的员工详细信息。

struct Employee
{ 
    string Name;
    int Dept;
}

您将获得N名员工的详细信息,其中N / 2名员工Dept == 0和N / 2名员工Dept == 1,按任意顺序排列。您需要根据其Dept值对员工详细信息进行排序,并且应该为stable,即应保留原始记录中的1和0的顺序。

例如,给出以下样本数据:

Name         Dept

X1           0
X2           1
X3           0
X4           1
X5           0

排序结果后应该是:

Name         Dept

X2           1
X4           1
X1           0
X3           0
X5           0

算法应该是稳定的,时间复杂度应该是O(N),其他变量具有恒定的空间(这意味着应该就地进行排序)。

16 个答案:

答案 0 :(得分:20)

分配第二个数组(O(N))。迭代第一个数组并按照它们出现的顺序将所有1移动到第二个数组。再次迭代并将以相同顺序保留的0移动到第二个数组。所有操作O(N)。这不是原位(就地)解决方案。通过运行Quicksort分区算法一次获得非稳定的原位解决方案。

经过一些研究,似乎没有任何额外记忆的已知O(N)解决方案不稳定。有关有效0-1稳定原位分选(就地)的学术研究,但解决方案需要一些额外的记忆。我想知道原始问题陈述是否没有以精确的方式再现。没有稳定性要求问题很容易;没有现场要求也很容易。满足要求(原位,稳定),解决方案似乎难以捉摸。

在这里的答案中,有一种算法在O(N)中工作并且是原位的,但只有当关键字段是(1)可变的并且(2)可以包含整数而不是单个位时。这可以工作,但不是原位0-1稳定排序,因为假设每个数组元素有可用的O(log N)可写内存。

答案 1 :(得分:13)

哦,这是我的方法。

例如a [] = {1,0,0,0,1,1,1,0,0,1};

<强>伪代码:

  1. 有两个计数器,count1 = 0count2 = (n/2)+1
  2. 遍历数组,

    if(arr[ i ] == 1) 
    { 
        arr[ i ] = count1++;
    } else { 
        arr[ i ] = count2++ 
    };
    
  3. 在遍历结束时,您的数组中填充了数字0到n-1,如:

    a[ ] = { 0, 5, 6, 7, 1, 2, 3, 8, 9 4}
    
  4. 现在问题在于对上面得到的数组进行排序,这可以在O(N)中完成,如下所示:

    for(j = 0; j <= 1; j++)  
    {
        for(i = 0; i<n; i++)  
        {  
            if(arr[ i ] != i)  
            {  
                swap(arr[ i ], arr[ arr[ i ] ]);  
            }  
        }  
    }
    

    注意:j循环只运行两次,与“n”无关,并且具有恒定的复杂性。整个循环的顺序是2 * n = O(n)。

  5. 对数组进行排序后,再次遍历数组并将元素arr[0]设置为arr[n/2]'1',将arr[(n/2)+1]设置为arr[n]'0'

  6. 空间复杂度是恒定的,时间复杂度是O(步骤2)+ O(步骤4)+ O(步骤5)= n + 2n + n = 4 * n = O(n)。

答案 2 :(得分:6)

std::stable_partitionstd::equal_tostd::binder1st一起使用,应该以一种漂亮,功能强大,类似STL的方式进行操作:

using namespace std
stable_partition(&array[0], &array[N], binder1st(equal_to(), 1));

当然,这假设数组的元素有一些比较运算符定义(即你可以说array[i]==1 ...)。如果它们只是整数,维持秩序就没有任何意义......

至于复杂性:为了 O(N)stable_partition需要额外的内存。如果算法无法分配额外的内存,它将在 O(N log N)中执行。

答案 3 :(得分:2)

原始问题文本没有提及除整数之外的任何其他字段(从那时起已经编辑过)。

在这种情况下,稳定性没有意义,因为否则两个相等的数字是无法区分的。解决方案是遍历数组并将1的n / 2次,然后是0的n / 2次。

答案 4 :(得分:2)

为简单起见,使用ints而不是bit,但基本概念是相同的。不是说不同的1和0的顺序最终会出现问题!

var emps = new[] 
           {
               new Employee(X1, 0),
               new Employee(X2, 1),
               new Employee(X3, 0),
               new Employee(X4, 1),
               new Employee(X5, 0),
               new Employee(X6, 1)
           };

var sortedEmps = new Employee[bits.Length];

var oneIndex = 0;
var zeroIndex = bits.Length/2;

foreach (var employee in employees)
{
    if (employee.Dept  == 1)
        sortedEmps[oneIndex++] = employee;
    else
        sortedEmps[zeroIndex++] = employee;
}

更新以解决员工问题。增加了一名额外的员工,因为原始问题说每个人都有N / 2,因此必须有一个偶数才能成为现实。否则它是一样的。

现在不确定这会编译,所以将其视为伪代码!

答案 5 :(得分:1)

这将是radix sort的第一步。

答案 6 :(得分:0)

在抽象术语中,您可以使用修改后的插入排序,将所有零值交换到右侧,将所有值都交换到左侧。然而,这将具有大于O(n)的复杂性。

我有点困惑为什么订购事项,如前所述,字节无法区分。

编辑:新示例有帮助。我的算法的好处,即使它在最坏的情况下比线性时间慢,它在内存使用中只有O(n)。由于1和0只是较大对象的键(可能是任意大的),因此可能需要考虑内存。

答案 7 :(得分:0)

到底是什么,这是整个解决方案: arr是项目列表,item.id为0或1,存储为int。
此代码将0移到前面。

count = { 0:0, 1:len(arr)/2 }
for ii in range(len( arr )):
  id = arr[ii].id
  arr[ii].id = count[id]
  count[id] += 1
for ii in range(len( arr )):
  while arr[ii].id != ii:
    T = arr[ii]
    arr[ii] = arr[arr[ii].id]
    arr[T.id] = T
for ii in range(len( arr )):
  arr[ii].id = (ii >= len(arr)/2)

答案 8 :(得分:0)

我使用简单加权在Perl中实现。

use Modern::Perl;

my @data;
{
  use List::MoreUtils qw'natatime';
  my $iter = natatime 2, qw'
    X1  0
    X2  1
    X3  0
    X4  1
    X5  0
  ';

  while( my($a,$b) = $iter->() ){
    push @data, [$a,$b]
  }
}

my @sorted = sort {
  ($a->[1] <=> $b->[1]) * -2 + # gives more weight to second element
  ($a->[0] cmp $b->[0])
} @data;

say "Name\tDept\n";
say join "\t", @$_ for @sorted;
Name    Dept

X2      1
X4      1
X1      0
X3      0
X5      0

<=>cmp比较的输出值为-1,0,1
通过将其中一个camparisons乘以2,我们得到-2,0,2之一 添加其他运算符的值后,我们得到-3,-2,-1,0,1,2,3

之一

我想知道它会进行多少次比较,所以我添加了一些调试信息并提出了这个。

 a           b        a1      b1     a0     b0
X1,0   ->   X2,1      0   --> 1      X1 <-  X2
X3,0   ->   X4,1      0   --> 1      X3 <-  X4
X2,1  <-    X4,1      1   -   1      X2 <-  X4
X4,1  <-    X1,0      1 <--   0      X4  -> X1
X1,0  <-    X3,0      0   -   0      X1 <-  X3
X2,1 <<-    X5,0      1 <--   0      X2 <-  X5
X5,0   ->>  X4,1      0   --> 1      X5  -> X4
X5,0   ->   X1,0      0   -   0      X5  -> X1
X5,0   ->   X3,0      0   -   0      X5  -> X3

The arrows point at the earlier value.

答案 9 :(得分:0)

这是适用于int数组的解决方案。你可以修改它。

sort (int [] a) {
   int pos = 0;
   for (int i = 1; i < a.length; i++) {
       if (a[i] == 0 && a[pos] == 1) {
           swap(a, pos, i);   // this makes all 0's go to the left.
           pos++;
       }
   } 
}

答案 10 :(得分:0)

#include<stdio.h>
//#include<conio.h>

int main()
{
  int i,a[20]={0};
  int *ptr1,*ptr2;
  //clrscr();
  //a[2]=a[4]=a[6]=a[8]=a[16]=1;
  a[19]=a[18]=1;

  for(i=0;i<20;i++)
    printf("%d",a[i]);

  printf("\n\nafter");

  ptr1=ptr2=a;
  for(i=0;i<20;i++)
  {
    if(*ptr1==0&&*ptr2==1)
    {
      int temp=*ptr1;*ptr1=*ptr2;*ptr2=temp;
      ptr1++;ptr2++;
    }
    else if(*ptr1==1&&*ptr2==0)
    {
      ptr1++;ptr2++;
    }
    else if(*ptr1==0&&*ptr2==0)
    {
      ptr2++;
    }
    else
    {
      if(ptr1<ptr2)
        ptr1++;
      else
      {
        ptr1++;ptr2++;
      }
    }
  }

  for(i=0;i<20;i++)
  {
    printf("%d",a[i]);
  }

 // getch();

  return 0;
}

答案 11 :(得分:0)

可以单程完成,也可以到位。

  1. 取两个变量进行索引。一个将指向其他将指向
    的第0个位置 最后一个元素位置。
  2. 循环,直到索引变量相等或相互交叉。
  3. 从第一个位置搜索值1,从最后一个位置搜索值0
    然后交换两个元素。在一次传递中,我们可以在O(n)时间内对数组进行排序。
  4. 例:     #包括     #define N 6     int main()     {         int list [N] = {1,1,0,0,0,0};         int s,end,tmp;         S = 0;端= N-1;

        while(s less than end)
        {
            if(list[s]==1)
            {
                while(list[end] == 1) 
                   end--;
    
                if(list[end] == 0 && s less than end)
                {
                    tmp = list[s];
                    list[s] = list[end];
                    list[end] = tmp;
                    s++;end--;
                }
            }
            else s++;
        }
        for(s=0;s less than N;s++)
        {
            printf("%d ",list[s]);
        }
        return;
    }
    

答案 12 :(得分:0)

left = 0;
right = n-1;

while(left<right){

while(left < right && array[left]==0){
    left++;
}

while(left < right && array[right]==1){
    right--;
}

while(left<right){
    array[left]=0;
    array[right]=1;
    left++;
    right--;    
}

}

答案 13 :(得分:-1)

易:

function sort(array):
    new_array = new Array(array.length)
    x = 0
    for(i : array): if(i): new_array(x++) = 1
    return new_array

如果你真的想要相同的 1s和0s:

function sort(array):
    new_array = new Array(array.length)
    x, y = 0, array.length / 2
    for(i : array):
        if(i): new_array(x++) = i
        else:  new_array(y++) = i
    return new_array

答案 14 :(得分:-1)

这是我在C#中的(完整)刺伤。它生成列表并将随机员工包括在内,因此无论您喜欢什么,都要numberOfEmployees。我意识到可能有一种更简单的方法可以做到这一点,因为原始海报指出有一定数量的0部门员工作为1-dept员工,但我无法帮助自己。

struct Employee
{
    public Employee(string name, int dept)
    {
        this.name = name;
        this.dept = dept;
    }

    public string name;
    public int dept;
}

class Program
{
    static void Main(string[] args)
    {
        int numberOfEmployees = 100;
        Random r = new Random();

        Employee[] emps = new Employee[numberOfEmployees];
        var empBuf = new Employee[numberOfEmployees];
        int nextAvail = 0;

        // Initialize array of employees with random data
        for (int i = 0; i < numberOfEmployees; i++)
        {
            emps[i] = new Employee("x" + i.ToString(), r.Next(0, 2));
        }

        Console.WriteLine("Old list:");
        foreach (var e in emps)
        {
            Console.WriteLine("Name: {0}, Dept: {1}", e.name, e.dept);
        }

        // throw employees with dept == 1 in first
        for (int i = 0; i < numberOfEmployees; i++)
        {
            if (emps[i].dept == 1)
            {
                empBuf[nextAvail] = emps[i];
                nextAvail++;
            }
        }

        // stick the employees with dept == 0 in last
        for (int i = 0; i < numberOfEmployees; i++)
        {
            if (emps[i].dept == 0)
            {
                empBuf[nextAvail] = emps[i];
                nextAvail++;
            }
        }


        Console.WriteLine("New list:");
        foreach (Employee e in empBuf)
        {
            Console.WriteLine("Name: {0}, Dept: {1}", e.name, e.dept);
        }

    }
}

答案 15 :(得分:-1)

我可以想到一个算法,它将花费O(n + i)时间(最坏情况为O(2n-1)和最佳情况O(n))并且没有额外的空间。我们开始遍历从0到n-1的数组,每次遇到1时我们递增一个计数器并用0替换1。现在当我们到达数组的末尾时,我们将开始向后移动尽可能多的计数器并初始化每个数字都是1。

private static int[] foo(int[] array) {

int counter = 0;
int i;

for(i = 0; i < array.length; i++) {
if (array[i] == 1) {
    array[i] = 0;
    counter++;      
}
}

while(counter > 0) {
array[--i] = 1;
counter--;
}
return array;
}