"유용한"C ++ 바이너리 검색 알고리즘은 어디서 구할 수 있습니까?


106

std::binary_search표준 라이브러리의 <algorithm>헤더 와 같은 C ++ STL 컨테이너와 호환되는 이진 검색 알고리즘이 필요하지만 요소가 존재하는지 알려주는 단순한 부울이 아닌 결과를 가리키는 반복자를 반환해야합니다.

(참고로, 표준위원회가 binary_search 용 API를 정의 할 때 도대체 무슨 생각을했을까요?!)

여기서 내 주요 관심사는 바이너리 검색의 속도가 필요하다는 것입니다. 따라서 아래에 언급 된 것처럼 다른 알고리즘으로 데이터를 찾을 수 있지만 바이너리의 이점을 얻기 위해 데이터가 정렬되어 있다는 사실을 활용하고 싶습니다. 선형 검색이 아닌 검색입니다.

지금까지 lower_boundupper_bound데이텀이없는 경우 실패합니다 :

//lousy pseudo code
vector(1,2,3,4,6,7,8,9,0) //notice no 5
iter = lower_bound_or_upper_bound(start,end,5)
iter != 5 && iter !=end //not returning end as usual, instead it'll return 4 or 6

참고 : 컨테이너와 호환되는 한 std 네임 스페이스에 속하지 않는 알고리즘을 사용해도 괜찮습니다. 예를 들어, boost::binary_search.


2
편집과 관련하여 : 이것이 std :: equal_range가 해결책 인 이유입니다. 그렇지 않으면 동등성 (또는 동등성)을 테스트해야합니다.
Luc Hermitte

(lower / upper) _bound를 사용한 후 동등성을 테스트해야합니다 (아래 답변 참조).
Luc Touraille

lower_bound 및 upper_bound 문서에는 범위를 정렬해야한다고 명시되어 있으며 이로 인해 이진 검색으로 구현할 수 있습니다.
vividos

@vividos, 만세! 내가 알아야 할 문서 조각을 찾았습니다! 감사!
Robert Gould

Robert, lower / upper_bound / equal_range 알고리즘은 정렬되지 않은 범위에서 작동하지 않습니다. 당신이 가져온 요소 샘플로 작업하는 것을 보는 것은 운이 좋습니다.
Luc Hermitte

답변:


97

이 그러한 기능은 없지만, 당신은 사용하여 간단한 하나를 쓸 수 있습니다 std::lower_bound, std::upper_bound또는 std::equal_range.

간단한 구현은 다음과 같습니다.

template<class Iter, class T>
Iter binary_find(Iter begin, Iter end, T val)
{
    // Finds the lower bound in at most log(last - first) + 1 comparisons
    Iter i = std::lower_bound(begin, end, val);

    if (i != end && !(val < *i))
        return i; // found
    else
        return end; // not found
}

또 다른 해결책은 std::set요소의 순서를 보장 iterator find(T key)하고 주어진 항목에 반복기를 반환하는 메서드 를 제공 하는를 사용하는 것입니다. 그러나 요구 사항이 집합 사용과 호환되지 않을 수 있습니다 (예 : 동일한 요소를 여러 번 저장해야하는 경우).


예, 작동합니다. 지금도 비슷한 구현이 있지만 상황의 컨텍스트,이 경우 정렬 된 데이터를 사용하지 않는다는 점에서 "순진한"구현입니다.
Robert Gould

5
lower_bound는 정렬 된 데이터에만 사용할 수 있기 때문에 귀하의 의견을 정말로 이해하지 못합니다. 복잡도는 찾기를 사용하는 것보다 낮습니다 (편집 참조).
Luc Touraille 09.01.15

4
Luc의 답변을 보완하려면 Matt Austern의 고전 기사 Why You Should n't Use set 및 What You Should Use 대신 (C ++ 보고서 12 : 4, 2000 년 4 월) 정렬 된 벡터를 사용한 이진 검색이 일반적으로 std :: set보다 선호되는 이유를 확인하십시오. , 이는 트리 기반 연관 컨테이너입니다.
ZunTzu

16
사용하지 마십시오 *i == val! 오히려 !(val < *i). 그 이유는 즉 lower_bound사용 <하지 ==(즉, T심지어 평등 비교 될 필요는 없습니다). ( 평등동등성 의 차이에 대한 설명은 Scott Meyers의 효과적인 STL 을 참조하십시오 .)
gx_

1
@ CanKavaklıoğlu에있는 요소가 없습니다 end. C ++ 표준 라이브러리의 범위는 반 개방 간격 ( 마지막 요소 의 끝 반복기 "점") 으로 표시됩니다. 따라서 값이 없음을 나타 내기 위해 알고리즘에 의해 반환 될 수 있습니다.
Luc Touraille 2015

9

를 봐야 std::equal_range합니다. 모든 결과의 범위에 대해 한 쌍의 반복자를 반환합니다.


cplusplus.com/reference/algorithm/equal_range 에 따르면 std :: equal_range 의 비용은 std :: lower_bound의 약 두 배입니다. std :: lower_bound에 대한 호출과 std :: upper_bound에 대한 호출을 래핑하는 것으로 보입니다. 데이터에 중복이 없다는 것을 알고 있다면 그것은 과잉이며 std :: lower_bound (상위 답변에서 설명)가 최선의 선택입니다.
Bruce Dawson

@BruceDawson : cplusplus.com 은 동작 을 지정 하는 참조 구현 만 제공합니다 . 실제 구현을 위해 선호하는 표준 라이브러리를 확인할 수 있습니다. 예를 들어, llvm.org/svn/llvm-project/libcxx/trunk/include/algorithm 에서 lower_bound 및 upper_bound에 대한 호출이 분리 된 간격 (일부 수동 이진 검색 후 )에서 이루어짐을 볼 수 있습니다. 즉, 특히 여러 값이 일치하는 범위에서 비용이 더 많이들 수 있습니다.
Matthieu M.

6

그 세트가 있습니다.

http://www.sgi.com/tech/stl/table_of_contents.html

검색 :

별도의 참고 :

그들은 아마도 컨테이너를 검색하면 하나 이상의 결과를 검색 할 수 있다고 생각했을 것입니다. 그러나 존재 여부를 테스트해야하는 이상한 경우에는 최적화 된 버전도 좋을 것입니다.


3
binary_search는 앞서 언급했듯이 반복자를 반환하지 않기 때문에 대안을 찾고 있습니다.
Robert Gould

1
네, 알아요. 그러나 그것은 이진 검색 알고리즘 세트에 적합합니다. 그래서 다른 사람들이 알기에 좋습니다.
Martin York

8
binary_search는 STL의 다른 많은 것들과 마찬가지로 이름이 잘못되었습니다. 나는 그것을 싫어한다. 존재를 테스트하는 것은 무언가를 찾는 것과는 다릅니다.
OregonGhost

2
이러한 이진 검색 기능은 찾고있는 요소의 색인을 알고 싶은 경우 유용하지 않습니다. 이 작업을 위해 자체 재귀 함수를 작성해야합니다. 이 템플릿 <class T> int bindary_search (const T & item)가 다음 버전의 C ++에 추가되기를 바랍니다.
Kemin Zhou

3

std :: lower_bound가 원하는 수준에 비해 너무 낮은 경우 boost :: container :: flat_multiset 을 확인하는 것이 좋습니다 . 이진 검색을 사용하여 정렬 된 벡터로 구현 된 std :: multiset의 드롭 인 대체입니다.


1
좋은 링크; 또한 좋은 링크 에서 링크 : lafstern.org/matt/col1.pdf , (모두 로그 (N) 비록) 조회 오히려 세트보다 정렬 된 벡터 구현 방법에 대해 설명이 상당히 ~ 비례의 더 나은 상수를하고 있습니다 두 배 빠릅니다 (단점은 INSERTION 시간이 더 크다는 것입니다).
Dan Nissenbaum 2013

2

표준 라이브러리에 포함되지 않은 이유를 궁금해하는 가장 짧은 구현 :

template<class ForwardIt, class T, class Compare=std::less<>>
ForwardIt binary_find(ForwardIt first, ForwardIt last, const T& value, Compare comp={})
{
    // Note: BOTH type T and the type after ForwardIt is dereferenced 
    // must be implicitly convertible to BOTH Type1 and Type2, used in Compare. 
    // This is stricter than lower_bound requirement (see above)

    first = std::lower_bound(first, last, value, comp);
    return first != last && !comp(value, *first) ? first : last;
}

에서 https://en.cppreference.com/w/cpp/algorithm/lower_bound


이것이 표준 라이브러리에없는 두 가지 이유를 생각할 수 있습니다. 그들은 구현하기 쉽다고 생각하지만, 값이 * first와 호환되지 않으면 operator () ()의 역 버전이 필요할 수 있기 때문일 것입니다.
user877329

1

이 기능을 확인하십시오. qBinaryFind :

RandomAccessIterator qBinaryFind ( RandomAccessIterator begin, RandomAccessIterator end, const T & value )

범위 (시작, 끝)의 이진 검색을 수행하고 값이 발생한 위치를 반환합니다. 값이 없으면 end를 반환합니다.

[시작, 끝) 범위의 항목은 오름차순으로 정렬해야합니다. qSort ()를 참조하십시오.

동일한 값이 많이 발생하면 그중 하나가 반환 될 수 있습니다. 더 세밀한 제어가 필요한 경우 qLowerBound () 또는 qUpperBound ()를 사용하십시오.

예:

QVector<int> vect;
 vect << 3 << 3 << 6 << 6 << 6 << 8;

 QVector<int>::iterator i =
         qBinaryFind(vect.begin(), vect.end(), 6);
 // i == vect.begin() + 2 (or 3 or 4)

이 함수는 Qt 라이브러리 <QtAlgorithms>의 일부인 헤더에 포함되어 있습니다.


1
불행히도이 알고리즘은 STL 컨테이너와 호환되지 않습니다.
bartolo-otrit 2013

0

std :: lower_bound () :)


OP : "지금까지 lower_bound 및 upper_bound가 실패합니다. 이유는 ..."
underscore_d

0
int BinarySearch(vector<int> array,int var)
{ 
    //array should be sorted in ascending order in this case  
    int start=0;
    int end=array.size()-1;
    while(start<=end){
        int mid=(start+end)/2;
        if(array[mid]==var){
            return mid;
        }
        else if(var<array[mid]){
            end=mid-1;
        }
        else{
            start=mid+1;
        }
    }
    return 0;
}

예 : 배열을 고려하십시오. A = [1,2,3,4,5,6,7,8,9] 처음에는 3의 인덱스를 검색한다고 가정합니다. start = 0 및 end = 9-1 = 8 이제 , start <= end; mid = 4; (5 인 array [mid])! = 3 이제 3은 5보다 작은 mid의 왼쪽에 있습니다. 따라서 배열의 왼쪽 부분 만 검색합니다. 따라서 이제 start = 0 및 end = 3입니다. mid = 2. array [mid] == 3 이후로 우리가 찾고 있던 숫자를 얻었습니다. 따라서 mid와 같은 인덱스를 반환합니다.


1
코드가 있으면 좋지만 언어를 처음 접하는 사람들을 위해 코드가 어떻게 작동하는지에 대한 간략한 설명을 제공하여 답변을 개선 할 수 있습니다.
Taegost

누군가 귀하의 게시물을 저품질로 잘못 신고했습니다 . 코드 전용 대답은 낮은 품질 아니다 . 질문에 대한 답변을 시도합니까? 그렇지 않은 경우 '답변이 아님'으로 플래그를 지정하거나 삭제를 권장합니다 (검토 대기열에있는 경우). b) 기술적으로 올바르지 않습니까? 반대 투표 또는 댓글.
Wai Ha Lee

0

범위 내의 위치를 ​​반환하는 솔루션은 반복자에 대한 연산 만 사용하여 다음과 같을 수 있습니다 (반복자가 산술이 아닌 경우에도 작동해야 함).

template <class InputIterator, typename T>
size_t BinarySearchPos(InputIterator first, InputIterator last, const T& val)
{       
    const InputIterator beginIt = first;
    InputIterator element = first;
    size_t p = 0;
    size_t shift = 0;
    while((first <= last)) 
    {
        p = std::distance(beginIt, first);
        size_t u = std::distance(beginIt, last);
        size_t m = p + (u-p)/2;  // overflow safe (p+u)/2
        std::advance(element, m - shift);
        shift = m;
        if(*element == val) 
            return m; // value found at position  m
        if(val > *element)
            first = element++;
        else
            last  = element--;

    }
    // if you are here the value is not present in the list, 
    // however if there are the value should be at position u
    // (here p==u)
    return p;

}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.