Python には明示的な二分探索アルゴリズムはありませんがbisect
、二分探索を使用してソートされたリスト内の要素の挿入ポイントを見つけるように設計されたモジュールがあります。これは、バイナリ検索を実行するように「だまされる」可能性があります。これの最大の利点は、ほとんどのライブラリ コードが持っているのと同じ利点です。パフォーマンスが高く、十分にテストされており、問題なく機能します (特にバイナリ検索は、特にエッジ ケースが慎重に考慮されていない場合、うまく実装するのが非常に難しい場合があります)。
基本タイプ
String や int などの基本的な型の場合は非常に簡単です。必要なのは、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']
明らかに、必要に応じて、そのインデックスの値ではなくインデックスを返すことができます。
オブジェクト
カスタム型またはカスタム オブジェクトの場合、少し複雑です。正しく比較するには、豊富な比較メソッドを実装する必要があります。
>>> 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 で動作するはずです。