线性查找-数组最大距离

已知整型数组A[n], 问找到最大的 j-i, 符合A[i] < A[j].

方法一:n个数据,两个变量,由于当i,j确定时,每次的比较很简单(A[i] ? A[j]),时间为O(1),所以最原始的暴力解法就是时间O(n^2)。

方法二:我们试着逐步降低时间复杂度。对于符合条件 A[i] < A[j]的 i 和 j,j-i 的可能取值范围为[1,n)。 由于必然存在一个极大值k = j-i, 当j-i > k时, 没有 j-i 符合条件;当j-i < k时,必有 j-i 符合条件(比如k序列的子序列)。所以k是在一个线性分布上的极大值,符合二分查找的应用条件。总的时间复杂度为O(n*lgn)

bool distanceExist(int *A, int n, int k, int& begin){ //k=j-i
    for(int i=0;i+k<n;++i){
        if(A[i] < A[i+k]){
            begin = i;
            return true;
        }
    }
    return false;
}
int maxDistance(int *A, int n, int& begin){
    int u=n, v=0, m=0;
    while(v<u){
        m = (u+v)/2;
        if(distanceExist(A, n, m, begin)){
            v = m+1; //enlarge v to try greater m
        }else{ //reduce u to try smaller m
            u = m;
        }
    }
    return m;
}
方法三:我们来尝试一下是否存在时间O(n)的解法。考虑到题目中每一个元素A[i]和它右边的元素比较,如果强化一下,让A[i]和它右边所有元素中最大值比较,会有什么效果?

                                             图一,A[] = {3,1,2,3,1,2,1}

如上图所示,我们有原始数组A[] = {3,1,2,3,1,2,1}, 纵坐标Rmax[i]表示的是从A[i]到最右边剩余数组中的最大值。如果我们从左边i=0开始,每次比较A[i] 和 Rmax[j] (i<j), 当A[i] < Rmax[j], 向右移动j, 当A[i] < Rmax[j] 刚刚不成立时,j-1-i即是A[0]到A[j]之间符合条件的最大j-i。因为如果有x<i, 能够符合A[x] < A[j], 由于x在i左边,所以A[x]肯定能够率先扫描到Rmax[j]. 然后,同时分别向左移动一位i和j,继续以上过程。因为我们已经确认存在j-1-i了,所以之后只需要考察j-i即可。

                                              图二,A[] = {1,0,0,0,2,1,1}

由于j首先到达右边边界,所以总的时间为O(n)。终于找到了线性查找的方法!一份实现代码如下:

int maxDistance_02(int *A, int n, int& left){
    if(n<2) return 0;
    int *Rmax = new int[n]();
    Rmax[n-1] = A[n-1];
    for(int i=n-2;i>=0;i--){
        Rmax[i] = max(A[i], Rmax[i+1]);
    }
 
    int l=0, r=0, maxDiff=-1;
    while(l<n && r<n){
        if(A[l] < Rmax[r]){
            if(r-l > maxDiff){
                maxDiff = r-l;
                left = l;
            }
            ++r;
        }else{
            ++l;
            ++r; //resume to scan from the potential distance remaining r-l
        }
    }
    delete[] Rmax;
    return maxDiff;
}

小结:

1。二分查找的适用情况:待比较值m呈线性分布,并且存在一个极值(极大或极小)作为目标值。

2。对于存在两个变量的情况,如果希望实现时间为线性的查找,那么第一,看是否能够简化两个变量的关系,避免两变量相互独立;第二,当得到两变量某组合时,对数据的处理能否简化到时间O(1)。


注:题目来自leetcode, 同时也附有解法。不过网站在介绍O(n*lgn)的解法时用了排序,相比于我这里原创的方法二,既多用了空间,又多了一遍扫描,所以没有引用。网站介绍的O(n)的解法("Best Solution"),过于晦涩也没有完整代码,我从评论中找到一份实现,作为这里的方法三。

### 查找操作在 C++ 中的实现方式 在 C++ 编程语言中,标准模板库(STL)提供了多种用于数组或其他容器的操作函数。对于查找操作而言,可以依据具体需求选择不同的方法。 #### 使用 `std::find` 进行线性查找 如果目标是在未排序的数据集中找到某个特定值,则可以使用 STL 提供的通用算法 `std::find`。该函数接受两个迭代器范围以及待查找的目标值作为参数,并返回指向第一个匹配项的迭代器,或者在找不到的情况下返回结束迭代器[^1]。 ```cpp #include <algorithm> #include <vector> int main() { std::vector<int> data = {1, 3, 5, 7, 9}; auto it = std::find(data.begin(), data.end(), 5); if (it != data.end()) { // 找到元素的位置可以通过距离计算得出 size_t index = std::distance(data.begin(), it); } else { // 元素不存在于向量中 } } ``` #### 对有序序列应用二分查找 当处理的是已经按升序排列好的数据集时,推荐采用更高效的二分查找技术来定位指定数值是否存在。这可通过调用 `std::binary_search` 实现,其时间复杂度仅为 O(log n)。 需要注意的是,虽然此功能仅告知我们所查询项目是否存在于范围内,但它并不提供确切位置信息。若还需要知道确切下标,则可考虑配合其他辅助工具一起工作: ```cpp #include <set> #include <algorithm> int main(){ std::set<int> numbers = { /*...*/ }; // 假设这里填充了一些整数 bool is42Present = std::binary_search(numbers.begin(), numbers.end(), 42); return 0; } ``` 另外,在某些情况下可能还会遇到需要最小化数组内的最大值等问题场景,这时则往往涉及到更加复杂的逻辑设计与优化策略探讨[^2]。 最后值得注意的一点是对静态分配型数组执行插入、删除之类的动态管理动作相对较为麻烦一些;相比之下借助像 vector 或 list 这样的高级封装类会显得更为便捷灵活得多[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值