Rotated array problem.

This commit is contained in:
Ivan Malison 2015-07-09 18:30:36 -07:00
parent 9418c8afe8
commit fba727783c
2 changed files with 160 additions and 0 deletions

View File

@ -0,0 +1,91 @@
def rotate_array(incoming, rotation_index):
new_back = incoming[:rotation_index]
new_front = incoming[rotation_index:]
new_front.extend(new_back)
return new_front
def binary_search(
array, item, low=0, high=None,
lower_predicate=lambda item, array, index, low, high: item <= array[index]
):
if low < 0:
raise ValueError('lo must be non-negative')
if high is None:
high = len(array)
while low < high:
mid = (low + high)//2
if lower_predicate(item, array, mid, low, high):
high = mid
else:
low = mid + 1
return low
class RotatedArrayProxy(object):
def __init__(self, incoming):
self.incoming = incoming
self._rotation_index = None
if incoming:
# Duplicates can not span the rotation
assert incoming[0] != incoming[-1]
def __getitem__(self, item):
if not isinstance(item, slice):
return self.incoming[self._actual_index(item)]
else:
self._handle_slice(item)
def _handle_slice(self, item):
start = self._actual_index(item.start)
stop = self._actual_index(item.stop)
if start > stop:
sliced = self.incoming[start::item.stride]
# Ensure that the stride is preserved as it passes through
# the rotation.
start_index = (len(self.incoming) - start) % (item.stride or 1)
if start_index <= stop:
sliced.extend(
self.incoming[start_index:stop:item.stride]
)
return sliced
else:
return self.incoming[start:stop:item.stride]
def _actual_index(self, index):
if index is None:
return index
elif 0 <= index < len(self.incoming):
return (index + self.rotation_index) % len(self.incoming)
elif index == len(self.incoming):
return self.rotation_index
else:
raise Exception()
@property
def rotation_index(self):
if self._rotation_index is None:
self._rotation_index = self._find_rotation_index()
return self._rotation_index
def _find_lower_predicate(self, item, array, index, low, high):
return array[0] > array[index]
def _find_rotation_index(self):
if len(self.incoming) < 1:
return 0
return binary_search(self.incoming, self.incoming[0],
lower_predicate=self._find_lower_predicate)
def __len__(self):
return len(self.incoming)
def sorted_insertion_index(self, x):
return binary_search(self, x)
def actual_insertion_index(self, x):
return self._actual_index(self.sorted_insertion_index(x))
def unrotated(self):
return rotate_array(self.incoming, self.rotation_index)

View File

@ -0,0 +1,69 @@
import rotated_array
# duplicates, slicing with stride, insertion index for item greater than everything for completely sorted array
def test_empty_rotated_array_proxy():
empty_rap = rotated_array.RotatedArrayProxy([])
assert empty_rap.rotation_index == 0
assert empty_rap.unrotated() == []
assert empty_rap.sorted_insertion_index(100) == 0
assert empty_rap.actual_insertion_index(100) == 0
def test_inserting_at_end_of_insertion_range():
rap = rotated_array.RotatedArrayProxy([3, 4, 5, 0, 2])
assert rap.rotation_index == 3
assert rap.unrotated() == [0, 2, 3, 4, 5]
assert rap.sorted_insertion_index(-1) == 0
assert rap.actual_insertion_index(-1) == 3
assert rap.sorted_insertion_index(1) == 1
assert rap.actual_insertion_index(1) == 4
assert rap.sorted_insertion_index(2) in [1, 2]
assert rap.actual_insertion_index(2) in [4, 5]
assert rap.sorted_insertion_index(3) in [2, 3]
assert rap.actual_insertion_index(3) in [0, 1]
def test_inserting_for_sorted_array():
rap = rotated_array.RotatedArrayProxy([0, 1])
assert rap.unrotated() == [0, 1]
assert rap.sorted_insertion_index(1000) == 2
assert rap.actual_insertion_index(1000) == 2
def test_inserting_largest_element():
rap = rotated_array.RotatedArrayProxy([3, 0, 1])
assert rap.rotation_index == 1
assert rap.sorted_insertion_index(1000) == 3
assert rap.actual_insertion_index(1000) == 1
def test_inserting_largest_element():
rap = rotated_array.RotatedArrayProxy([3, 0, 1])
assert rap.unrotated() == [0, 1, 3]
assert rap.actual_insertion_index(2) == 0
assert rap.actual_insertion_index(1) in [2, 3]
def test_rotation_index_and_unrotate():
arr = [3]*117 + [1] + [2]*16
rap = rotated_array.RotatedArrayProxy(arr)
assert rap[0] == 1
assert rap.rotation_index == 117
assert rap.unrotated() == sorted(arr)
arr = [3, 3, 3, 3, 1, 1, 1, 2, 2]
rap = rotated_array.RotatedArrayProxy(arr)
assert rap.rotation_index == 4
assert rap.unrotated() == sorted(arr)
rap = rotated_array.RotatedArrayProxy([3, 3, 3, 3, 1, 1, 1, 2])
assert rap.rotation_index == 4
assert rap.unrotated() == [1, 1, 1, 2, 3, 3, 3, 3]
def test_insert():