diff --git a/sortedcontainers/__init__.py b/sortedcontainers/__init__.py index a141dd1..8f973e1 100644 --- a/sortedcontainers/__init__.py +++ b/sortedcontainers/__init__.py @@ -34,6 +34,9 @@ SortedSet(['a', 'b', 'c', 'd', 'r']) >>> ss.bisect_left('c') 2 + >>> sa = SortedArray('i', [5,7,3,6,2,1,4]) + >>> sa + SortedArray('i', [1, 2, 3, 4, 5, 6, 7]) Sorted Containers takes all of the work out of Python sorted types - making your deployment and use of Python easy. There's no need to install a C compiler @@ -54,6 +57,7 @@ SortedItemsView, SortedValuesView, ) +from .sortedarray import SortedArray __all__ = [ 'SortedList', @@ -64,6 +68,7 @@ 'SortedItemsView', 'SortedValuesView', 'SortedSet', + 'SortedArray' ] __title__ = 'sortedcontainers' diff --git a/sortedcontainers/sortedarray.py b/sortedcontainers/sortedarray.py new file mode 100644 index 0000000..fa84bf0 --- /dev/null +++ b/sortedcontainers/sortedarray.py @@ -0,0 +1,143 @@ +"""Sorted Array +=============== + + +:doc:`Sorted Containers` is an Apache2 licensed Python sorted +collections library, written in pure-Python, and fast as C-extensions. The +:doc:`introduction` is the best way to get started. + +Sorted list implementations: + +.. currentmodule:: sortedcontainers + +* :class:`SortedArray` + +""" +# pylint: disable=too-many-lines +from __future__ import print_function +from sys import hexversion + +from .sortedlist import SortedList, recursive_repr +from array import array + +class SortedArray(SortedList): + """Sorted array is a sorted mutable sequence. + + Sorted array values are maintained in sorted order. + + Underlying data structure is the standard library array.array + Enables densly packed lists floats and doubles. + Enables densly packed lists of integers in CPython. + + Methods for adding values: + + * :func:`SortedArray.add` + * :func:`SortedArray.update` + * :func:`SortedArray.__add__` + * :func:`SortedArray.__iadd__` + * :func:`SortedArray.__mul__` + * :func:`SortedArray.__imul__` + + Methods for removing values: + + * :func:`SortedArray.clear` + * :func:`SortedArray.discard` + * :func:`SortedArray.remove` + * :func:`SortedArray.pop` + * :func:`SortedArray.__delitem__` + + Methods for looking up values: + + * :func:`SortedArray.bisect_left` + * :func:`SortedArray.bisect_right` + * :func:`SortedArray.count` + * :func:`SortedArray.index` + * :func:`SortedArray.__contains__` + * :func:`SortedArray.__getitem__` + + Methods for iterating values: + + * :func:`SortedArray.irange` + * :func:`SortedArray.islice` + * :func:`SortedArray.__iter__` + * :func:`SortedArray.__reversed__` + + Methods for miscellany: + + * :func:`SortedArray.copy` + * :func:`SortedArray.__len__` + * :func:`SortedArray.__repr__` + * :func:`SortedArray._check` + * :func:`SortedArray._reset` + + Sorted lists use lexicographical ordering semantics when compared to other + sequences. + + Some methods of mutable sequences are not supported and will raise + not-implemented error. + + """ + DEFAULT_LOAD_FACTOR = 1000 + + + def __init__(self, typecode, initializer=None): + """Initialize sorted array instance. + + Optional `iterable` argument provides an initial iterable of values to + initialize the sorted array. + + Runtime complexity: `O(n*log(n))` + + >>> sl = SortedArray('i') + >>> sl + SortedArray('i', []) + >>> sl = SortedArray('i', [3, 1, 2, 5, 4]) + >>> sl + SortedArray('i', [1, 2, 3, 4, 5]) + + :param typecode: type code for the array, as in the array.array standard library class (required) + :param iterable: initial values (optional) + + """ + self._typecode = typecode + if hexversion >= 0x03000000: + super().__init__(iterable=initializer, key=None) + else: + super(SortedArray, self).__init__(iterable=initializer, key=None) + + + def __new__(cls, typecode, initializer=None): + # pylint: disable=unused-argument + if hexversion >= 0x03000000: + return super().__new__(cls, iterable=initializer, key=None) + else: + return super(SortedArray, cls).__new__(cls, iterable=initializer, key=None) + + + def _new_list(self): + _typecode = self._typecode + return array(_typecode) + + + def _sort_in_place(self, _list): + # array.array does not support sort in place + sorted_list = sorted(_list) + del _list[:] + _list.extend(sorted_list) + + + @recursive_repr() + def __repr__(self): + """Return string representation of sorted array. + + ``sa.__repr__()`` <==> ``repr(sa)`` + + :return: string representation + + >>> sa = SortedArray('i',[5,4,3]) + >>> sa + SortedArray('i', [3, 4, 5]) + """ + class_name = type(self).__name__ + _typecode = self._typecode + return "{0}('{1}', {2!r})".format(class_name, _typecode, list(self)) diff --git a/sortedcontainers/sortedlist.py b/sortedcontainers/sortedlist.py index e3b58eb..ee202a3 100644 --- a/sortedcontainers/sortedlist.py +++ b/sortedcontainers/sortedlist.py @@ -160,10 +160,11 @@ def __init__(self, iterable=None, key=None): """ assert key is None + _new_list = self._new_list self._len = 0 self._load = self.DEFAULT_LOAD_FACTOR self._lists = [] - self._maxes = [] + self._maxes = _new_list() self._index = [] self._offset = 0 @@ -201,6 +202,14 @@ def __new__(cls, iterable=None, key=None): raise TypeError('inherit SortedKeyList for key argument') + def _new_list(self): + return [] + + + def _sort_in_place(self, _list): + _list.sort() + + @property def key(self): # pylint: disable=useless-return """Function used to extract comparison key from values. @@ -229,7 +238,8 @@ def _reset(self, load): :param int load: load-factor for sorted list sublists """ - values = reduce(iadd, self._lists, []) + _new_list = self._new_list + values = reduce(iadd, self._lists, _new_list()) self._clear() self._load = load self._update(values) @@ -280,7 +290,10 @@ def add(self, value): self._expand(pos) else: - _lists.append([value]) + _new_list = self._new_list + new_list = _new_list() + new_list.append(value) + _lists.append(new_list) _maxes.append(value) self._len += 1 @@ -335,13 +348,16 @@ def update(self, iterable): """ _lists = self._lists _maxes = self._maxes - values = sorted(iterable) + _new_list = self._new_list + _sort_in_place = self._sort_in_place + values = _new_list() + values.extend(iterable) if _maxes: if len(values) * 4 >= self._len: _lists.append(values) - values = reduce(iadd, _lists, []) - values.sort() + values = reduce(iadd, _lists, _new_list()) + _sort_in_place(values) self._clear() else: _add = self.add @@ -350,6 +366,7 @@ def update(self, iterable): return _load = self._load + _sort_in_place(values) _lists.extend(values[pos:(pos + _load)] for pos in range(0, len(values), _load)) _maxes.extend(sublist[-1] for sublist in _lists) @@ -1471,7 +1488,8 @@ def __add__(self, other): :return: new sorted list """ - values = reduce(iadd, self._lists, []) + _new_list = self._new_list + values = reduce(iadd, self._lists, _new_list()) values.extend(other) return self.__class__(values) @@ -1515,7 +1533,8 @@ def __mul__(self, num): :return: new sorted list """ - values = reduce(iadd, self._lists, []) * num + _new_list = self._new_list + values = reduce(iadd, self._lists, _new_list()) * num return self.__class__(values) __rmul__ = __mul__ @@ -1537,7 +1556,8 @@ def __imul__(self, num): :return: existing sorted list """ - values = reduce(iadd, self._lists, []) * num + _new_list = self._new_list + values = reduce(iadd, self._lists, _new_list()) * num self._clear() self._update(values) return self @@ -1593,7 +1613,8 @@ def comparer(self, other): def __reduce__(self): - values = reduce(iadd, self._lists, []) + _new_list = self._new_list + values = reduce(iadd, self._lists, _new_list()) return (type(self), (values,))