Example #1
0
class TwoSum:
    """Stores a set of integers and returns the pairs of values that sum to a 
    specified total.
    
    """
    def __init__(self, startList=None):
        """Initialize set of integers.
        
        """
        self._intSet = SortedSet()
        if startList is not None: self.addIntegerList(startList)

    def addIntegerList(self, intList):
        """Add integers from list to set
        
        """
        for i in intList:
            self.addInteger(i)

    def addInteger(self, integer):
        """Add a single integer to set
        
        """
        self._intSet.add(integer)

    def findSums(self, totalLow, totalHigh, requireUnique=False):
        """Return set of totals in range for which at least one pair of
        integers in set existins such that x + y = total.
        
        Optional parameter for uniqueness will exclude x + x = integer.
        """
        # Flip total high and low if passed out of order
        if totalLow > totalHigh:
            swap = totalLow
            totalLow = totalHigh
            totalHigh = swap

        # Initalize empty found totals set
        foundTotals = set()

        # Iterate over each integer in the set
        for x in self._intSet:
            # Determine search range for y
            # NOTE: start index is inclusive where stop index is exclusive
            # to match behavior of SortedSet.islice()
            iStart = self._intSet.bisect_left(totalLow - x)
            iStop = self._intSet.bisect_right(totalHigh - x)

            # Continue if search range invalid
            if iStart >= iStop: continue

            # Add each sum x + y to found totals
            for y in self._intSet.islice(iStart, iStop):
                foundTotals.add(x + y)

        # Return found totals set
        return foundTotals
 def maxSumSubmatrix(self, matrix: List[List[int]], k: int) -> int:
     m, n = len(matrix), len(matrix[0])
     res = float("-inf")
     for row_start in range(m):
         prefix_sum = [0] * n
         for row_end in range(row_start, m):
             row_prefix_sum = 0
             for col in range(n):
                 row_prefix_sum += matrix[row_end][col]
                 prefix_sum[col] += row_prefix_sum
             seen = SortedSet([0])
             for num in prefix_sum:
                 index = seen.bisect_right(num - k - 1)
                 if index < len(seen):
                     res = max(res, num - seen[index])
                 seen.add(num)
     return res
Example #3
0
def concert_tickets(n, m, tickets, c_price):
    tickets.sort()
    cnt = Counter(tickets)
    t_values = SortedSet(cnt.keys())
    t_values.add(-1)
    res = ''
    for c in c_price:
        i = t_values.bisect_right(c) - 1
        if t_values[i] <= c:
            p = t_values[i]
        else:
            i = i - 1
            p = t_values[i]
        if cnt[p] == 0:
            res += '-1\n'
        else:
            res += str(p) + '\n'
            cnt[p] -= 1
            if cnt[p] == 0:
                cnt.pop(p)
                t_values.pop(i)
    return res
def test_bisect():
    temp = SortedSet(range(100), load=7)
    assert all(temp.bisect_left(val) == val for val in range(100))
    assert all(temp.bisect(val) == val for val in range(100))
    assert all(temp.bisect_right(val) == (val + 1) for val in range(100))
Example #5
0
import sys
INT_MAX = sys.maxsize
from sortedcontainers import SortedSet

s = SortedSet()  # treeset

cmd = list(map(int, input().split()))

result = INT_MAX
for i in range(cmd[0]):
    num = int(input())
    bigNum = num + cmd[1]
    smallNum = num - cmd[1]
    bigIdx = s.bisect_left(bigNum)
    smallIdx = s.bisect_right(smallNum) - 1
    if smallIdx < 0 and bigIdx == len(s):
        s.add(num)
        continue
    elif bigIdx == len(s):
        temp = num - s[smallIdx]
    elif smallIdx < 0:
        temp = s[bigIdx] - num
    else:
        smallTemp = num - s[smallIdx]
        bigTemp = s[bigIdx] - num
        temp = min(smallTemp, bigTemp)
    if result > temp:
        result = temp
    s.add(num)
if result == INT_MAX:
Example #6
0
def test_bisect():
    temp = SortedSet(range(100))
    temp._reset(7)
    assert all(temp.bisect_left(val) == val for val in range(100))
    assert all(temp.bisect(val) == (val + 1) for val in range(100))
    assert all(temp.bisect_right(val) == (val + 1) for val in range(100))
Example #7
0
class Selection(IMutableGSlice):
    def __init__(
            self,
            universe: slice,
            revealed: list = None,
            intervals: Iterator = None,
            _length: Optional[int] = None  # For performance
    ):
        #assert isinstance(universe, slice)  # Should universe even be visible/exist?
        #assert universe.start == 0
        #assert isinstance(universe.stop, int)
        #assert universe.stop >= 1  # TODO Do we need this?
        self.universe = universe
        if intervals is None and revealed is None:
            self._intervals = self.revealed2sortedset([slice(0, universe.stop)])
        elif intervals is not None:
            self._intervals = SortedSet(intervals)
        else:
            self._intervals = self.revealed2sortedset(revealed)
        self._revealed_count = _length if isinstance(_length, int) else Selection._compute_len(self._intervals)

    @staticmethod
    def revealed2sortedset(revealed: List[Union[tuple, slice]]) -> SortedSet:
        """ Converts a list of included pairs to a sorted set of integers in O(n), n = size of @slices.
        Every number from every slice is added to the sorted set, except 0.
        """
        # 10, [] -> 10, []
        # 10, [(0, 10)] -> 10, [10]
        # 10, [(0, 7)] -> 10, [7]
        # 10, [(7, 10)] -> 10, [7, 10]
        # 10, [(3, 7)] -> 10, [3, 7]
        # 10, [(0, 3), (7, 10)] -> 10, [3, 7, 10]
        # 10, [(0, 1), (2, 3), (4, 5), (6, 7), (8, 9)] -> 10, [1, 2, 3, 4, 5, 6, 7, 8, 9]

        try:
            #intervals = SortedSet(a for a, _ in revealed).union(b for _, b in revealed)
            intervals = SortedSet()
            for a, b in revealed:
                intervals.add(a)
                intervals.add(b)
        except TypeError:  # slice
            intervals = SortedSet(sl.start for sl in revealed).union(sl.stop for sl in revealed)
        if 0 in intervals:
            intervals.remove(0)
        return intervals

    @staticmethod
    def sortedset2slices(sortedset: SortedSet) -> List[slice]:
        """ Converts a sorted set of integers to a list of included slices in O(n), n = size of @sortedset.
        If there is an even number of elements in @sortedset, the first slice is formed by the first and second
        numbers, the second slice is formed by the third and fourth numbers, and so on.
        If there is an odd number of elements in @sortedset, the pair consisting of the number 0 and the first element
        in @sortedset becomes the first slice in the output list. The remaining slices, if any, are formed by the
        second and third numbers, the fourth and fifth numbers, and so on.
        """
        slices = []
        if len(sortedset) % 2 == 0:
            for i in range(0, len(sortedset), 2):
                slices.append(slice(sortedset[i], sortedset[i + 1]))
        else:
            slices.append(slice(0, sortedset[0]))
            for i in range(1, len(sortedset), 2):
                slices.append(slice(sortedset[i], sortedset[i + 1]))
        return slices

    def slices(self) -> List[slice]:
        return self.sortedset2slices(self._intervals)

    def pairs(self) -> Iterator[Tuple[int, int]]:
        if len(self._intervals) % 2 == 0:
            return zip(self._intervals[::2], self._intervals[1::2])
        return itertools.chain([(0, self._intervals[0])], zip(self._intervals[1::2], self._intervals[2::2]))

    def gap_pairs(self) -> Iterator[Tuple[int, int]]:
        return self.complement().pairs()

    def intervals(self):
        return self._intervals

    def exclude(self, from_index: Optional[int], to_index: Optional[int]):
        original_length = self._revealed_count
        if isinstance(from_index, int) and -self.universe.stop <= from_index < 0:
            from_index = from_index % self.universe.stop
        if isinstance(to_index, int):
            if to_index > self.universe.stop:
                return self.exclude(from_index, None)
            if -self.universe.stop <= to_index < 0:
                to_index = to_index % self.universe.stop
        assert from_index is None or self.universe.start <= from_index <= self.universe.stop
        assert to_index is None or self.universe.start <= to_index <= self.universe.stop
        if from_index is None:
            from_index = self.universe.start
        if to_index is None:
            to_index = self.universe.stop
        if len(self._intervals) == 0:
            return 0
        if from_index >= to_index:
            return 0

        m = self._intervals.bisect_right(from_index)
        n = self._intervals.bisect_right(to_index)

        try:
            from_index_index = self._intervals.index(from_index)
        except ValueError:
            from_index_index = None
        try:
            to_index_index = self._intervals.index(to_index)
        except ValueError:
            to_index_index = None
        from_index_is_included = (
            len(self._intervals) % 2 == 0 and m % 2 == 1 or len(self._intervals) % 2 == 1 and m % 2 == 0)
        to_index_is_included = (
            len(self._intervals) % 2 == 0 and n % 2 == 1 or len(self._intervals) % 2 == 1 and n % 2 == 0)
        from_index_is_leftmost_included = from_index == 0 and from_index_is_included or from_index_index is not None and (
                len(self._intervals) % 2 == 0 and from_index_index % 2 == 0
                or len(self._intervals) % 2 == 1 and (from_index == 0 or from_index_index % 2 == 1))
        to_index_right_of_excluded = to_index_index is not None and (
                len(self._intervals) % 2 == 0 and to_index_index % 2 == 1
                or len(self._intervals) % 2 == 1 and (to_index == 0 or to_index_index % 2 == 0))

        if from_index_is_included:
            if from_index_is_leftmost_included:
                if to_index_is_included:
                    if m == 0:
                        to_remove = self._intervals[m:n]
                        endpoint = 0 if n == 0 else self._intervals[n - 1]
                        addendum = 0 if n == 0 else self._intervals[0]
                        self._revealed_count -= (to_index - endpoint) + addendum + sum(
                            b - a for a, b in zip(to_remove[1:-1:2], to_remove[2:-1:2]))
                        del self._intervals[m:n]
                        self._intervals.add(to_index)
                    else:
                        intermediates = self._intervals[m + 1:n - 1]
                        from_start, from_end = self._intervals[m - 1], self._intervals[m]
                        to_start, to_end = self._intervals[n - 1], self._intervals[n]
                        if m == n:
                            self._revealed_count -= to_index - from_start
                            self._intervals.remove(from_start)
                            self._intervals.add(to_index)
                        else:
                            self._revealed_count -= (from_end - from_start) + (to_index - self._intervals[n - 1]) + (
                                from_index - from_start) + sum(
                                b - a for a, b in zip(intermediates[::2], intermediates[1::2]))
                            del self._intervals[m + 1:n - 1]  # intermediates
                            self._intervals.remove(from_start)
                            self._intervals.remove(from_end)
                            self._intervals.remove(to_start)
                            self._intervals.add(to_index)
                else:
                    from_start = 0 if m == 0 else self._intervals[m - 1]
                    from_end = self._intervals[m]
                    self._revealed_count -= from_end - from_start
                    if from_start > 0:
                        self._intervals.remove(from_start)
                    self._intervals.remove(from_end)
            else:
                if to_index_is_included:
                    from_end = self._intervals[m]
                    to_start = self._intervals[n - 1]
                    if m == n:
                        self._revealed_count -= to_index - from_index
                        if from_index > 0:
                            self._intervals.add(from_index)
                        self._intervals.add(to_index)
                    else:
                        intermediates = self._intervals[m + 1:n - 1]
                        self._revealed_count -= (from_end - from_index) + (to_index - to_start) + sum(
                            b - a for a, b in zip(intermediates[::2], intermediates[1::2]))
                        del self._intervals[m + 1:n - 1]  # intermediates
                        if from_index > 0:
                            self._intervals.add(from_index)
                        self._intervals.add(to_index)
                        self._intervals.remove(from_end)
                        self._intervals.remove(to_start)
                else:
                    to_remove = self._intervals[m:n]
                    self._revealed_count -= self._intervals[m] - from_index + sum(b - a for a, b in zip(to_remove[1::2], to_remove[::2]))
                    del self._intervals[m:n]
                    if from_index != 0:
                        self._intervals.add(from_index)
        else:
            if to_index_is_included:
                if to_index_right_of_excluded:
                    to_remove = self._intervals[m:n - 1]
                    del self._intervals[m:n - 1]
                    self._revealed_count -= sum(b - a for a, b in zip(to_remove[::2], to_remove[1::2]))
                else:
                    to_remove = self._intervals[m:n]
                    del self._intervals[m:n]
                    self._intervals.add(to_index)
                    self._revealed_count -= (to_index - to_remove[0]) + sum(b - a for a, b in zip(to_remove[1::2], to_remove[::2]))
            else:
                to_remove = self._intervals[m:n]
                del self._intervals[m:n]
                self._revealed_count -= sum(b - a for a, b in zip(to_remove[::2], to_remove[1::2]))

        return original_length - self._revealed_count

    def exclude_virtual(self, from_index: Optional[int], to_index: Optional[int]):
        if from_index is None or from_index < -len(self) or from_index >= len(self):
            p_from_index = None
        else:
            p_from_index = self.virtual2physical(from_index)
        if to_index is None or to_index < -len(self) or to_index >= len(self):
            p_to_index = None
        else:
            p_to_index = self.virtual2physical(to_index)
        return self.exclude(p_from_index, p_to_index)

    def include(self, from_index: Optional[int], to_index: Optional[int]):
        original_length = len(self)
        if isinstance(from_index, int) and -self.universe.stop <= from_index < 0:
            from_index = from_index % self.universe.stop
        if isinstance(to_index, int):
            if to_index > self.universe.stop:
                return self.include(from_index, None)
            if -self.universe.stop <= to_index < 0:
                to_index = to_index % self.universe.stop
        assert from_index is None or self.universe.start <= from_index <= self.universe.stop
        assert to_index is None or self.universe.start <= to_index <= self.universe.stop
        if from_index is None:
            from_index = self.universe.start
        if to_index is None:
            to_index = self.universe.stop
        if not self._intervals:
            if from_index > 0:
                self._intervals.add(from_index)
            self._intervals.add(to_index)
            self._revealed_count += to_index - from_index
            return to_index - from_index
        if from_index == to_index:
            return 0

        m = self._intervals.bisect_right(from_index)
        n = self._intervals.bisect_right(to_index)

        try:
            from_index_index = self._intervals.index(from_index)
        except ValueError:
            from_index_index = None

        from_index_is_included = (
                len(self._intervals) % 2 == 0 and m % 2 == 1 or len(self._intervals) % 2 == 1 and m % 2 == 0)
        to_index_is_included = (
                len(self._intervals) % 2 == 0 and n % 2 == 1 or len(self._intervals) % 2 == 1 and n % 2 == 0)
        from_index_right_of_included = from_index_index is not None and (
                len(self._intervals) % 2 == 0 and from_index_index % 2 == 1
                or len(self._intervals) % 2 == 1 and from_index_index % 2 == 0)

        if from_index_is_included:
            if to_index_is_included:
                to_remove = self._intervals[m:n]
                del self._intervals[m:n]
                self._revealed_count += sum(b - a for a, b in zip(to_remove[::2], to_remove[1::2]))
            else:
                to_remove = self._intervals[m:n]
                del self._intervals[m:n]
                self._intervals.add(to_index)
                self._revealed_count += (to_index - to_remove[-1]) + sum(b - a for a, b in zip(to_remove[1::2], to_remove[::2]))
        else:
            if to_index_is_included:
                if from_index_right_of_included:
                    to_remove = self._intervals[m - 1:n]
                    del self._intervals[m - 1:n]
                    self._revealed_count += sum(b - a for a, b in zip(to_remove[::2], to_remove[1::2]))
                else:
                    to_remove = self._intervals[m:n]
                    del self._intervals[m:n]
                    self._intervals.add(from_index)
                    self._revealed_count += (to_remove[0] - from_index) + sum(b - a for a, b in zip(to_remove[1::2], to_remove[::2]))
            else:
                if from_index_right_of_included:
                    intermediates = self._intervals[m:n]
                    del self._intervals[m:n]  # intermediates
                    self._intervals.remove(from_index)
                    self._intervals.add(to_index)
                    self._revealed_count += (to_index - from_index) - sum(b - a for a, b in zip(intermediates[::2], intermediates[1::2]))
                else:
                    to_remove = self._intervals[m:n]
                    del self._intervals[m:n]
                    if from_index > 0:
                        self._intervals.add(from_index)
                    self._intervals.add(to_index)
                    self._revealed_count += (to_index - from_index) - sum(b - a for a, b in zip(to_remove[::2], to_remove[1::2]))

        return len(self) - original_length

    def include_partially(self, from_index: Optional[int], to_index: Optional[int], count: Union[int, tuple]):
        if isinstance(count, int):
            return self.include_partially(from_index, to_index, (count, count))
        head_count, tail_count = count
        head_revealed_count = self._include_partially_from_left(from_index, to_index, head_count)
        tail_revealed_count = self._include_partially_from_right(from_index, to_index, tail_count)
        return head_revealed_count + tail_revealed_count

    def _include_partially_from_left(self, from_index: int, to_index: int, count: int):
        if count == 0:
            return 0
        from_index, to_index = self._normalized_range(from_index, to_index)
        subsel = self._spanning_subslice(from_index, to_index).complement().subslice(from_index, to_index)

        revealed_count = 0
        for covered_start, covered_stop in subsel.pairs():
            coverage = covered_stop - covered_start
            if revealed_count + coverage < count:
                self.include(covered_start, covered_stop)
                revealed_count += coverage
            else:
                self.include(covered_start, covered_start + count - revealed_count)
                revealed_count = count
                break
        return revealed_count

    def _include_partially_from_right(self, from_index: int, to_index: int, count: int):
        if count == 0:
            return 0
        from_index, to_index = self._normalized_range(from_index, to_index)
        subsel = self._spanning_subslice(from_index, to_index).complement().subslice(from_index, to_index)

        revealed_count = 0
        for covered_start, covered_stop in reversed(list(subsel.pairs())):
            coverage = covered_stop - covered_start
            if revealed_count + coverage < count:
                self.include(covered_start, covered_stop)
                revealed_count += coverage
            else:
                self.include(covered_stop - (count - revealed_count), covered_stop)
                revealed_count = count
                break
        return revealed_count

    def include_expand(self, from_index: Optional[int], to_index: Optional[int], count: Union[int, Tuple[int, int]]):
        if isinstance(count, int):
            return self.include_expand(from_index, to_index, (count, count))
        if count == (0, 0):
            return 0
        head_count, tail_count = count
        revealed_counter = 0
        gaps = self.complement().subslice(from_index, to_index)
        for a, b in gaps.pairs():
            if b < self.universe.stop:
                revealed_counter += self._include_partially_from_right(a, b, head_count)
            if a > self.universe.start:
                revealed_counter += self._include_partially_from_left(a, b, tail_count)
        return revealed_counter

    def _previous_slice(self, sl: slice):
        """ :return The revealed or covered slice immediately to the left of @sl.
        :raise ValueError if there is none. """
        if sl.start == self.universe.start:
            raise ValueError("There is no slice to the left of {}.".format(sl))
        # TODO O(n) -> O(1)
        zero_or_one = [s for s in self._intervals + self.complement()._intervals if s.stop == sl.start]
        if len(zero_or_one) == 1:
            return zero_or_one[0]
        else:
            raise ValueError("Slice not found: {}.".format(sl))

    def _next_slice(self, sl: slice):
        """ :return The revealed or covered slice immediately to the right of @sl.
        :raise ValueError if there is none. """
        if sl.stop == self.universe.stop:
            raise ValueError("There is no slice to the right of {}.".format(sl))
        # TODO O(n)
        zero_or_one = [s for s in self._intervals + self.complement()._intervals if s.start == sl.stop]
        if len(zero_or_one) == 1:
            return zero_or_one[0]
        else:
            raise ValueError("Slice not found: {}.".format(sl))

    def include_virtual(self, from_index, to_index):
        if from_index is None or from_index < -len(self) or from_index >= len(self):
            p_from_index = None
        else:
            p_from_index = self.virtual2physical(from_index)
        if to_index is None or to_index < -len(self) or to_index >= len(self):
            p_to_index = None
        else:
            p_to_index = self.virtual2physical(to_index)
        return self.include(p_from_index, p_to_index)

    def include_partially_virtual(self, from_index: Optional[int], to_index: Optional[int], count: Union[int, tuple]):
        if from_index is None or from_index < -len(self) or from_index >= len(self):
            p_from_index = None
        else:
            p_from_index = self.virtual2physical(from_index)
        if to_index is None or to_index < -len(self) or to_index >= len(self):
            p_to_index = None
        else:
            p_to_index = self.virtual2physical(to_index)
        return self.include_partially(p_from_index, p_to_index, count)

    # FIXME Inconsistent with reversed(selection). Should probably make this use the default implementation and instead
    # rewrite this one to iter_slices or something.
    def __iter__(self):
        for a, b in self.pairs():
            yield a, b  # FIXME should probably generate slices instead, or every index

    def complement(self):
        if len(self._intervals) >= 1 and self._intervals[-1] == self.universe.stop:
            return Selection(universe=self.universe, intervals=self._intervals[:-1],
                             _length=self.universe.stop - len(self))
        return Selection(universe=self.universe, intervals=self._intervals.union([self.universe.stop]),
                         _length=self.universe.stop - len(self))

    def _normalized_range(self, from_index: Optional[int], to_index: Optional[int]) -> Tuple[int, int]:
        """ For any range [@from_index, @to_index) where the indices are either None or any integer, returns the
        equivalent range [x, y) such that either 0 <= x < y <= upper_bound or x = y = 0. The ranges are equivalent in
        the sense that when using them to slice this selection, they produce the same sub-selection. """
        if from_index is None or from_index <= -self.universe.stop:
            from_index = self.universe.start
        elif from_index > self.universe.stop:
            from_index = self.universe.stop
        elif -self.universe.stop <= from_index < 0:
            from_index = self.universe.stop - from_index

        if to_index is None or to_index >= self.universe.stop:
            to_index = self.universe.stop
        elif -self.universe.stop <= to_index < 0:
            to_index = self.universe.stop - to_index
        elif to_index < -self.universe.stop:
            to_index = self.universe.start

        if from_index >= to_index:
            from_index, to_index = (0, 0)
        return from_index, to_index

    def subslice(self, from_index: Optional[int], to_index: Optional[int]):
        from_index, to_index = self._normalized_range(from_index, to_index)
        sel = self._spanning_subslice(from_index, to_index)
        if len(sel._intervals) % 2 == 0:
            if len(sel) > 0:
                if sel._intervals[0] < from_index < sel._intervals[1]:
                    sel._revealed_count -= from_index - sel._intervals[0]
                    del sel._intervals[0]
                    sel._intervals.add(from_index)
                if sel._intervals[-2] < to_index < sel._intervals[-1]:
                    sel._revealed_count -= sel._intervals[-1] - to_index
                    del sel._intervals[-1]
                    sel._intervals.add(to_index)
        else:
            if 0 < from_index < sel._intervals[0]:
                sel._revealed_count -= from_index
                sel._intervals.add(from_index)
            if (len(sel._intervals) == 1 and to_index < sel._intervals[-1]
                    or len(sel._intervals) >= 2 and sel._intervals[-2] < to_index < sel._intervals[-1]):
                sel._revealed_count -= sel._intervals[-1] - to_index
                del sel._intervals[-1]
                sel._intervals.add(to_index)
        return sel

    def _spanning_subslice(self, from_index: int, to_index: int):
        """ :return A Selection whose set of revealed slices is a subset of that of this Selection such that every index
        in [from_index, to_index) is either on some slice in the subset, or on a gap. """
        if from_index >= to_index:
            return Selection(universe=deepcopy(self.universe), intervals=[])
        m = self._intervals.bisect_right(from_index)
        if len(self._intervals) % 2 == 0:
            n = self._intervals.bisect_left(to_index)
            intervals = self._intervals[m - (m % 2):n + (n % 2)]
        else:
            n = self._intervals.bisect_right(to_index)
            a = max(0, m - ((m + 1) % 2))
            b = n + ((n + 1) % 2)
            intervals = self._intervals[a:b]
        sel = Selection(universe=deepcopy(self.universe), intervals=intervals)
        return sel

    def _slow_subslice(self, from_index: Optional[int], to_index: Optional[int]):
        sel = self.deepcopy()
        if isinstance(from_index, int):
            sel.exclude(None, from_index)
        if isinstance(to_index, int):
            sel.exclude(to_index, None)
        return sel

    def _interval_index(self, pindex):
        """ :return n if the nth interval edge is the smallest number such that @pindex < n (zero-indexed). """
        lower = 0
        upper = len(self._intervals) - 1
        while lower <= upper:
            middle = (lower + upper) // 2
            midsl = self._intervals[middle]
            if pindex < midsl.start:
                upper = middle - 1
            elif midsl.stop <= pindex:
                lower = middle + 1
            else:  # midsl.start <= pindex < midsl.stop:
                return middle
        raise IndexError("{} is not in any interval.".format(pindex))

    def select(self, listlike):
        # TODO only works for stringlike objects
        lst = []
        for interval in self.slices():
            lst.append(listlike[interval])
        selection = listlike[0:0].join(lst)
        return selection

    def physical2virtual(self, pindex: int):
        vindex = 0
        for a, b in self.pairs():
            if a <= pindex < b:
                vindex += pindex - a
                return vindex
            vindex += b - a
        raise IndexError("Physical index {} out of bounds for selection {}".format(pindex, self))

    # TODO: O(n) -> O(log(n)) (using another sorted set for cumulative lengths?)
    def virtual2physical(self, vindex: int):  # TODO -> virtualint2physical
        """ :return the integer n such that where the @vindex'th revealed element is the nth element. If
        @vindex < 0, @vindex is interpreted as (number of revealed elements) + @vindex.
        """
        if vindex < -len(self):
            raise IndexError(
                "Got index {}, expected it to be within range [{},{})".format(vindex, -len(self), len(self)))
        elif vindex < 0:
            return self.virtual2physical(len(self) + vindex)
        cumlength = 0
        for a, b in self.pairs():
            cumlength += b - a
            if vindex < cumlength:
                pindex = b - (cumlength - vindex)
                if a <= pindex < b:
                    return pindex
                else:
                    break
        raise IndexError("Virtual index {} out of bounds for selection {}".format(vindex, self))

    def virtual2physicalselection(self, vslice: slice) -> 'Selection':  # TODO -> virtualslice2physical
        """ :return the sub-Selection that is the intersection of this selection and @vslice. """
        if not self._intervals or vslice.stop == 0:
            return Selection(self.universe, revealed=[])
        if vslice.start is None:
            a = self.virtual2physical(0)
        elif -len(self) <= vslice.start < len(self):
            a = self.virtual2physical(vslice.start)
        elif vslice.start >= len(self):
            a = self._intervals[-1]
        else:
            raise ValueError("Unexpected slice start: {}".format(vslice))
        if vslice.stop is None or vslice.stop >= len(self):
            b = self._intervals[-1] - 1
        elif -len(self) <= vslice.stop < len(self):
            b = self.virtual2physical(vslice.stop - 1)
        else:
            raise ValueError("Unexpected slice stop: {}".format(vslice))
        # INV: a is the physical index of the first element, b is the physical index of the last element
        if b < a:
            return Selection(universe=self.universe, revealed=[])
        m = self._intervals.bisect_right(a)
        n = self._intervals.bisect_right(b)
        intervals = SortedSet([a] + self._intervals[m:n] + [b + 1])
        return Selection(universe=self.universe, intervals=intervals)

    def virtualselection2physical(self, vselection: 'Selection'):  # TODO -> virtualslice2physical
        """ :return the sub-Selection that is the intersection of this selection and @vselection. """
        intervals = []
        for start, stop in vselection:
            for a, b in self.virtual2physicalselection(slice(start, stop)):
                intervals.append(slice(a, b))
        return Selection(universe=self.universe, revealed=intervals)

    def stretched(self, from_index: Optional[int], to_index: Optional[int]):  # TODO remove?
        """ :return A potentially shrinked deep copy of this selection, delimited by the universe
        [@from_index, @to_index). """
        m = self._intervals.bisect_right(from_index)
        n = self._intervals.bisect_right(to_index)
        intervals = self._intervals[m:n]
        return Selection(universe=slice(from_index, to_index), intervals=intervals)

    def __getitem__(self, item):
        return self.virtual2physical(item)

    @staticmethod
    def _compute_len(sortedset: SortedSet):
        """ :return The sum of the lengths of every slice in @slicelist. """
        if len(sortedset) == 0:
            return 0
        elif len(sortedset) % 2 == 0:
            return sum(sortedset[i + 1] - sortedset[i] for i in range(0, len(sortedset), 2))
        return sortedset[0] + sum(sortedset[i + 1] - sortedset[i] for i in range(1, len(sortedset), 2))

    def __len__(self):
        return self._revealed_count

    def __eq__(self, other):
        return repr(self) == repr(other)

    def __mul__(self, other: int):
        if other == 0:
            return Selection(universe=slice(0, 0), revealed=[])
        scaled_universe = slice(self.universe.start * other, self.universe.stop * other)
        scaled_revealed = [other * x for x in self._intervals]
        return Selection(universe=scaled_universe, intervals=scaled_revealed)

    def __rmul__(self, other):
        return self.__mul__(other)

    def __repr__(self):
        return "{}(universe={}, intervals={})".format(self.__class__.__name__, self.universe, self._intervals)

    def __str__(self):
        return repr(self)

    def deepcopy(self):
        """ :return A deep copy of this object. """
        return Selection(universe=deepcopy(self.universe), intervals=deepcopy(self._intervals))
Example #8
0
def test5():
    """
    有序的集合:SortedSet
    网址:http://www.grantjenks.com/docs/sortedcontainers/sortedset.html
    """
    from sortedcontainers import SortedSet
    # 创建 SortedSet
    ss = SortedSet([3, 1, 2, 5, 4])
    print(ss)  # SortedSet([1, 2, 3, 4, 5])
    from operator import neg
    ss1 = SortedSet([3, 1, 2, 5, 4], neg)
    print(ss1)  # SortedSet([5, 4, 3, 2, 1], key=<built-in function neg>)
    # SortedSet 转为 list/tuple/set
    print(list(ss))  # SortedSet转为list    [1, 2, 3, 4, 5]
    print(tuple(ss))  # SortedSet转为tuple    (1, 2, 3, 4, 5)
    print(set(ss))  # SortedSet转为set    {1, 2, 3, 4, 5}
    # 插入、删除元素
    ss.discard(-1)  # 删除不存在的元素不报错
    ss.remove(1)  # 删除不存在的元素报错, KeyError
    ss.discard(3)  # SortedSet([1, 2, 4, 5])
    ss.add(-10)  # SortedSet([-10, 1, 2, 4, 5])
    # 返回第一个和最后一个元素
    print(ss[0])  # -10
    print(ss[-1])  # 5
    # 遍历 set
    for e in ss:
        print(e, end=", ")  # -10, 2, 4, 5,
    print()
    # set 中判断某元素是否存在
    print(2 in ss)  # True
    # bisect_left() / bisect_right()
    print(ss.bisect_left(4))  # 返回大于等于4的最小元素对应的下标    2
    print(ss.bisect_right(4))  # 返回大于4的最小元素对应的下标    3
    # 清空 set
    ss.clear()
    print(len(ss))  # 0
    print(len(ss) == 0)  # True
    """
    无序的集合: set
    """
    # 集合的定义:集合是不可变的,因此集合中元素不能是list
    A = {"hi", 2, ("we", 24)}
    B = set()  # 空集合的定义,不能使用B = {}定义集合,这样是字典的定义
    # 集合间的操作, 下面的运算法符都可以写成 op= 的形式
    print("---------------------------------------")
    S = {1, 2, 3}
    T = {3, 4, 5}
    print(S & T)  # 交集,返回一个新集合,包括同时在集合S和T中的元素
    print(S | T)  # 并集,返回一个新集合,包括在集合S和T中的所有元素
    print(S - T)  # 差集,返回一个新集合,包括在集合S但不在T中的元素
    print(S ^ T)  # 补集,返回一个新集合,包括集合S和T中的非相同元素
    # 集合的包含关系
    print("---------------------------------------")
    C = {1, 2}
    D = {1, 2}
    print(C <= D)  # C是否是D的子集  True
    print(C < D)  # C是否是D的真子集  False
    print(C >= D)  # D是否是C的子集  True
    print(C > D)  # D是否是C的真子集  False
    # 集合的处理方法
    print("---------------------------------------")
    S = {1, 2, 3, 5, 6}
    S.add(4)  # 如果x不在集合S中,将x增加到S
    S.discard(1)  # 移除S中元素x,如果x不在集合S中,不报错
    S.remove(2)  # 移除S中元素x,如果x不在集合S中,产生KeyError异常
    for e in S:  # 遍历
        print(e, end=",")
    print()
    print(S.pop())  # 从S中随机弹出一个元素,S长度减1,若S为空产生KeyError异常
    print(S.copy())  # 返回集合S的一个副本, 对该副本的操作不会影响S
    print(len(S))  # 返回集合S的元素个数
    print(5 in S)  # 判断S中元素x, x在集合S中,返回True,否则返回False
    print(5 not in S)  # 判断S中元素x, x在集合S中,返回True,否则返回False
    S.clear()  # 移除S中所有元素
Example #9
0
# 작지만 큰 숫자
## n개의 숫자로 이루어진 수열이 하나 주어지고, 그 이후 m개의 질의가 주어집니다
##  각 질의마다 하나의 숫자가 주어진다 했을 때, 순서대로 수열 내에서 주어진 숫자보다 같거나 작은 숫자들 중 최댓값을 하나 골라 제거하는 것을 반복하는 프로그램을 작성해보세요
## 단, 같거나 작은 숫자가 없는 경우에는 제거하지 않고 넘어갑니다.

from sortedcontainers import SortedSet

s = SortedSet()  # treeset

numSize = list(map(int, input().split()))
numList = list(map(int, input().split()))
numSet = SortedSet(numList)

cmd = list(map(int, input().split()))

for i in cmd:
    if i in numSet:
        print(i)
        numSet.remove(i)
    else:
        idx = numSet.bisect_right(i) - 1
        if idx < 0:
            print(-1)
        else:
            print(numSet[idx])
            numSet.remove(numSet[idx])
Example #10
0
s = SortedSet()      # treeset

cmd = list(map(int,input().split()))
peopleWant = list(map(int, input().split()))
peopleWant.sort(reverse=True)

chairSet = SortedSet()
chair = []
for i in range(cmd[1]):
    chairSet.add(i+1)

resultCount = 0




for i in peopleWant:
    idx = chairSet.bisect_right(i)-1
    if i in chairSet:
        chairSet.remove(i)
        resultCount += 1
    else:
        if idx < 0:
            #print(i)
            break
        else:
            chairSet.remove(chairSet[idx])
            resultCount += 1
            
print(resultCount)
Example #11
0
class MutableIntervalDict(
        # pylint: disable=unsubscriptable-object
        IntervalDict[atomic.TO, V],
        MutableMapping[atomic.Interval[atomic.TO], V],
):
    """
    Mutable Interval Dictionary class.

    The :class:`MutableIntervalDict` class (which inherits from the
    :class:`IntervalDict` class) is designed to hold mutable dict of disjoint sorted
    intervals.

    Note
    ----
        Les :math:`n` (or :math:`n_0`)  the number of intervals of the *self* variable
        and :math:`m` the number of intervals in the *other* variable. Let
        :math:`n_1, ... n_k` the number of intervals for methods with multiple
        arguments.

        The complexity in time of methods is:

        =========================  ====================================================
        Methods                    Average case
        =========================  ====================================================
        :meth:`__setitem__`        :math:`O(n)`
        :meth:`__delitem__`        :math:`O(n)`
        :meth:`__ior__`            :math:`O(m\\log(n+m))`
        :meth:`update`             :math:`O((\\sum_{i=1}^kn_i)\\log(\\sum_{i=0}^kn_i))`
        :meth:`clear`              :math:`O(1)`
        =========================  ====================================================
    """

    __slots__ = ("_default", "_operator", "_strict")

    def __init__(
        self,
        iterable: Optional[Union[IntervalDict[atomic.TO, V], Mapping[
            atomic.IntervalValue[atomic.TO],
            V], Iterable[Tuple[atomic.IntervalValue[atomic.TO], V]], ]] = None,
        default: Optional[Callable[[], V]] = None,
        operator: Optional[Callable[[V, V], V]] = None,
        strict: Optional[bool] = True,
    ) -> None:
        """
        Initialize a :class:`MutableIntervalDict` instance.

        Arguments
        ---------
            iterable: :class:`Iterable <python:typing.Iterable>`
                An optional iterable that can be converted to a dictionary of (
                interval, value).

        Keyword arguments
        -----------------
            default: :class:`Callable[[], V] <python:typing.Callable>`, optional
                The default factory.
            operator: :class:`Callable[[V, V], V] <python:typing.Callable>`
                The operator function.
            strict: bool
                :data:`False <python:False>` if ``operator`` is a commutative and
                associative law on ``V``.

        Note
        ----

            If  ``operator`` is a commutative and associative law on ``V``,
            the complexity in time is much faster if ``strict`` is set to
            :data:`False <python:False>`.

        Examples
        --------

            >>> from part import MutableIntervalDict
            >>> a = MutableIntervalDict[int, set](
            ...     operator=lambda x, y: x | y,
            ...     strict=False
            ... )
            >>> a.update({(1, 10): {1}})
            >>> print(a)
            {'[1;10)': {1}}
            >>> a.update({(5, 20): {2}})
            >>> print(a)
            {'[1;5)': {1}, '[5;10)': {1, 2}, '[10;20)': {2}}
            >>> a.update({(10, 30): {1}})
            >>> print(a)
            {'[1;5)': {1}, '[5;10)': {1, 2}, '[10;20)': {1, 2}, '[20;30)': {1}}
            >>> print(a.compress())
            {'[1;5)': {1}, '[5;20)': {1, 2}, '[20;30)': {1}}
        """
        super().__init__()
        self._default = default
        self._operator = operator
        self._strict = strict
        if isinstance(iterable, IntervalDict):
            self._mapping = iterable._mapping.copy()
            self._intervals = SortedSet(iterable._intervals)
        else:
            self._mapping = {}
            self._intervals: SortedSet = SortedSet()
            if isinstance(iterable, dict):
                self.update(*({key: value} for key, value in iterable.items()))
            elif iterable is not None:
                self.update(*({
                    key: value
                } for key, value in iterable))  # type: ignore

    def __getitem__(self, key: Union[slice,
                                     atomic.IntervalValue[atomic.TO]]) -> V:
        """
        Return a value using either a slice or an interval value.

        Arguments
        ---------
            key: Union[IntervalValue, slice]
                The interval requested.

        Returns
        -------
            The found value

        Raises
        ------
            KeyError
                If the *key* is out of range.
        """
        try:
            return super().__getitem__(key)
        except KeyError:
            if self._default is not None:
                value = self._default()
                self[key] = value
                return value
            raise

    def __setitem__(self, key: Union[slice, atomic.IntervalValue[atomic.TO]],
                    value: V) -> None:
        """
        Set a value using either a slice or an interval value.

        Arguments
        ---------
            key: Union[IntervalValue, slice]
                The interval requested.

        Raises
        ------
            KeyError
                If the *key* is out of range.

        Examples
        --------

            >>> from part import MutableIntervalDict
            >>> a = MutableIntervalDict[int, int](
            ...     {(10, 15): 1, (20, 25): 2, (30, 35): 3}
            ... )
            >>> print(a)
            {'[10;15)': 1, '[20;25)': 2, '[30;35)': 3}
            >>> a[12] = 4
            >>> print(a)
            {'[10;12)': 1, '[12;12]': 4, '(12;15)': 1, '[20;25)': 2, '[30;35)': 3}
            >>> a[13:31] = 5
            >>> print(a)
            {'[10;12)': 1, '[12;12]': 4, '(12;13)': 1, '[13;31)': 5, '[31;35)': 3}
            >>> a[:] = 0
            >>> print(a)
            {'(-inf;+inf)': 0}
        """
        interval = self._remove(key)
        if interval:
            self._intervals.add(interval)
            self._mapping[interval] = value

    def __delitem__(
            self, key: Union[slice, atomic.IntervalValue[atomic.TO]]) -> None:
        """
        Delete a value using either a slice or an interval value.

        Arguments
        ---------
            key: Union[IntervalValue, slice]
                The interval requested.

        Raises
        ------
            KeyError
                If the *key* is out of range.

        Examples
        --------

            >>> from part import MutableIntervalDict
            >>> a = MutableIntervalDict[int, int](
            ...     {(10, 15): 1, (20, 25): 2, (30, 35): 3}
            ... )
            >>> print(a)
            {'[10;15)': 1, '[20;25)': 2, '[30;35)': 3}
            >>> del a[12]
            >>> print(a)
            {'[10;12)': 1, '(12;15)': 1, '[20;25)': 2, '[30;35)': 3}
            >>> del a[13:31]
            >>> print(a)
            {'[10;12)': 1, '(12;13)': 1, '[31;35)': 3}
            >>> del a[:]
            >>> print(a)
            {}
        """
        self._remove(key)

    def _remove(self, key):
        # pylint: disable=protected-access
        interval = IntervalDict._interval(key)
        if interval:
            start = self._start(interval)
            stop = self._stop(interval)
            for index in range(start, stop):
                del self._mapping[self._intervals[index]]
            del self._intervals[start:stop]
        return interval

    def _add(self, interval, value):
        if self._operator is None:
            self[interval] = value
        else:
            intervals = list(self.select(interval, strict=False))
            for another in ((interval & found)[0] for found in intervals):
                self[another] = self._operator(self[another], value)
            for another in sets.FrozenIntervalSet[atomic.TO](
                [interval]) - sets.FrozenIntervalSet[atomic.TO](intervals):
                self[another] = value

    def __or__(self, other) -> "MutableIntervalDict[atomic.TO, V]":
        """
        Construct a new dictionary using self and the *other*.

        Arguments
        ---------
            other: :class:`IntervalDict`
                Another interval dict.

        Returns
        -------
            :class:`MutableIntervalDict`
                The new :class:`IntervalDict`.

        Examples
        --------

            >>> from part import MutableIntervalDict
            >>> a = MutableIntervalDict[int, int](
            ...     {(10, 15): 1, (20, 25): 2, (30, 35): 3},
            ...     operator=lambda x, y: x + y,
            ...     strict=False
            ... )
            >>> print(a | FrozenIntervalDict[int, int]({(15, 22): 4}))
            {'[10;15)': 1, '[15;20)': 4, '[20;22)': 6, '[22;25)': 2, '[30;35)': 3}
        """
        if not isinstance(other, IntervalDict):
            return NotImplemented
        result = self.__class__(self,
                                default=self._default,
                                operator=self._operator,
                                strict=self._strict)
        result.update(other)
        return result

    def __ior__(self, other) -> "MutableIntervalDict[atomic.TO, V]":
        """
        Update self with the *other*.

        Arguments
        ---------
            other: :class:`IntervalDict`
                Another interval dict.

        Returns
        -------
            :class:`MutableIntervalDict`
                The updated :class:`MutableIntervalDict`.

        Examples
        --------

            >>> from part import MutableIntervalDict
            >>> a = MutableIntervalDict[int, int](
            ...     operator=lambda x, y: x + y,
            ...     strict=False
            ... )
            >>> a |= MutableIntervalDict[int, int]({(1, 10): 1})
            >>> print(a)
            {'[1;10)': 1}
            >>> a |= MutableIntervalDict[int, int]({(5, 20): 2})
            >>> print(a)
            {'[1;5)': 1, '[5;10)': 3, '[10;20)': 2}
            >>> a |= MutableIntervalDict[int, int]({(10, 30): 3})
            >>> print(a)
            {'[1;5)': 1, '[5;10)': 3, '[10;20)': 5, '[20;30)': 3}
        """
        if not isinstance(other, IntervalDict):
            return NotImplemented
        self.update(other)
        return self

    # pylint: disable=arguments-differ,signature-differs
    def update(  # type: ignore
        self,
        *args: Union[IntervalDict, Mapping[atomic.IntervalValue[atomic.TO], V],
                     Iterable[Tuple[atomic.IntervalValue[atomic.TO], V]], ],
    ) -> None:
        """
        Update the dict.

        Arguments
        ---------
            *args: :class:`Iterable <python:typing.Iterable>`
                An iterable of :class:`IntervalDict` or valid iterable for an interval
                dictionary creation.

        Raises
        ------
            TypeError
                if an argument is not iterable.

        Note
        ----
            If the parameter ``strict`` used in the constructor is :data:`False
            <python:False>`, the complexity is in :math:`O(n\\,log(n)\\,k\\,\\lambda)`
            where:

            * :math:`n` is the length of ``*args``;
            * :math:`k` is the number of output intervals;
            * :math:`\\lambda` is the the cost of the ``operator`` parameter used in
              the constructor.

        Examples
        --------

            >>> from part import MutableIntervalDict
            >>> from operator import add
            >>> a = MutableIntervalDict[int, int](
            ...     operator=add,
            ...     default=lambda: 0,
            ... )
            >>> a.update({(1, 10): 1})
            >>> print(a)
            {'[1;10)': 1}
            >>> a.update(
            ...     FrozenIntervalDict[int, int]({(5, 20): 2}),
            ...     FrozenIntervalDict[int, int]({(10, 30): 3})
            ... )
            >>> print(a)
            {'[1;5)': 1, '[5;10)': 3, '[10;20)': 5, '[20;30)': 3}
            >>> a = MutableIntervalDict[int, set](
            ...     operator=lambda x, y: x | y,
            ...     strict=False
            ... )
            >>> a.update({(1, 10): {1}})
            >>> print(a)
            {'[1;10)': {1}}
            >>> a.update(
            ...     FrozenIntervalDict[int, set]({(5, 20): {2}}),
            ...     FrozenIntervalDict[int, set]({(10, 30): {3}})
            ... )
            >>> print(a)
            {'[1;5)': {1}, '[5;10)': {1, 2}, '[10;20)': {2, 3}, '[20;30)': {3}}
        """
        # TODO determine complexity
        strict = self._strict
        operator = self._operator
        if strict or operator is None:
            self._strict_update(*args)
        else:
            self._enhanced_update(*args)

    def _strict_update(
        self,
        *args: Union[IntervalDict, Mapping[atomic.IntervalValue[atomic.TO], V],
                     Iterable[Tuple[atomic.IntervalValue[atomic.TO], V]], ],
    ) -> None:
        for other in args:
            if isinstance(other, collections.abc.Mapping):
                for key, value in other.items():
                    self._add(atomic.Atomic.from_value(key), value)
            elif isinstance(other, collections.abc.Iterable):
                for key, value in other:  # type: ignore
                    self._add(atomic.Atomic.from_value(key), value)
            else:
                raise TypeError(f"{type(other)} object is not iterable")

    # pylint: disable=protected-access
    @classmethod
    def _rest(cls, rest, cursors, index, element):
        if cursors[index] < len(element):
            interval = element._intervals[cursors[index]]
            value = element._mapping[interval]
            rest.add((interval.lower, interval.upper, index, value))

    # pylint: disable=protected-access
    def _create(self, *args):
        # Create a list of non empty IntervalDict
        elements = []
        for element in itertools.chain([self], args):
            if not isinstance(element, IntervalDict):
                element = FrozenIntervalDict(element)
            if element:
                elements.append(element)

        cursors = [0] * len(elements)

        current = SortedList()

        rest = SortedList()
        for index, element in enumerate(elements):
            self._rest(rest, cursors, index, element)

        return (elements, cursors, current, rest)

    # pylint: disable=too-many-arguments,protected-access
    @classmethod
    def _next(cls, upper, elements, cursors, current, rest):

        # Remove useless elements from current
        while current and current[0][0] == upper:
            (_, _, index, _) = current[0]
            del current[0]
            cursors[index] += 1
            element = elements[index]
            cls._rest(rest, cursors, index, element)

        if current or not rest:
            lower = upper.next()
        else:
            lower = rest[0][0]

        # Move elements from rest to current
        while rest and rest[0][0] == lower:
            (lower, upper, index, value) = rest[0]
            del rest[0]
            current.add((upper, lower, index, value))

        if current:
            upper = current[0][0]
        if rest:
            upper = min(upper, rest[0][0].prev())

        return (lower, upper)

    def _enhanced_update(
        self,
        *args: Union[IntervalDict, Mapping[atomic.IntervalValue[atomic.TO], V],
                     Iterable[Tuple[atomic.IntervalValue[atomic.TO], V]], ],
    ) -> None:
        intervals = []
        mapping = {}

        (elements, cursors, current, rest) = self._create(*args)
        (lower, upper) = self._next(-atomic.INFINITY, elements, cursors,
                                    current, rest)

        while current:
            interval = atomic.Interval[atomic.TO](
                lower_value=lower.value,
                lower_closed=lower.type == 0,
                upper_value=upper.value,
                upper_closed=upper.type == 0,
            )
            value = reduce(
                self._operator,
                (value for (_, _, _, value) in current)  # type: ignore
            )
            intervals.append(interval)
            mapping[interval] = value
            (lower, upper) = self._next(upper, elements, cursors, current,
                                        rest)

        self._intervals = SortedSet(intervals)
        self._mapping = mapping

    def clear(self) -> None:
        """Remove all items from self (same as del self[:])."""
        self._intervals = SortedSet()
        self._mapping = {}

    def _start(self, interval):
        start = self._intervals.bisect_left(interval)
        if start < len(self._intervals):
            start_interval = self._intervals[start]
            start_value = self._mapping[start_interval]
            if self._intervals[start].overlaps(interval, strict=False):
                del self._intervals[start]
                del self._mapping[start_interval]
                if self._insert(
                    (
                        start_interval.lower_value,
                        interval.lower_value,
                        start_interval.lower_closed,
                        not interval.lower_closed,
                    ),
                        start_value,
                ):
                    start += 1
            elif interval.overlaps(self._intervals[start], strict=False):
                del self._intervals[start]
                del self._mapping[start_interval]
                if self._insert(
                    (
                        interval.upper_value,
                        start_interval.upper_value,
                        not interval.upper_closed,
                        start_interval.upper_closed,
                    ),
                        start_value,
                ):
                    start += 1
            elif interval.during(self._intervals[start], strict=False):
                del self._intervals[start]
                del self._mapping[start_interval]
                if self._insert(
                    (
                        start_interval.lower_value,
                        interval.lower_value,
                        start_interval.lower_closed,
                        not interval.lower_closed,
                    ),
                        start_value,
                ):
                    start += 1
                if self._insert(
                    (
                        interval.upper_value,
                        start_interval.upper_value,
                        not interval.upper_closed,
                        start_interval.upper_closed,
                    ),
                        start_value,
                ):
                    start += 1
        return start

    def _stop(self, interval):
        stop = self._intervals.bisect_right(interval)
        if 0 < stop <= len(self._intervals):
            stop -= 1
            stop_interval = self._intervals[stop]
            stop_value = self._mapping[stop_interval]
            if self._intervals[stop].during(interval, strict=False):
                del self._intervals[stop]
                del self._mapping[stop_interval]
            elif interval.overlaps(self._intervals[stop], strict=False):
                del self._intervals[stop]
                del self._mapping[stop_interval]
                self._insert(
                    (
                        interval.upper_value,
                        stop_interval.upper_value,
                        not interval.upper_closed,
                        stop_interval.upper_closed,
                    ),
                    stop_value,
                )

        return stop

    def _insert(self, key, value):
        interval = atomic.Atomic.from_tuple(key)
        if interval:
            self._intervals.add(interval)
            self._mapping[interval] = value
            return True
        return False

    def _bisect_left(self, search) -> int:
        return self._intervals.bisect_left(search)

    def _update_intervals(self, intervals) -> None:
        self._intervals = SortedSet(intervals)
Example #12
0
"""
https://leetcode.com/discuss/general-discussion/452863/how-do-you-deal-with-no-treeset-or-treemap-in-python
Python3 可以使用 sortedcontainers 库来实现 Java 中 TreeSet, TreeMap, Collections.sort(list)
"""

from sortedcontainers import SortedList

sl = SortedList(['e', 'a', 'c', 'd', 'b'])
print(sl)  # SortedList(['a', 'b', 'c', 'd', 'e'])
sl *= 100
print(sl.count('c'))  # 100
print(sl[-3:])  # ['e', 'e', 'e']

from sortedcontainers import SortedDict

sd = SortedDict({'c': 3, 'a': 1, 'b': 2})
print(sd)  # SortedDict({'a': 1, 'b': 2, 'c': 3})
print(sd.popitem(index=-1))  # ('c', 3)

from sortedcontainers import SortedSet

ss = SortedSet('abracadabra')
print(ss)  # SortedSet(['a', 'b', 'c', 'd', 'r'])
print(ss.bisect_left('c'))  # 2
print(ss.bisect_right('c'))  # 3
print(ss.bisect_left('f'))  # 4
print(ss.bisect_right('f'))  # 4