在 C++ 中的集合中实现两个指针技术

Implementing two pointer technique in sets in C++

我正在尝试在 C++ 的集合中实现两个指针技术。 我想检查集合中两个元素的差值是否等于常数 'k'.

void solve(int n,int k,set<int> s){
    auto it_1 = s.begin();
    auto it_2 = s.end();
    while(it_1 < it_2){ 
        if(*it_2 - *it_1 == k){
            cout << "YES" << endl;
            return;
        }
        else if(*it_2 - *it_1 > k){
            it_1++;
        }
        else{
            it_2--;
        }
    }
    cout << "NO" << endl;
    return;
}

我收到以下错误:-

error: no match for 'operator<' (operand types are 'std::_Rb_tree_const_iterator' and 'std::_Rb_tree_const_iterator') while(it_1 < it_2)

我知道错误是因为我在比较两个指针。如何检查 it_1 是否在 it_2 后面?

迭代器的问题可以很快解决。

  • 只需使用 != 运算符而不是 <
  • 不从end迭代器开始,而是从前一个元素开始。因为 end-迭代器指向过去的最后一个有效元素。

但是,不幸的是,如果您想找到具有给定增量的一对,2 指针方法将不起作用。这样做的原因是您会过多地增加一个指针,并且永远无法达到增量。通过总结这些值,它会起作用。

让我们以您的初始方法为例修复小错误并查看输出:

#include <iostream>
#include <set>

void solve(int k, std::set<int>& s) {
    auto it_1 = s.begin();
    auto it_2 = std::prev(s.end());
    while (it_1 != it_2) {
        std::cout << *it_1 << ' ' << *it_2 << '\n';
        if (*it_2 - *it_1 == k) {
            std::cout << "YES\n";
            return;
        }
        else if (*it_2 - *it_1 > k) {
            it_1++;
        }
        else {
            it_2--;
        }
    }
    std::cout << "NO\n";
    return;
}
int main() {
    std::set test{ 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
    solve(8, test);
}

这将导致:

1 89
2 89
3 89
5 89
8 89
13 89
21 89
34 89
55 89
NO

你看到问题了。

顺便说一句,求和是 2 指针方法的典型用例。然后它将始终有效。该集合已排序。我们从开头的最小值和结尾的最大值开始。因此,构建第一个总和将为我们提供最大可能的总和。然后我们先移动小数字的指针,或者根据总和,减少上面的指针。

示例:

#include <iostream>
#include <set>

void solve(int k, std::set<int>& s) {
    auto it_1 = s.begin();
    auto it_2 = std::prev(s.end());
    while (it_1 != it_2) {
        std::cout << *it_1 << ' ' << *it_2 << '\n';
        if (*it_2 + *it_1 == k) {
            std::cout << "YES\n";
            return;
        }
        else if (*it_2 + *it_1 < k) {
            it_1++;
        }
        else {
            it_2--;
        }
    }
    std::cout << "NO\n";
    return;
}
int main() {
    std::set test{ 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
    solve(8, test);
}

输出:

1 89
1 55
1 34
1 21
1 13
1 8
1 5
2 5
3 5
YES


但是delta问题怎么解决呢?

永远有效的方法是暴力破解。这会像这样:

#include <iostream>
#include <set>

void solve(int k, std::set<int>& s) {
    auto it_1 = s.begin();
    auto it_2 = std::next(s.begin());

    while ((it_1 != s.end()) and (it_2 != s.end())) {
        std::cout << *it_1 << ' ' << *it_2 << '\n';
        if ((it_1 != it_2) and ((*it_2 - *it_1 == k) or (*it_1 - *it_2 == k))) {
            std::cout << "YES\n";
            return;
        }
        else if (*it_2 - *it_1 < k)
            ++it_2;
        else
            ++it_1;
    }
    std::cout << "NO\n";
    return;
}
int main() {
    std::set test{ 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
    solve(8, test);
}

输出如下:

1 2
1 3
1 5
1 8
1 13
2 13
3 13
5 13
YES

稍微优化一下的反对意见会使用 std::unordered_map。但也许这对这里来说太多了。