您的位置: 题酷首页 » 所有题目 » 判断n个数是否为全排列


判断n个数是否为全排列


3
3
3 答
1k 看

给一个n长的数组,判断它是否为一个1, 2, ..., n的全排列,要求在线形时间,常数空间内实现.

http://yueweitang.org/bbs/topic/22

面试题算法排列
2009/08/28 by spellscroll 1个月前更新 更新记录



2

提示:1,2,...n和它一个排列组成的二分图总可以分拆成若干个独立的环。

[add] :-),我特意说的算法化一点,看来可能还不如说的数学化一点。这题和104126的解决办法是非常类似的,都可以和symmetric group联系上。简单地说,一个数列的全排列可以写成cycle decomposition的形式,环与环之间不相交,比如这里的f。(可以想一下证明,很简单的,注意f是双射且我们关心的数列是有限长)。这题中我们就是判断数组中的值是不是数组下标的全排列,变换关系是f(i)=a[i],若能拆成几个不相交的环那就对了。symmetric group是最基础的抽象代数知识,随便找本书翻几页就有了,推荐看看。

// This c++ function tests if the numbers stored in a[0..len-1] is a permutation of 1..len and guarantees infected array does not change after invocation.
bool testPerm (int *a, int len) {
    int i, p;
    bool result = true;

    for (i = 0; i < len; ++i) {
        if (a[i] <= 0) return false;
    }
    for (i = 0; i < len; ++i) {
        if (a[i] > 0) {
            p = i;
            while (a[p] >= 1 && a[p] <= len) {
                a[p] = -a[p];
                p = -a[p]-1;
            }
            if (p != i) break;
        }
    }
    for (i = 0; i < len; ++i) {
        if (a[i] > 0)
            result = false;
        else
            a[i] = -a[i];
    }
    return result;
}
1 具体实现是不是从a[0]开始跳,跳过的设置为-1,然后搞个外循环直到n为止?这样的话要修改原来的数组哦,否则常数空间比较难半瓶墨水, 5个月前
1 @半瓶墨水: 你看到的-1其实是为了cope C语言数组下标从0开始的情况。我下面对 @jge: 的回答做了一个解释,可以视为 better explain 吧。Solrex Yang, 5个月前
没看明白,能详细的说说嘛?3ksbriversong, 5个月前
"若能拆成几个不相交的环那就对了" 怎么在常数空间,线性时间做呢?fbird, 5个月前
排版有规律可循么。。。jge, 5个月前
| 显示所有评论(还有7个)
1

我用汉语中数学的语言来说吧,《近世(抽象)代数》书上都会有这样的定理:

每一个置换都可以表示为不相交轮换的乘积。

下面是一个例子,假设我们有这样一个置换 P:

2, 5, 4, 3, 1
1, 2, 3, 4, 5

那么这个置换怎么样表示成轮换呢?我们先从 1 出发,1被轮换到2,2被轮换到5,5又被轮换到1;然后再从3出发,3被轮换到4,4又被轮换到3。也就是说P是两个不相交轮换(1, 2, 5)和(3,4)的乘积。

那么如何检测P是不是一个置换呢?只需要检查P是不是由不相交的轮换构成的即可。

就按照上面的方法,首先从 1 开始,P[1]=2,那么再检查 P[P[1]]=P[2]=5,然后再检查P[P[P[1]]]=P[5]=1。那这个循环怎么终止呢?我们每次检查完一个数,就将它置负,这样P[P[P[P[1]]]]就是负值,那么循环就终止了。如果P[P[P[1]]]=1,那么我们就发现了一个轮换;如果直到循环完了,其结果不等于我们开始的点,那么这就不是一个轮换,说明P不是一个置换。

然后接下来检测第二个轮换。由于检查过的轮换中的数字都被置为负值,所以第二个轮换肯定不会与第一个轮换相交。如果到最后所有的数都被置为负值,那么说明它们都在不相交的轮换里,那么P就是一个置换。

如果不想影响数组的值,到最后把所有置负的元素都重新置正即可。

这个算法只需要将数组扫描一遍,复杂度是O(N)。

1 @beno: 嗯呐,前面jge的回答里有程序,写得比较明白半瓶墨水, 4个月前
不错,修改原来的数组,再修改回来,这样就不影响了半瓶墨水, 5个月前
第二轮是怎么开始的?扫描数组,找到第1个非负整数吗?beno, 4个月前
@半瓶墨水: 你不能说你把数组修改了,再改回来,就没有用中间的线性空间了吧。从算法上来说,这个修改数组的办法等效于使用了一个线性的bit数组。fbird, 4个月前
@fbird: 唉,这么争就是狡辩了,算法上一直以来都是说“额外的空间”。否则对于Max/min函数,因为总要遍历一遍,也算是“使用”了O(n)的空间了吧。。。汗!半瓶墨水, 4个月前
| 显示所有评论(还有4个)
1

为什么要这么麻烦? 这个数组只要没有元素出现不就说明是一个排列了吗?用异或O(n)的时间,O(1)的空间

什么叫“没有元素出现”?是想说重复元素吗?异或是不能保证这一点的。半瓶墨水, 3个月前
哦,是的.不好意思,想错了.ms前面提出的是计数排序的思想. skygram, 3个月前
1 对的! 先让a = 1 xor 2 xor 3 .... n,然后遍历整个数组,各个元素分别和a求异或,最终如果a == 0则是n的全排列。wuxicn, 2个月前
@wuxicn: 这个方法ms不行啊,如:a = 1 xor 2 xor 3 = 0,数组中的元素全为0,就不对了cadence, 1个月前

250x

参与回答

 提示:如不是回答问题,请采用发表评论形式! (比如针对题目或者某个回复的意见、建议)

注册登录后再回答