【问题标题】:Interview question: C program to sort a binary array in O(n)面试题:C程序对O(n)中的二进制数组进行排序
【发布时间】:2011-01-05 10:48:24
【问题描述】:

我想出了以下程序来执行此操作,但它似乎不起作用并进入无限循环。它的工作原理类似于快速排序。

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

【问题讨论】:

  • 为什么不直接将 1 和 0 的数量相加,然后按顺序输出(鸽子排序)?在您的情况下,如果我的计数正确,则 9 个零后跟 9 个。
  • 还有,这里有什么问题?
  • @pmg:鸽子排序(以及更复杂的基数排序)是 O(n)。但是,没有基于比较的排序可以比 O(n log n) 执行得更好。
  • 感谢大家的建议。我当然可以通过按照你们所有人的建议找到总和来做得更好。但如果被要求对 1 和 2 的数组进行排序,我认为我的方法会有所帮助。
  • @Zacky112 如果您被要求对 1 和 2 的数组进行排序,您可以将元素相加减 1 并减少到以前的解决方案。

标签: c arrays algorithm sorting


【解决方案1】:

你的意思是数组只有0s 和1s?

将所有 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;
}

【讨论】:

  • 我认为您的意思是在第三个循环中初始化 ndx=N-sum。
  • 谢谢贾斯汀,解决了错误
  • 确实,这是counting sort的特例
【解决方案2】:

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

问题一:

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.


问题2:
接下来是你的while循环:

while(front <= last)

不正确,应该是:

while(front < last)

在您的情况下,当 front 和 last 相等时,您的循环将继续,但 此时 front 和 last 都没有被修改,导致无限循环。

当 front 和 last 相等时,没有必要继续,你的数组 到那时就已经排序了。

【讨论】:

    【解决方案3】:

    您的算法的基本思想运作良好,实现可以简化:

    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 &lt; end) 是终止循环的更强条件,并且在每次迭代中只执行一个操作(移动指针或交换值),从而简化了代码并更容易理解循环将真的终止。

    【讨论】:

    • 为什么没有得到更多的选票?这个解决方案只遍历循环一次,而计数方法执行两次。
    【解决方案4】:

    你对自己太苛刻了!你可以在 O(n) 中做到这一点,只知道数组 n 的大小和元素的总和 S。由于二进制数组每个元素只有两种可能性,知道一个元素有多少个和总大小就足够了。

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

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

    【讨论】:

    • 哦。当我写这个答案时,我没有看到上面的 roe 评论。感谢他也发现了这里的简单出路。 :)
    【解决方案5】:
    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 个索引保留一个基准,并不断从左到右交换所有数字,直到到达数组的末尾

    【讨论】:

      【解决方案6】:

      如果您的目标是 O(n),忘记所有快速排序 (Θ(nlogn))、冒泡排序等。对于标准数据集,没有经典的排序算法可以达到 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);
      

      【讨论】:

      • 快速排序不在\Theta(n log n)中
      • @swegi:普通的快速排序不会(O(n^2) 最坏的情况),但我认为他的意思是它大于 O(n),平均而言是 O( n log n)
      • 同样,基数排序是 O(n),只要您可以将数字分配给一个值(这对于数值来说是微不足道的),它就可以工作。
      • @roe:你真的相信对这个特定任务使用基数排序(或几乎任何流行的知名排序算法)比应用我的“作弊”更好吗?
      • 好吧,从技术上讲,您的“作弊”是基数排序的一种形式,所以是的.. :) 如果您愿意,也可以使用鸽巢排序。 s 保存零的数量(在您的代码中,s+=arr[i] 将保存一的数量),即是 0 的分类,而 N-s 是隐含的分类。我指的是您的“没有经典算法可以达到 O(n)”,基数排序可以做到。当您对任何类型的数值数据(例如向量、点、颜色值等)进行排序时,这很常见。
      【解决方案7】:

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

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

      这会让面试官大吃一惊:P

      【讨论】:

        【解决方案8】:

        一般的解法(如果不是只有0s和1s的话)叫做“Sorting by Counting”。如果您知道数据在某个范围内,则始终可以使用它。例如。您想按生日(不包括年份)对一组人进行排序。 您只需创建一个包含 367(闰年)天的数组,该数组中的每个插槽都是一个能够保存您的数据的(链接)列表。 现在你扫描你的数据,从同伴的生日日期计算“一年中的哪一天”,并将它们附加到批准的列表中。

        【讨论】:

          【解决方案9】:
          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--;
              }
          }
          

          【讨论】:

            【解决方案10】:

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

            # 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 乘以 1。

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

            #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]);
            }
            

            【讨论】:

              【解决方案11】:

              在此处重新发布,因为我回答的问题已关闭(此问题的重复)。

              我很抱歉使用 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】:

                显然另一个问题已经结束......您的算法运行良好。我在dup 中回复maddy 发布的内容由关闭它的人重定向到这里

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

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

                【讨论】:

                  【解决方案13】:

                  这里有一个简单的答案:)

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

                  【讨论】:

                  • 类似于快速排序中的分区功能。它的时间复杂度是 O(n),而且也很到位。
                  【解决方案14】:

                  这可以通过一个简单的二进制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;
                  }
                  

                  【讨论】:

                    【解决方案15】:

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

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

                    【讨论】:

                      【解决方案16】:

                      这是 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;
                              }
                          }
                      }
                      

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2020-08-14
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多