Tìm hiểu trong thời gian tuyến tính xem có một cặp trong vectơ được sắp xếp cộng với giá trị nhất định không


8

Đưa ra một std::vectortrong các phần tử riêng biệt được sắp xếp theo thứ tự tăng dần, tôi muốn phát triển một thuật toán xác định xem có hai phần tử trong bộ sưu tập có tổng là một giá trị nhất định hay không sum.

Tôi đã thử hai cách tiếp cận khác nhau với sự đánh đổi tương ứng của họ:

  1. Tôi có thể quét toàn bộ vectơ và, đối với mỗi phần tử trong vectơ, áp dụng tìm kiếm nhị phân ( std::lower_bound) trên vectơ để tìm kiếm một phần tử tương ứng với sự khác biệt giữa sumvà phần tử hiện tại. Đây là một giải pháp thời gian O (n log n) không yêu cầu thêm không gian.

  2. Tôi có thể đi qua toàn bộ vectơ và điền vào một std::unordered_set. Sau đó, tôi quét vectơ và, đối với mỗi phần tử, tôi tìm kiếm std::unordered_setsự khác biệt giữa sumphần tử hiện tại. Vì tìm kiếm trên bảng băm chạy trong thời gian trung bình không đổi, giải pháp này chạy trong thời gian tuyến tính. Tuy nhiên, giải pháp này đòi hỏi không gian tuyến tính bổ sung vì std::unordered_setcấu trúc dữ liệu.

Tuy nhiên, tôi đang tìm kiếm một giải pháp chạy trong thời gian tuyến tính và không yêu cầu thêm không gian tuyến tính. Có ý kiến ​​gì không? Có vẻ như tôi buộc phải đánh đổi tốc độ lấy không gian.

Câu trả lời:


10

std::vectorđã được sắp xếp và bạn có thể tính tổng của một cặp khi đang bay , bạn có thể đạt được giải pháp thời gian tuyến tính theo kích thước của vectơ với không gian O (1).

Sau đây là một triển khai giống như STL không yêu cầu thêm không gian và chạy trong thời gian tuyến tính:

template<typename BidirIt, typename T>
bool has_pair_sum(BidirIt first, BidirIt last, T sum) {
    if (first == last)
        return false; // empty range

   for (--last; first != last;) {
      if ((*first + *last) == sum)
         return true; // pair found

      if ((*first + *last) > sum)
         --last; // decrease pair sum
      else // (*first + *last) < sum (trichotomy)
         ++first; // increase pair sum
   }

    return false;
}

Ý tưởng là đi qua vectơ từ cả hai đầu - trước và sau - theo hai hướng ngược nhau cùng một lúc và tính tổng của các cặp phần tử trong khi thực hiện.

Lúc đầu, cặp bao gồm các yếu tố có giá trị thấp nhất và cao nhất tương ứng. Nếu tổng kết quả thấp hơn sum, thì tiến lên first- trình lặp chỉ vào đầu bên trái. Nếu không, di chuyển last- iterator chỉ vào đầu bên phải - lùi lại. Bằng cách này, tổng kết quả dần dần tiếp cận sum. Nếu cả hai lần lặp cuối cùng chỉ vào cùng một phần tử và không có cặp nào có tổng bằng với sumđã được tìm thấy, thì không có cặp nào như vậy.

auto main() -> int {
   std::vector<int> vec{1, 3, 4, 7, 11, 13, 17};

   std::cout << has_pair_sum(vec.begin(), vec.end(), 2) << ' ';
   std::cout << has_pair_sum(vec.begin(), vec.end(), 7) << ' ';
   std::cout << has_pair_sum(vec.begin(), vec.end(), 19) << ' ';
   std::cout << has_pair_sum(vec.begin(), vec.end(), 30) << '\n';
}

Đầu ra là:

0 1 0 1

Nhờ vào bản chất chung của mẫu hàm has_pair_sum()và vì nó chỉ yêu cầu các trình lặp hai chiều, giải pháp này cũng hoạt động với std::list:

std::list<int> lst{1, 3, 4, 7, 11, 13, 17};
has_pair_sum(lst.begin(), lst.end(), 2);

5

Tôi đã có cùng một ý tưởng như trong câu trả lời của 眠 り ネ ロ ク , nhưng với việc thực hiện dễ hiểu hơn một chút.

bool has_pair_sum(std::vector<int> v, int sum){
    if(v.empty())
        return false;

    std::vector<int>::iterator p1 = v.begin();
    std::vector<int>::iterator p2 = v.end(); // points to the End(Null-terminator), after the last element
    p2--; // Now it points to the last element.

    while(p1 != p2){  
        if(*p1 + *p2 == sum)
            return true;
        else if(*p1 + *p2 < sum){ 
            p1++;
        }else{
            p2--;
        }
    }

    return false;
}

Cảm ơn bạn đã bình luận của bạn. Tôi đã chỉnh sửa bài viết.
Atr0x

2

tốt, vì chúng ta đã được cung cấp mảng đã được sắp xếp, chúng ta có thể thực hiện bằng hai cách tiếp cận con trỏ, trước tiên chúng ta giữ một con trỏ bên trái ở đầu mảng và một con trỏ bên phải ở cuối mảng, sau đó trong mỗi lần lặp chúng ta kiểm tra xem tổng giá trị của chỉ số con trỏ bên trái và giá trị của chỉ mục con trỏ phải bằng hoặc không, nếu có, quay trở lại từ đây, nếu không chúng ta phải quyết định làm thế nào để giảm ranh giới, đó là tăng con trỏ trái hoặc giảm con trỏ phải, vì vậy chúng ta so sánh tổng tạm thời với tổng đã cho và nếu tổng tạm thời này lớn hơn tổng đã cho thì chúng tôi quyết định giảm con trỏ phải, nếu chúng tôi tăng con trỏ bên trái thì tổng tạm thời sẽ giữ nguyên hoặc chỉ tăng nhưng không bao giờ giảm, vì vậy chúng tôi quyết định giảm con trỏ bên phải để tổng tiền tạm thời giảm và chúng tôi đạt gần tổng của chúng tôi, similary nếu tổng tạm thời nhỏ hơn tổng đã cho,do đó, không có nghĩa là giảm con trỏ bên phải vì tổng tạm thời sẽ duy trì tổng hoặc giảm nhiều hơn nhưng không bao giờ tăng vì vậy chúng tôi tăng con trỏ bên trái để tăng tổng tạm thời và chúng tôi đạt được gần tổng và chúng tôi thực hiện cùng một quy trình trừ khi chúng tôi lấy tổng bằng hoặc giá trị chỉ mục con trỏ bên trái trở nên lớn hơn chỉ mục con trỏ bên phải hoặc ngược lại bên dưới là mã để trình diễn, hãy cho tôi biết nếu có gì đó không rõ ràngcho tôi biết nếu có gì không rõ ràngcho tôi biết nếu có gì không rõ ràng

bool pairSumExists(vector<int> &a, int &sum){
    if(a.empty())
    return false;

    int len = a.size();
    int left_pointer = 0  , right_pointer = len - 1;

    while(left_pointer < right_pointer){
        if(a[left_pointer] + a[right_pointer] == sum){
            return true;
        }
        if(a[left_pointer] + a[right_pointer] > sum){
            --right_pointer;
        }
        else
        if(a[left_pointer] + a[right_poitner] < sum){
            ++left_pointer;
        }
    }
    return false;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.