Pythonには明示的なバイナリ検索アルゴリズムはありbisect
ませんが、バイナリ検索を使用して、ソートされたリスト内の要素の挿入ポイントを見つけるように設計されたモジュールがあります。これは、二分探索を実行するために「だまされ」ます。これの最大の利点は、ほとんどのライブラリコードが持つ利点と同じです-高性能で十分にテストされており、機能します(特に、エッジケースを慎重に検討しないと、バイナリ検索を正常に実装するのは非常に難しい場合があります)。
基本的なタイプ
Stringsやintsのような基本的なタイプの場合、それは非常に簡単です。必要なのはbisect
モジュールとソートされたリストだけです。
>>> import bisect
>>> names = ['bender', 'fry', 'leela', 'nibbler', 'zoidberg']
>>> bisect.bisect_left(names, 'fry')
1
>>> keyword = 'fry'
>>> x = bisect.bisect_left(names, keyword)
>>> names[x] == keyword
True
>>> keyword = 'arnie'
>>> x = bisect.bisect_left(names, keyword)
>>> names[x] == keyword
False
これを使用して重複を見つけることもできます。
...
>>> names = ['bender', 'fry', 'fry', 'fry', 'leela', 'nibbler', 'zoidberg']
>>> keyword = 'fry'
>>> leftIndex = bisect.bisect_left(names, keyword)
>>> rightIndex = bisect.bisect_right(names, keyword)
>>> names[leftIndex:rightIndex]
['fry', 'fry', 'fry']
もちろん、必要に応じて、インデックスの値ではなくインデックスを返すこともできます。
オブジェクト
カスタム型またはカスタムオブジェクトの場合、少し注意が必要です。bisectを正しく比較するには、豊富な比較メソッドを実装する必要があります。
>>> import bisect
>>> class Tag(object): # a simple wrapper around strings
... def __init__(self, tag):
... self.tag = tag
... def __lt__(self, other):
... return self.tag < other.tag
... def __gt__(self, other):
... return self.tag > other.tag
...
>>> tags = [Tag('bender'), Tag('fry'), Tag('leela'), Tag('nibbler'), Tag('zoidbe
rg')]
>>> key = Tag('fry')
>>> leftIndex = bisect.bisect_left(tags, key)
>>> rightIndex = bisect.bisect_right(tags, key)
>>> print([tag.tag for tag in tags[leftIndex:rightIndex]])
['fry']
これは少なくともPython 2.7-> 3.3で動作するはずです