|
1 | 1 | class Solution:
|
2 |
| - def single_place(self, nums, num): |
3 |
| - """returns on which place >= num starts and how many same items""" |
4 |
| - lf, rg = 0, len(nums) - 1 |
| 2 | + def bin_search(self, arr, num, fits=lambda a, b: a == b, le=lambda a, b: a <= b): |
| 3 | + """ |
| 4 | + returns tuple: the leftmost place of num in arr and the amount of items same an num |
| 5 | + """ |
| 6 | + lf, rg = 0, len(arr) - 1 |
5 | 7 | md = (lf + rg) // 2
|
6 |
| - while lf <= rg and nums[md] != num: |
7 |
| - if nums[md] <= num: |
| 8 | + while lf <= rg and not fits(arr[md], num): |
| 9 | + if le(arr[md], num): |
8 | 10 | lf = md + 1
|
9 | 11 | else:
|
10 | 12 | rg = md - 1
|
11 | 13 | md = (lf + rg) // 2
|
12 | 14 |
|
13 |
| - if nums[md] == num: |
14 |
| - left_step, right_step = 1 >> 11, 1 >> 11 |
15 |
| - lf_sum, rg_sum = 0, 0 |
| 15 | + if fits(arr[md], num): |
| 16 | + # searching the biggest possible step to each side and do it as many times as possible |
| 17 | + left_step, right_step = 1 << 20, 1 << 20 |
| 18 | + lf_offset, rg_offset = 0, 0 |
16 | 19 | while left_step and right_step:
|
17 |
| - if md - left_step - lf_sum < 0 or nums[md - left_step - lf_sum] != num: |
18 |
| - left_step <<= 1 |
| 20 | + if md - left_step - lf_offset < 0 or not fits(arr[md - left_step - lf_offset], num): |
| 21 | + left_step >>= 1 |
19 | 22 | else:
|
20 |
| - lf_sum += left_step |
| 23 | + lf_offset += left_step |
21 | 24 |
|
22 |
| - if md + right_step + rg_sum >= len(nums) or nums[md + right_step + rg_sum] != num: |
23 |
| - right_step <<= 1 |
| 25 | + if md + right_step + rg_offset >= len(arr) or not fits(arr[md + right_step + rg_offset], num): |
| 26 | + right_step >>= 1 |
24 | 27 | else:
|
25 |
| - rg_sum += right_step |
26 |
| - |
27 |
| - return [md - lf_sum, rg_sum + lf_sum + 1] |
28 |
| - return [lf, 0] |
| 28 | + rg_offset += right_step |
| 29 | + return [md - lf_offset, rg_offset + lf_offset, True] |
| 30 | + return [lf, 0, False] |
29 | 31 |
|
30 | 32 | def place(self, nums1, nums2, num):
|
31 | 33 | """returns the leftmost and the rightmost place of the element from nums1 in merged list"""
|
32 |
| - place1, place2 = self.single_place(nums1, num), self.single_place(nums2, num) |
33 |
| - return [place1[0] + place2[0] + 2, place1[1] + place2[1]] |
| 34 | + place1, place2 = self.bin_search(nums1, num), self.bin_search(nums2, num) |
| 35 | + return [place1[0] + place2[0], place1[1] + int(place1[2]) + place2[1] + int(place2[2])] |
34 | 36 |
|
35 | 37 | def find_place(self, nums, other_nums, desired_place):
|
36 |
| - lf, rg = 0, len(nums) - 1 |
37 |
| - md = (lf + rg) // 2 |
38 |
| - p = self.place(nums, other_nums, nums[md]) |
39 |
| - while lf <= rg and not (p[0] <= desired_place <= p[0] + p[1]): |
40 |
| - if p[0] > desired_place: |
41 |
| - rg = md - 1 |
42 |
| - else: |
43 |
| - lf = md + 1 |
44 |
| - md = (lf + rg) // 2 |
45 |
| - p = self.place(nums, other_nums, nums[md]) |
46 |
| - return p |
| 38 | + class Shared: |
| 39 | + def __init__(self): |
| 40 | + self.p = None |
| 41 | + |
| 42 | + def fits(self, a, b): |
| 43 | + self.p = Solution().place(nums, other_nums, a) |
| 44 | + return self.p[0] <= b < self.p[0] + self.p[1] |
| 45 | + |
| 46 | + def le(self, _, b): |
| 47 | + return self.p[0] < b |
| 48 | + |
| 49 | + s = Shared() |
| 50 | + p = self.bin_search( |
| 51 | + nums, |
| 52 | + desired_place, |
| 53 | + fits=s.fits, |
| 54 | + le=s.le |
| 55 | + ) |
| 56 | + |
| 57 | + if p[2]: |
| 58 | + return p |
| 59 | + return False |
47 | 60 |
|
48 | 61 | def findMedianSortedArrays(self, nums1, nums2, desired_place=None, need_two=None):
|
49 | 62 | if desired_place is None:
|
50 |
| - desired_place = int((len(nums1) + len(nums2)) / 2.0 + 0.5) |
| 63 | + desired_place = int((len(nums1) + len(nums2)) / 2.0 + 0.5) - 1 |
51 | 64 | if need_two is None:
|
52 | 65 | need_two = (len(nums1) + len(nums2)) % 2 == 0
|
53 | 66 |
|
54 | 67 | p = self.find_place(nums1, nums2, desired_place)
|
55 |
| - if p[0] <= desired_place < p[0] + p[1]: |
56 |
| - second = nums1[p[0] - 1] |
| 68 | + if p: |
| 69 | + second = nums1[p[0]] |
57 | 70 | if need_two:
|
58 | 71 | second = self.findMedianSortedArrays(nums1, nums2, desired_place=desired_place+1, need_two=False)
|
59 |
| - return (nums1[p[0] - 1] + second) / 2. |
| 72 | + return (nums1[p[0]] + second) / 2. |
60 | 73 |
|
61 | 74 | p = self.find_place(nums2, nums1, desired_place)
|
62 |
| - if p[0] <= desired_place < p[0] + p[1]: |
63 |
| - second = nums2[p[0] - 1] |
| 75 | + if p: |
| 76 | + second = nums2[p[0]] |
64 | 77 | if need_two:
|
65 | 78 | second = self.findMedianSortedArrays(nums1, nums2, desired_place=desired_place+1, need_two=False)
|
66 |
| - return (nums2[p[0] - 1] + second) / 2. |
| 79 | + return (nums2[p[0]] + second) / 2. |
67 | 80 |
|
68 | 81 | print("Something went wrong")
|
69 | 82 | return -1
|
|
0 commit comments