找1到n中缺失的数字(长度为n-1的整形数组,数字的范围在1到n,找其中一个缺失的数字)

给定一个长度为n-1的整形数组,数字的范围在1到n(无重复),其中有一个缺失的数字,写一段高效的程序找出该数字。

一、数组有序

对于该数组是否有序,题目没有说明,假设有序,则可使用二分查找,时间复杂度为O(logN)。
如果中间元素的值和下标相等,那么下一轮查找只需要查找右半边;如果中间元素的值和下标不相等,并且它前面一个元素和它的下标相等,这意味着这个中间的数字正好是第一个值和下标不相等的元素,它的下标就是在数组中不存在的数字;如果中间元素的值和下标不相等,并且它前面一个元素和它的下标不相等,这意味着下一轮查找我们只需要在左半边查找即可。
参考代码:

int getLoseNum(int a[], int left, int right)
{
    int mid;
    while(left <= right)
    {
        mid = (left + right) / 2;
        if(a[mid] == mid + 2) //缺失的数据在后半部分
            right = mid - 1;
        else if(a[mid] == mid + 1)  //缺失的数据在前半部分
            left = mid + 1;
    }
    return left + 1;
}

二、与数组顺序无关

以下方法,数组是否有序都可使用,但一中的二分查找,要求数组必须有序。

1、求和法

用1+2+…+n减去当前输入数据的总和,则所得的差就是缺失的那个数。
时间复杂度:O(n) 空间复杂度:O(1)。但n过大时,求和存在溢出问题。

//n:最大元素的值而不是元素的个数
int getNum(int a[], int n)
{
    int sum = n*(n+1)/2;
    int t;
    for(int i=0; i<n-1; i++)
    {
        sum = sum - a[i];
    }
    return sum;
}

2、异或法

利用异或运算,
任何数异或自己都等于0,x^x=0,任何数异或0都等于他自己x^0=x;
假如缺的为3。result = 1^2^4^5^N
第二次异或后 result = 1^2^4^5^N ^1^2^3^4^5^N = 0^3 = 3
时间复杂度:O(n) 空间复杂度:O(1)
参考代码:

//异或方法,n:最大元素的值
int getLose(int a[], int n)
{
    int t = 0;
    for(int i =1; i<=n; i++)
        t = t ^ i;
    //最大值为n,缺失一个元素,则元素个数为n-2
    for(int i=0; i<n-1; i++)
        t = t ^ a[i];
    return t;
}

3、hash方法

对输入数据进行Hash,然后从头到尾遍历一次。时间复杂度O(n) 空间复杂度O(n)
参考代码:

//max:最大值
int getMiss(int a[], int max)
{
    //hash表的长度比max大1
    int *tmp = new int[max+1];
    //数组的值从1开始,因此hash表的0位不用
    for(int i=1; i<=max; i++)
        tmp[i] = 0;
    //对数组遍历,缺失一个数,数组的长度为max-2
    for(int i=0; i<max-1; i++)
    {
        tmp[a[i]]++;
    }
    for(int i=1; i<=max; i++)
        if(tmp[i] == 0)
            return i;
}
int main()
{
    int a[] = {1,2,3,4,5,7};
    cout<<getLoseNum(a, 0, 6)<<endl;
    cout<<getNum(a, 7)<<endl;
    cout<<getLose(a, 7)<<endl;
    cout<<getMiss(a, 7)<<endl;
    return 0;
}

二、题目扩展:数组中缺失两个数?

同样的可以用两种方法:
1、对数据从min~max求和,为S1;并且求出数组中所有元素的和,为S2;
记缺失的数据为a,b。S1-S2=a+b;
那么a、b,一定是一个比(a+b)/2小,一个比它大。再遍历一遍数组,以(a+b)/2为分界线,较小的加为Smin,较大的加为Smax。求出min~(a+b)/2和(a+b)/2~max的和,相减就可以得到两个数了。
2、将数组中的元素全部进行异或运算,再异或上min^(min+1)^….^max,这样就得到了a^b,记为c。由于a!=b。所以c!=0;那么一定有一位是1.用d=(c-(c&(c-1)))就可以得到c的最右边的为1的位,以这一位为分界线,为1的分为一组,不为1的分成另一组,然后求min~d和d~max的异或,再分别异或上刚刚分好的两组数据。这样就将问题转化成问题1的情况了。那a、b自然也就出来了。
扩展部分参考:
http://www.cnblogs.com/prajna/archive/2013/02/25/2932807.html
https://blog.csdn.net/nature_XD/article/details/51999793

  • 19
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值