在排序数组中查找数字

第一题,数字在排序数组中出现的次数

  统计一个数字在排序数组中出现的次数. 例如输入排序数组{1,2,3,3,3,3,4,5},由于3在这个数中出现了4次,输出4.

思路

  如何利用二分查找找到第一个k? 二分查找算法总是先拿数组中间的数组和k作比较.

  1. 如果中间数字比k大,那么k只可能出现数组的前半段,下一轮只在数组的前半段查找就可以了.
  2. 如果中间数字比k小,那么k只可能出现数组的后半段,下一轮只在数组的后半段查找就可以了.
  3. 如果中间数组和k相等,先判断这个数字是不是第一个k.
  4. 如果位于中间数字前面的一个数字不是k,那么此时中间的数字刚好就是第一个k.
  5. 如果中间数字的前面一个数字也是k,也就是说第一个k肯定在数组的前半段,下一轮仍然需要在数组的前半段查找.

  基于同样的思路可以在排序数组中找到最后一个k.

  1. 如果中间数字比k大,那么k只能出现在数组的前半段.
  2. 如果中间数字比k小,那么k只能出现在数组的后半段.
  3. 如果中间数子和k相等,需要判断这个数字是不是最后一个k.
  4. 如果位于中间数字后面一个数字不是k,那么此时中间的数字刚好就是最后一个k.
  5. 如果中间数字的后面一个数字也是k,也就是说第一个k肯定在数组的后半段,下一轮仍然需要在数组的后半段查找.
#include <iostream>
#include <vector>
using namespace std;

class Solution
{
    public:
        int get_num_k(const vector<int> &v,int k);
        int get_first_k(const vector<int> &v,int k,int start,int end);
        int get_last_k(const vector<int> &v,int k,int start,int end);
};
int Solution::get_num_k(const vector<int> &v,int k)
{
    if(v.empty()||v.size()<0)
        return -1;
    
    int first=get_first_k(v,k,0,v.size()-1);
    int last=get_last_k(v,k,0,v.size()-1);
    if(first>=0&&last<v.size()  ) 
        return last-first+1;
    else
        return 0;    
}
int Solution::get_first_k(const vector<int> &v,int k,int start,int end)
{
    if(v.empty()||v.size()<0)
        return -1;
        
    int mid=(start+end)>>1;
    int mid_value=v[mid];
    if(k==mid_value)
    {
        if((mid>0&&v[mid-1]!=k)||mid==0)
            return mid;
        else
            end=mid-1;
    }
    else if(k>mid_value)
        start=mid+1;
    else
        end=mid-1;
        
    return get_first_k(v,k,start,end);
}
int Solution::get_last_k(const vector<int> &v,int k,int start,int end)
{
    if(v.empty()||v.size()<0)
        return -1;
    
    int mid=(start+end)/2;
    int mid_value=v[mid];
    if(mid_value==k)
    {
        if((mid<v.size()-1&&v[mid+1]!=k)||mid==v.size()-1)
            return mid;
        else
            start=mid+1;
    }
    else if(mid_value>k)
        end=mid-1;
    else
        start=mid+1;
        
    return get_last_k(v,k,start,end);
}
int main()
{
    vector<int> v{1,2,3,3,3,3,4,5};
    int k;
    cin>>k;
    
    Solution s;
    cout<<s.get_num_k(v,k)<<endl;
    return 0;
}

第二题,0~n-1中缺失的数字

  一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0 ~ n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

思路

  1.先用公式n*(n-1)/2求出0~n-1所有数字之和s1,在求出数组所有数字之和s2,那个不在数组中的数字就是s1-s2的差,这种解法需要O(n)的时间

  2.数组一开始的一些数字与他们的下表相同,如果说不在数组中的那个数组记为m,那么所有比m小的数字下标与他们的值都相同。可以基于二分查找

    1>如果中间元素的值和它的下标相等,那么下一轮只需要找它的右半边

    2>如果中间元素的值和它的下标不等,并且它前一个元素和它的下标相等,意味着这个中间的数字就是第一个值与下标不等的数字。

    3>如果中间元素的值和它的下标不等,并且他前面的元素和它的下标相等不等,那么只需查找左半边。

#include <iostream>
#include <vector>
using namespace std; 

class Solution
{
    public:
        int get_miss_num(const vector<int> &v,int len);
};
int Solution::get_miss_num(const vector<int> &v,int len)
{
    if(v.empty()||v.size()<=0||len<=0)
        return -1;
    
    int left=0,right=len-1;
    while(left<=right)
    {
        int mid=(left+right)>>1;
        if(v[mid]!=mid)
        {
            if(mid==0||v[mid-1]==mid-1)
                return mid;
            else
                right=mid-1;
        }
        else
            left=mid+1;
    }
    if(left==len)
        return len;
}
int main()
{
    vector<int> v{1};
    Solution s;
    cout<<s.get_miss_num(v,1)<<endl;
    return 0;
}

第三题,数组中数值和它的下标相等的元素

  一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0到n-1之内。在范围0到n-1的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

思路 

  由于数组是单调递增排序的,因此我们可以尝试二分查找算法来进行优化。假设我们某一步抵达数组中的第i个数字。如果我们很幸运,该数字的值刚好也是i,那么我们就找到了一个数字和其下标相等。

当数字的值和下标不相等的时候,假设数字的值为m。先考虑m大于i的情形,即数字的值大于它的下标。由于数组中的所有数字都唯一并且单调递增,那么对于任意大于0的k,位于下标i+k的数字的值大于或等于m+k。另外,因为m>i,所以m+k>i+k。因此,位于下标i+k的数字的值一定大于它的下标。这意味着如果第i个数字的值大于i,那么它的右边的数字都大于对应的下标,我们都可以忽略。下一轮查找只需要从它左边的数字中查找即可。

  数字的值m小于它的下标i的情形和上面类似。它左边的所有数字的值都小于对应的下标,也可以忽略。

也可以这样想:

  先获取数组的中间数,若中间数的值和下标相等,则找到一个满足条件的数;若中间数的值大于它的下标,则根据数组递增的特性可知:中间元素至少比他的下标大1,而且中间数以后的元素的值至少每个比前面大1,同时它们的下标每次也是增加1,所以右边的这些元素的值都大于它们的下标(至少大1),因此需要继续在左边寻找。同理,若中间数的值小于它的下标,则中间数左边的那些元素的值也都小于它们的下标,因此需要继续在右边寻找。

#include <iostream>
#include <vector>
using namespace std;

class Solution
{
    public:
        int get_num_same_as_index(const vector<int> &v);
};
int Solution::get_num_same_as_index(const vector<int> &v)
{
    if(v.empty()||v.size()<0)
        return -1;
    int left=0;
    int right=v.size()-1;
    while(left<=right)
    {
        int mid=(left+right)>>1;
        if(v[mid]==mid)
            return mid;
        if(v[mid]>mid)
            right=mid-1;
        else
            left=mid+1;
    }
}
int main()
{
    vector<int> v{-3,-1,1,3,5};
    Solution s;
    cout<<s.get_num_same_as_index(v)<<endl;
    return 0;
}

 

转载于:https://www.cnblogs.com/tianzeng/p/10262126.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是C语言的代码实现: ```c #include <stdio.h> int inputArray(int arr[]) { int i = 0; while (1) { scanf("%d", &arr[i]); if (arr[i] == -9999) { break; } i++; } return i; } void sortArray(int arr[], int n) { int i, j, temp; for (i = 0; i < n - 1; i++) { for (j = i + 1; j < n; j++) { if (arr[i] > arr[j]) { temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } } int binarySearch(int arr[], int n, int key) { int left = 0, right = n - 1, mid; while (left <= right) { mid = (left + right) / 2; if (arr[mid] == key) { return mid; } else if (arr[mid] > key) { right = mid - 1; } else { left = mid + 1; } } return -1; } int main() { int arr[100]; int n = inputArray(arr); sortArray(arr, n); printf("The sorted array is: "); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); int key; printf("Enter the number you want to search: "); scanf("%d", &key); int index = binarySearch(arr, n, key); if (index == -1) { printf("%d not found in the array.\n", key); } else { printf("%d found at index %d.\n", key, index); } return 0; } ``` 该程序包括三个函数: 1. `inputArray`:输入数组元素,并返回数组长度; 2. `sortArray`:将数组元素从小到大排序; 3. `binarySearch`:在有序数组中查找给定的元素,返回元素的下标,如果找不到则返回-1。 程序先调用 `inputArray` 函数输入数组元素,再调用 `sortArray` 函数将数组元素从小到大排序,然后提示用户输入要查找数字,调用 `binarySearch` 函数在排序后的数组中查找数字。最后输出查找结果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值