面试问题:C程序在O(n)中对二进制数组进行排序

时间:2010-01-15 09:45:25

标签: c arrays algorithm sorting

我已经提出了以下程序来实现它,但它似乎无法工作并进入无限循环。它的工作方式类似于快速排序。

int main()
{
 int arr[] = {1,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1};
 int N = 18;
 int *front, *last;

 front = arr;
 last = arr + N;
 while(front <= last)
 {
  while( (front < last) && (*front == 0) )
   front++;

  while( (front < last) && (*last == 1) )
   last--;

  if( front < last)
  {
   int temp = *front;
   *front = *last;
   *last = temp;
   front ++;
   last--;
  }
 }
 for(int i=0;i<N;i++)
  printf("%d ",arr[i]);

 return 0;
}

16 个答案:

答案 0 :(得分:22)

你的意思是阵列只有01 s?

对所有N个元素求和,然后覆盖数组:)

int main() {
    int arr[] = {1,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1};
    int N = sizeof arr / sizeof *arr; /* 18 */
    int sum = 0;
    int ndx;
    for (ndx=0; ndx<N; ndx++) sum += arr[ndx];
    for (ndx=0; ndx<N-sum; ndx++) arr[ndx] = 0;
    for (ndx=N-sum; ndx<N; ndx++) arr[ndx] = 1;
}

答案 1 :(得分:16)

我在程序中看到至少两个问题:

问题1:

last = arr + N;

不正确。它应该是:

last = arr + N - 1;

,因为

(arr + 0) points to 0th ele
(arr + 1) points to 1st ele
...
(arr + N -1) points to (N-1)th ele..which is the last element.


Problem2:
接下来你的while循环:

while(front <= last)

不正确,应该是:

while(front < last)

在你和前一个和最后一个相等的情况下,你的循环继续但是 此时前面和后面都没有被修改,导致无限循环。

当前面和后面变得相等时,没有必要继续你的阵列 本来可以排序。

答案 2 :(得分:5)

您的算法的基本思想很好,并且可以简化实现:

int a[] = {1,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1};

int *begin = a;
int *end = begin + 17;

while (begin < end) {
   if (*begin == 0)
      begin++;
   else if (*end == 1)
      end--;
   else {
      *begin = 0;
      *end = 1;
   }
}

请注意(begin < end)是终止循环的更强条件,并且在每次迭代中只执行一次操作(移动指针或交换值),简化了代码并使其更容易理解循环将真正终止。

答案 3 :(得分:4)

你自己太难了!你可以在O(n)中只知道数组n的大小和元素S的总和。由于二元数组每个元素只有两种可能性,因此知道一个元素的数量和总大小就足够了。

一旦你知道,只需按顺序输出一个包含S - n个零和n个的数组。完成!

另一种不需要先求和并就地工作的方法如下:在索引0处放置一个“写”指针w,在一个位置放置一个“读”指针r指数n-1。使用读指针向后迭代,每次遇到0时,在w处写一个“0”并递增它。当您使用r到达开头时,请使用w使用“1”填充数组的其余部分。

答案 4 :(得分:2)

void my_sort(int* arr, int n){
  int zero = -1;

  for(int i = 0; i < n;i++){
    if(arr[i] == 0){
      zero++; 
      swap(arr[zero],arr[i]);
    }
  }
}

为最后一个第0个索引保留一个数据透视,并保持从左到右交换所有数字,直到到达数组的末尾

答案 5 :(得分:1)

如果你瞄准O(n)忘记所有快速排序(Θ(nlogn)),bubblesorts等。没有经典排序算法实现标准数据集的O(n),你必须利用集合的二进制特性。

 int arr[] = {1,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1};
 int N = 18;
 int i,s=0;
 for(i=0;i<N;i++) s+=(arr[i]==0);
 for(i=0;i<N;i++) arr[i]=!(i<s);

答案 6 :(得分:1)

Overkill,但你可以使用Pang Ko的线性时间后缀数组构造算法:

http://sites.google.com/site/kopangcn2/software2

这会打击采访者的想法:P

答案 7 :(得分:1)

int[] arr = { 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 };

int left = 0;
int right = arr.Length-1;
int first = arr[left];
while (left < right)
{
    if (arr[left] == 0 && arr[right] ==1)
    {
        left++;
        right--;
    }
    else if (arr[left] == 1 && arr[right] == 1)
    {
        right--;
    }
    else if (arr[left] == 0 && arr[right] == 0)
    {
        left++;
    }
    else
    {
        arr[left] = 0;
        arr[right] = 1;
        left++;
        right--;
    }
}

答案 8 :(得分:1)

一般解决方案(如果它不仅仅是0和1)被称为“按计数排序”。如果您知道数据在某个范围内,则始终可以使用它。例如。你想按照他们的出生日期对一群人进行排序,不包括年份。 你只需要创建一个367(闰年)的数组,该数组中的每个插槽都是一个能够保存数据的(链接)列表。 现在,您扫描数据,从同行的生日日期计算“一年中的某一天”,并将它们附加到已批准的列表中。

答案 9 :(得分:0)

您的代码不会在我的系统上无限循环:

# gcc $CFLAGS -o test test.c
# ./test
0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1

然而结果是错误的。我看到8次1,但它应该是9次。

有些人指出,总结是一种更简单的方法:

#include <stdio.h>

int main() 
{
    int i;
    int count;
    int N = 18;
    int arr[] = {1,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1};

    /* Sum up all elements */
    i = 0;
    count = 0;
    while (i < N) count += arr[i++];

    /* Overwrite the array */
    i = 0;
    count = N - count;
    while (i < count) arr[i++] = 0;
    while (i < N) arr[i++] = 1;

    /* Print result */
    for (i = 0; i < N; i++) printf("%d ",arr[i]);
}

答案 10 :(得分:0)

显然另一个问题已经关闭......你的算法完美无缺。我发布的回复maddy的内容dup明显{{3}}这个内容由关闭它的人重定向

int main()
{
  int v[] = {1,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1}; int *a, *b, i, n;
  n = sizeof(v) / sizeof(int);
  for (a = v, b = v + n - 1; a < b; ++a) {
    if (*a) {
      for (; *b; --b) if (a == b) goto end;
      *a = 0; *b-- = 1;
    }
  }
  end: for (i = 0; i < n; ++i) printf("%d%s", v[i], (i==n-1?"\n":",")); return 0;
}

将一些线条移动到一起以使其适合页面....几乎相同

答案 11 :(得分:0)

重新发布此处,因为我回复的问题已经结束(此副本重复)。

我很抱歉用Python回答这个问题,但这是我想要做的练习。代码意味着冗长,以输出算法的步骤。当然,只要你小心移动指针,转换为C并不困难。干杯!

# Put zeros on the left, ones on the right in one pass                                                
a = [1,0,1,0,0,1,1,1,0,0,1,0,0,1,0,0,1,1,0,0,1,1,0,0,1,0,1]
cl = 0
cr = len(a) - 1
print a

while(True):
    if cl + 1 == cr:
        print 'last pass; adjacent elements'
        if a[cl] == 0:
            print 'a[%d] = 0; leave and exit loop' % (cl)
            print 'final array:'
            print a
            break
        if a[cl] == 1:
            print 'a[%d] = 1; try to swap with a[%d]' % (cl, cr)
            if a[cr] == 1:
                print 'a[%d] = 1 as well; leave and exit loop' % (cr)
                print 'final array:'
                print a
                break
            else:
                print 'a[%d] and a[%d] swapped; leave and exit loop' % (cl, cr)
                a[cl] = 0
                a[cr] = 1
                print 'final array:'
                print a
                break
    if a[cl] == 0:
        print 'a[%d] = 0; leave and move on to a[%d]' % (cl,cl+1)
        cl += 1
        continue
    else:
        print 'a[%d] = 1 move to the right' % (cl)
        while(True):
            if a[cr] == 1:
                print 'a[%d] cannot be moved to a[%d], try a[%d]' % (cl, cr, cr-1)
                cr -= 1
                continue
            else:
                print 'a[%d] swapped with a[%d]' % (cl, cr)
                a[cr] = 1
                a[cl] = 0
                cr -= 1
                cl += 1
                print 'next up: a[%d]; right side blocked up to %d' % (cl,cr)
                break
    if (cl + 1) == cr:
        break

示例输出:

[1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1]
a[0] = 1 move to the right
a[0] cannot be moved to a[26], try a[25]
a[0] swapped with a[25]
next up: a[1]; right side blocked up to 24
a[1] = 0; leave and move on to a[2]
a[2] = 1 move to the right
a[2] cannot be moved to a[24], try a[23]
a[2] swapped with a[23]
next up: a[3]; right side blocked up to 22
a[3] = 0; leave and move on to a[4]
a[4] = 0; leave and move on to a[5]
a[5] = 1 move to the right
a[5] swapped with a[22]
next up: a[6]; right side blocked up to 21
a[6] = 1 move to the right
a[6] cannot be moved to a[21], try a[20]
a[6] cannot be moved to a[20], try a[19]
a[6] swapped with a[19]
next up: a[7]; right side blocked up to 18
a[7] = 1 move to the right
a[7] swapped with a[18]
next up: a[8]; right side blocked up to 17
a[8] = 0; leave and move on to a[9]
a[9] = 0; leave and move on to a[10]
a[10] = 1 move to the right
a[10] cannot be moved to a[17], try a[16]
a[10] cannot be moved to a[16], try a[15]
a[10] swapped with a[15]
next up: a[11]; right side blocked up to 14
a[11] = 0; leave and move on to a[12]
a[12] = 0; leave and move on to a[13]
last pass; adjacent elements
a[13] = 1; try to swap with a[14]
a[13] and a[14] swapped; leave and exit loop
final array:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

答案 12 :(得分:0)

这是一个简单的答案:)

int main()
{
    int a[]={1,0,1,1,1,0,1,0,1},size=9,end_value,index1,index2=-1;
    end_value=a[size-1];
    for(index1=0;index1 < size-1;index1++)
    {
        if(a[index1]==end_value )
        {
            index2++;
            a[index2]=!a[index2];
            a[index1]=!a[index1];
        }
    }
    index2++;
    a[index2]=!a[index2];
    a[index1]=!a[index1];
}

答案 13 :(得分:0)

这可以通过简单的二进制counting sort

来完成
#include <stdio.h>

int main()
{
    int N = 18, zeros=0, i;
    int arr[] = {1,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,0,1}, *ptr, *last;

    ptr = arr;
    last = arr + N - 1;
    while (ptr != last)
    {
        if (!*ptr) zeros++;
        ptr++;
    }

    for (i = 0; i < zeros; i++) arr[i] = 0;
    for (; i < N; i++) arr[i] = 1;

    for (i = 0; i < N; i++)
        printf("%d ", arr[i]);
    putchar('\n');

    return 0;
}

答案 14 :(得分:0)

这应该可以正常工作。只有一个循环才能完成这项任务。

int arr[]={0,0,0,1,0,1,0,1,0};
int lastz=7,i=0,temp,n;
n=9;
while(i<n){
        if(arr[i]==0 && i<lastz){
            lastz=i;
        } else if(arr[i]==1 && lastz<i){
            temp=arr[lastz];
            arr[lastz]=arr[i];
            arr[i]=temp;
            lastz++;
        }
        i++;
 }

答案 15 :(得分:0)

这是C实现,它将在O(n)时间内提供解决方案。

/*
C program to sort a binary array in one pass
Input:  0 1 0 1 1 0
OutPut: 0 0 0 1 1 1
*/

#include<stdio.h>
void segregate0and1(int*, int);

int main()
{
    int a[] = {0, 1, 0, 1, 1, 0};
    int array_length = sizeof(a)/sizeof(a[0]);

    segregate0and1(a, array_length);

    printf("Array after segregation: ");
    for (int i = 0; i < array_length; i++)
        printf("%d ", a[i]);
    printf("\n");    

    return 0;
}

void segregate0and1(int a[], int array_length)
{
    int left = 0, right = array_length-1;

    while (left < right)
    {
        /* Increment left index while we see 0 at left */
        while (a[left] == 0 && left < right)
            left++;

        /* Decrement right index while we see 1 at right */
        while (a[right] == 1 && left < right)
            right--;

        /* If left is smaller than right then there is a 1 at left
          and a 0 at right.  Exchange a[left] and a[right]*/
        if (left < right)
        {
            a[left] = 0;
            a[right] = 1;
        }
    }
}