面试题整理-最小没出现的整数

题目

#面试题#给定一个无序的整数数组,怎么找到第一个大于0,并且不在此数组的最小整数。比如[1,2,0] 返回 3, [3,4,-1,1] 返回 2。最好能O(1)空间和O(n)时间。

来源http://weibo.com/lirenchen

 

解法:

直接考虑这个问题是比较困难的。不如先换个简单的问题。

 

问题1

给定一个数组,长度为n,除a[0]以外,其他元素都是a[i] == i。那么请找出第一个大于0,且不在此数组中的最小整数。

答案就是:  n + (a[0] == n);

 

问题2

给定一个数组,长度为n,某几个位置的元素满足 a[x] < 0 或者 a[x] > n,余下的元素都满足a[i] == i。那么请找出第一个大于0,且不在此数组中的最小整数。

答:

很明显:当x的值限定为0时。就变成了问题1。

除此之外,这个问题的解也容易求得。

情况1: 首先从1~n开始扫描,当发现i != a[i]时,直接返回i。此时的i必定是最小的未出现的整数。

情况2: 当扫描完1~n之后,那么就回归至问题1了。

for (i = 1; i < n; ++i) {
    if (i != a[i]) return i;
}
return (n + (n == a[0]));


问题3

当给出问题2之后,就需要考虑如何把原题目转换成为问题2了。

实际上要完成的任务就变成了,如何把一个数组中的元素元神归位。也就是让a[i] == i。如果能让这些元素元神归位,那么就转换成为问题2了。就很容易求解了。

算法如下:

       我们从后往前扫描,i = n - 1 to 0;

                  step1如果发现a[i] < 0 || a[i] > n; 则 continue;

                  step2 如果发现a[i] == a[a[i]]; 则continue;

                  step3 如果发现0 < a[i] < n

                          说明需要将a[i]元神归位。也就是放到a[a[i]]上去。

                           swap(a[i], a[a[i]]).

                           再跳转至step2。

 

Note: 需要注意一种有重复数的情况,比如a[11] = 2, a[2] = 2。这时候,就不用进行交换了。直接处理下一个元素。

可以给出代码:

    int i = n, t, temp;
    if (!a || n <= 0) return -1;

    while ((--i) >= 0) {
        while (0 < a[i] && a[i] < n && i != a[i]) {
            t = a[i];
            if (a[i] == a[t]) break;
            temp = a[i];
            a[i] = a[t];
            a[t] = temp;
        }
    }


好吧,到现在为止,原题已经变得很简单了。可以直接给出代码了。

原题解答

int find(int *a, int n) {
    int i = n, t, temp;
    if (!a || n <= 0) return -1;

    while ((--i) >= 0) {
        while (0 < a[i] && a[i] < n && i != a[i]) {
            t = a[i];
            if (a[i] == a[t]) break;
            temp = a[i];
            a[i] = a[t];
            a[t] = temp;
        }
    }

    for (i = 1; i < n; ++i)
        if (a[i] != i) return i;

    return (n + (a[0] == n));
}


算法复杂度分析

这里再加上对算法复杂度的分析。算法的复杂度在下面这段代码看起来。唔~~很难说清楚到底复杂底是多少。

我们不妨回归到问题本身。把一些条件分析清楚:

条件1、对于给定的一个数组。元神归位数目是有限的。假定为k。这个k表示的是,当处理完成之后,a[i] == i的数目。

             k的范围是固定的: 0 <= k < n。

条件2、对于每个元素而言,while (0 < a[i] && a[i] < n && i != a[i]) 这个while 循环里面,每交换一次,就会使得一个元神归位。如果交换了xi次,就会使得xi个元神归位。

             这个是很容易搞清楚的。因为每次交换的效果,都是让某个元神归了位。

             比如a[11] = 5, a[5] = 2, a[2] = 0;

             第一次交换: a[11] = 2, a[5] = 5, a[2] = 0;

             第二次交换: a[11] = 0, a[5] = 5, a[2] = 2;

条件3、对于整个数组而言,元神归位的数目K应该是满足:

             x0 + x1 + x2 + x3 + ..... + xn-1 = K

             这也就是说交换K次。

             注意啊: 不是每个i都会交换K次。而是所有的i交换次数的总和,应该是K次。

 

可以想象一种极端的情况,比如,当i=n-1的时候,置换次数最多。刚好把所有的元神都归位了。那么从i = n - 2开始,就再也不会进入到里面的while循环中去了。

再例如:如果i=n-1的时候,使得2个元神归了位。那么从 i = n-2 ~ 0,则只需要让k-2个元神归位了。因为已经归位的,是不用再去处理的。

 

想到这里,你应该明白了,下面这段代码的复杂度是O(n) + O(K)。由于0<=K < n。所以复杂度是O(n)。也就是线性时间完成了任务

 

    while ((--i) >= 0) {
        while (0 < a[i] && a[i] < n && i != a[i]) {
            t = a[i];
            if (a[i] == a[t]) break;
            temp = a[i];
            a[i] = a[t];
            a[t] = temp;
        }
    }


 

 

 

 

昨日,11.19,最新整理了,第61-80题,现在公布上传。 另加上之前公布的第1-60 题,在此做一次汇总上传,以飨各位。 可以这么说,绝大部分的面试题,都是这100 道题系列的翻版, 此微软等公司数据结构+算法面试100 题系列,是极具代表性的经典面试题。 而,对你更重要的是,我自个还提供了答案下载,提供思路,呵。 所以,这份资料+答案,在网上是独一无二的。 ------------------------------------ 整理资源,下载地址: 答案系列: 1.[最新答案V0.3 版]微软等数据结构+算法面试100 题[第21-40 题答案] http://download.csdn.net/source/2832862 2.[答案V0.2 版]精选微软数据结构+算法面试100 题[前20 题]--修正 http://download.csdn.net/source/2813890 //此份答案是针对最初的V0.1 版本,进行的校正与修正。 3.[答案V0.1 版]精选微软数据结构+算法面试100 题[前25 题] http://download.csdn.net/source/2796735 题目系列: 4.[第一部分]精选微软等公司数据结构+算法经典面试100 题[1-40 题] http://download.csdn.net/source/2778852 5.[第1 题-60 题汇总]微软等数据结构+算法面试100 题 http://download.csdn.net/source/2826690 更多资源,下载地址: http://v_july_v.download.csdn.net/ 若你对以上任何题目或任何答案,有任何问题,欢迎联系我: My E-mail: zhoulei0907@yahoo.cn ------------- 作者声明: 本人July 对以上公布的所有任何题目或资源享有版权。转载以上公布的任何一题, 或上传百度文库资源,请注明出处,及作者我本人。 向你的厚道致敬。谢谢。 ---July、2010 年11 月20 日。 ------------------------------------------------------ 各位,若对以上100题任何一道,或对已上传的任何一题的答案, 有任何问题,请把你的思路、想法,回复到此帖子上, 微软等100题系列,永久维护地址(2010年11.26日): http://topic.csdn.net/u/20101126/10/b4f12a00-6280-492f-b785-cb6835a63dc9.html
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值