谜题:在一次解析中对包含 0 和 1 的数组进行排序。
Posted
技术标签:
【中文标题】谜题:在一次解析中对包含 0 和 1 的数组进行排序。【英文标题】:Puzzle: Sort an array of 0's and 1's in one parse. 【发布时间】:2013-07-27 21:05:10 【问题描述】:是否可以在不使用辅助数组的情况下在一次解析中按降序排列仅由 1 和 0 组成的数组?
例如:假设您有一个数组a[]=1,0,0,0,1,0,1
,为此预期的输出将是a[]=1,1,1,0,0,0,0
。
我已经编写了下面的 C 代码,但它在 2 次解析中找到了解决方案。可以优化吗?
void arrange(int a[],int n)
int i,count=0;
for(i=0;i<n;i++)
if(a[i]==1)
count++;
a[i]=0;
for(i=0;i<count;i++)
a[i]=1;
【问题讨论】:
您在寻找什么样的优化?一次数据传递? 是的,解决方案应该是一次解析。 Parse 和 pass 是不同的东西,即使它们的发音相同(它们可能是某些人的同音字)。您的代码代表一个解析。 【参考方案1】:for (size_t i = 0, count = 0; i < n; i++)
if (a[i] == 1) a[count++] = 1;
if (i >= count) a[i] = 0;
【讨论】:
如果 a[0] 最初是 1 怎么办? 糟糕。谢谢,吉姆,我会解决这个问题的。 @Inspired 我认为a[count+=a[i]] |= a[i]
将不再需要第一个if
声明。第二个 if
也可以删除,如果我们将循环分成两个,第一个循环遍历 1 直到遇到 0,第二个循环从那里接替。
@unxnut 那么代码将是无分支的,太好了。在我看来,count
的初始值应该是 -1,因为 a[count+=a[i]] 太早增加了count
。
我喜欢这个。这对我来说很不明显,这是正确的:-)【参考方案2】:
让我试试这个:
void arrange(int a[],int n)
int* p = a;
int* q = &a[n-1];
while (p <= q)
while (*p == 1 && p <= q) /* Find a Zero, starting from the front */
++p;
while (*q == 0 && p <= q) /* Find a One, starting from the back */
--q;
if (p < q) /* *p == Zero, and *q == One, and p is to the left of q. */
*p = 1;
*q = 0;
这适用于两个指针,一个从前面开始,另一个从后面开始,它们都向中间移动直到相遇。
一路上,如果两个指针在左边找到一个0,在右边找到一个1,交换值,然后继续。
(代码未经测试,但轮廓看起来很扎实)
【讨论】:
谢谢,我编译好了。它工作正常。当我在参数 n 中发送大小时,我们需要将代码int* q = &a[n]
更改为 int* q = &a[n-1]
好主意,但似乎有点太复杂了:)
它们确实有道理,但在格式化为评论时却没有。我会将我的代码粘贴到答案中,然后将其删除 - 你可以看到。
@JonathanLeffler;您应该为您的代码制作并附上屏幕截图链接(对于
@hacks:它太大而不能成为一个好的屏幕截图,它是对答案的评论(尽管是一个很大的评论),这就是为什么它被删除为非答案的原因。努力达到 10k,你也可以看到它。 :D【参考方案3】:
递归呢?一如既往的简洁优雅。
void countAndRewrite(int arr[], size_t n, size_t *cone, size_t total)
if (n)
if (arr[0])
++*cone;
countAndRewrite(arr + 1, n - 1, cone, total);
arr[0] = total - n < *cone;
int main()
int arr[] = 0, 1, 0, 1, 1, 1, 0 ;
size_t cone = 0;
countAndRewrite(arr, 7, &cone, 7);
for (size_t i = 0; i < 7; i++)
printf("arr[%zu] = %d\n", i, arr[i]);
return 0;
【讨论】:
理解起来有点复杂,我通常避免递归解决方案。无论如何感谢您的帮助:) 优雅,是的,但简单? :) 不错的解决方案。 @Inspired 谢谢!同样,这取决于您的口味 :) 我觉得它很直观。也许我只是忙于实现我的虚拟机,我总是从堆栈的角度思考:P @H2CO3 好吧,对于二叉树:queue.push(root); while (!queue.empty()) node = queue.front(); queue.pop(); if (node!=NULL) process_node(node); queue.push(node->left); queue.push(node->right);
:)
@Inspired 是的,就是这样!【参考方案4】:
试一试!
(读取 cmets):
#include<stdio.h>
int main(void)
int a[]=1,0,0,0,1,0,1;
int n = 7,
i,
index = 0;
while(index < n && a[index]) index++; // skip initial 1's
for(i = index; i < n; i++)
if(a[i]) a[index++] = 1; // if `1` at a[i] make its 0 and
a[i] = 0; // make at index 1.
for(i = 0; i < n; i++)
printf("%3d", a[i]);
return 1;
检查工作代码@ideone的链接:
案例 1: 1,0,0,0,1,0,1
案例 2: 1,0,1,1,1,0,0,1,0,1, 1
案例 3: @987654323 @案例 4: 0, 0, 0, 0, 0, 0, 0
案例 5: 0, 0, 0, 1, 1, 1, 1
所以我认为它是正确的!
很简单,只需要n
迭代。
复杂性明智O(n)
。
【讨论】:
我认为1,0,0,1,1,0,1
会失败让我检查一下
这对于1,1,1,...,1
将失败(超出while
循环中的数组边界)。
所有解决方案(甚至 2 次通过)的时间复杂度为 O(n)
,因为 O(n) = O(2*n)。
@Inspired 是的,它只需要n
迭代甚至没有 2n ..更好一点...不?
@GrijeshChauhan 更好,是的,但仍然是 O(n) 或 O(2*n) 或 O(1000*n)。我只是在强调大O的含义。【参考方案5】:
只是另一种方式。
有两个枢轴,一个从开始,另一个在结束,如下所示,
for(int i=0, j=n-1;i<j;)
if(a[i]==1 && a[j]==0) swap(a[i],a[j]);
else if(a[i]==1 && a[j]==1) j--;
else if(a[i]==0 && a[j]==0) i++;
else if(a[i]==0 && a[j]==1) j--; i++;
【讨论】:
【参考方案6】:#include<iostream>
using namespace std;
int main()
int arr[] = 1, 1, 1, 1, 1, 0, 0, 0;
int N = sizeof(arr) / sizeof(arr[0]);
int p = 0, q = 1;
while (q != N)
if (arr[p] > arr[q])
arr[p] = 0;
arr[q] = 1;
p++;
q++;
else
q++;
if (arr[p] == 0)
p++;
for (int i = 0; i < N; i++)
cout << arr[i];
return 0;
【讨论】:
以上是关于谜题:在一次解析中对包含 0 和 1 的数组进行排序。的主要内容,如果未能解决你的问题,请参考以下文章