剑指offer面试题(三)数组中重复的数字

题目
在一个长度为n的数组里的所有数字都在0~n-1 的范围里面,数组中有一些重复的数字,但是不知道有几个数字重复了,也不知道每个数字重复了几次,题目要求:找出数组中任意一个重复的数字

这个题目有三种思路可以实现两种是通过数组实现,还有一个是通过哈希表来实现,我就把除了哈希表的那两种算法实现一下吧。
方法一
如果规定了空间复杂度为O(1),那也就是说不能开辟新的空间,我们就只能在原数组上进行操作了。前提是,允许数组进行变动。

第一种方法算法思想如下
——>因为数组元素的范围已经给定,数组的大小也已知道,那么我们可以模拟哈希表进行数组的排序,在数组下标为0的位置放上元素0,然后在1的位置放上元素1,这样如果没有重复的情况下,每个位置都放着对应的元素
——>但是问题已经说明肯定有重复的数字,所以说,如果第一次在对应的位置上放上元素之后,第二次再次遇到了这个元素,发现它在对应的位置山已经有一个数了,那么它就一定是重复的了,然后用一个指针指向这个值,返回给主函数就可以得到了
优点
这个算法有一个明显的优点,时间复杂度很小,只有O(n),因为虽然它经过的是两层循环,但是每次一个数字最多只要交换两次就可以找到它的位置,所以总的时间复杂度仍然是O(n)
空间复杂度也是很小的,只有O(1),因为它就是在本数组中操作的,没有开辟额外的空间
缺点
唯一的缺点就在于破坏了数组的结构,如果具体的面试题中要求不能破坏数组,那就不要选择这种啦
代码放在下面

int FindNumbers(int numbers[],int length)
{
    int i=0;
    for(i=0;i<length;i++)
    {
        while(numbers[i] != i)
        {
            if(numbers[i]== i)
            {
                return ture;
            }
            swap(numbers[i],numbers[numbers[i]]);
        }

    }

}

方法二
第二种方法相比较第一种能复杂一些了,第二种方法的思路和二分查找法很类似,不过,它比二分查找多了一个数组个数的计算,第二种算法的思想其实也不难,具体思路如下
第一步 -> 将数组一分为二,假设有十个数字,那么就分成1-5 和 6-10 l两段区间
第二步-> 统计1-5区间中的元素个数,如果按照上一题的思路,1-5区间中数字如果没有重复,那就应该刚好是五个数字,但是如果有重复的数字,那么它的个数应该大于5,这个时候,我们就需要把数组缩小到1-5,然后再把1-5的区间缩小为1-2和3-5,这样继续查找,直到最后剩下一个数的时候就终止查找
但是这个方法也有缺陷,它有可能第一次就会查找失败,比如说如下数组
1 2 2 4 5 6 7 8 9
这个时候,由于左边和右边数组个数都相同,就无法进行查找了
不过,使用这种方法也要依靠具体情况而定,如果考官给出的数组满足查找条件,那就放心大胆的用
优点
不会修改数组,由于是近似于二分查找,所以时间复杂度也不高,只有O(n *log n)
缺点
只有一部分情况可以满足,因此需要看具体情况使用
代码放在下面

int FindNumbers2(int a[],int length,int start,int end)
{



    while(end>=start)
    {
        int middle=(end-start)>>1+start;
        int count=CountRange(numbers,length,start,end)

            if(end == start)
            {
                if(count > 1)
                    return start;
                else
                    break;
            }


            if(count>middle-start+1)
                 end=middle;
            else 
                 start=middle+1;
            }
            return -1;
    }
int CountRange(const int *numbers,int length,int start,int end)
{
    if(numbers  == NULL)
        return 0;
    int count=0;
    for(int i=0;i<length;i++)
    {
        if(numbers[i]>=start && numbers[i]<=end)
            count ++;
        return count;
    }
}


bool FindOne(int a[],int row,int col,int numbers)
{

    while( col>=0 &&row >= 0 )
    {
        if(a[row-1][col-1] == numbers)
        {
            return true;
        }
        if(numbers>a[row-1][0])
            ++col;
        else
            --row;
    }
    return false;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值