13 在大数组中查找(Search in a Big Sorted Array)

本文介绍如何利用二分搜索的逆向策略,在无法直接获取数组长度的情况下,以O(logk)时间复杂度在大排序数组中查找目标值。通过倍增技巧确定边界,然后进行常规二分搜索,解决实际问题。

1 题目

题目:在大数组中查找(Search in a Big Sorted Array)
描述:给一个按照升序排序的非负整数数组。这个数组很大以至于你只能通过固定的接口 ArrayReader.get(k) 来访问第k个数(或者C++里是ArrayReader->get(k)),并且你也没有办法得知这个数组有多大。找到给出的整数target第一次出现的位置。你的算法需要在O(logk)的时间复杂度内完成,k为target第一次出现的位置的下标。如果找不到target,返回-1。

  1. 如果你访问了一个不可访问的下标(比如越界),ArrayReader 会返回2,147,483,647。

lintcode:题号——447,难度——medium

样例1:

输入: [1, 3, 6, 9, 21, ...], target = 3
输出: 1

样例2:

输入: [1, 3, 6, 9, 21, ...], target = 4
输出: -1

2 解决方案

2.1 思路

  由于无法知道数组的长度,所以需要自己制定,因为时间复杂度要求时O(log k),二分的时间复杂度是log的数量级,考虑将二分逆向使用,通过倍增的方式来不断寻找符合条件的边界,找到边界之后即可按照标准二分搜索来解题。

倍增思想在很多容器中被使用到,c++中的vector等主要容器起始分配的容量大小都是一定值的(为了不浪费资源),每次在存入的数据即将超出容量的时候,会动态地将容量加倍。

2.2 时间复杂度

  先寻找边界耗时O(log n),再搜索目标耗时O(log n),该题的算法的时间复杂度为O(2*log n) = O(log n)。

深度优先搜索的时间复杂度计算没有通用的方式,只能根据具体题目计算,可以理解成看作答案个数与构造每个答案花费的时间的乘积。

2.3 空间复杂度

  使用了vector数据结构保存节点,算法的空间复杂度为O(n)。

3 源码

细节:

  1. 边界未知,需要先用倍增的方式找到边界。
  2. 有了边界之后就可以使用二分搜索来找到目标。

C++版本:

/**
* @param reader: An instance of ArrayReader.
* @param target: An integer
* @return: An integer which is the first index of target.
*/
int searchBigSortedArray(ArrayReader * reader, int target) {
    // write your code here
    int index = 1;
    // 倍增寻找边界
    while (reader->get(index) < target)
    {
        index *= 2;
    }

    // 找到边界之后,二分搜索目标
    int start = index / 2;
    int end = index;
    while (start + 1 < end)
    {
        int mid = start + (end - start) / 2;
        if (reader->get(mid) < target)
        {
            start = mid;
        }
        if (reader->get(mid) >= target)
        {
            end = mid;
        }
    }

    if (reader->get(start) == target)
    {
        return start;
    }
    if (reader->get(end) == target)
    {
        return end;
    }

    return -1;
}
### 查找指定元素的算法 对于在数组中查找指定元素的任务,通常采用线性搜索或二分搜索的方法。当数组未排序时,只能使用线性搜索;而如果数组已经按顺序排列,则可以选择更高效的二分搜索。 #### 线性搜索 这是一种简单直接的方式,在给定的一维数组`list[]`内逐个比较目标值`x`与各元素之间的关系直到发现匹配项为止。这种方法适用于无序列表,并且时间复杂度为O(n),其中n表示数组长度[^1]。 ```c int search_linear(int list[], int n, int x){ for (int i = 0; i < n ; ++i) { if(list[i]==x)return i; } return -1; } ``` #### 有序数组中的插入操作 考虑到特定情况下需要处理的是降序排列的数据结构,为了保持原有秩序不变的情况下完成新成员加入工作,可以从前往后扫描直至遇到首个小于待插数的位置并执行相应移位动作来腾出空间供其安置下去[^2]。 ```c void insert_into_sorted_array_descending_order(int* array,int size,int value){ // 找到第一个比value小的地方 int pos=-1;++i){ if(array[i]<value)pos=i; } // 如果没找到说明应该放在最后面 if(pos==-1)pos=size; // 向后移动数据以便于插入新的数值 for(int j=size;j>pos;--j){ array[j]=array[j-1]; } // 插入新值 array[pos]=value; } ``` #### PTA练习题解答框架 根据提供的裁判测试程序样例可以看出此题目的核心在于定义函数`search()`用于返回查询结果索引号或是报告找不到的情况(-1)[^3]。这里给出完整的解决方案如下: ```c #include <stdio.h> #define MAXN 10 // 定义search函数实现逻辑 int search(int list[], int n, int x); int main(){ int i,index,n,x,a[MAXN]; scanf("%d",&n); for(i=0;i<n;i++) scanf("%d",&a[i]); scanf("%d",&x); index=search(a,n,x); if(index!=-1) printf("index=%d\n",index); else printf("Not found\n"); return 0; } /* 实现search功能 */ int search(int list[], int n, int x){ for(int i=0;i<n;i++){ if(x==list[i]) return i; } return -1; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值