86 lines
4.0 KiB
Markdown
86 lines
4.0 KiB
Markdown
# 8.3. Magic index
|
|
|
|
## Given an array of **distinct** integers, find a magix index, ifo ne exists. A magic index in an array A is an index such that A[i] = 1
|
|
|
|
## First idea
|
|
|
|
> example: [0,4,8,3,2,1,6,9] -> 0, 3, 6
|
|
|
|
```python
|
|
def magic_index(s):
|
|
if not s:
|
|
return -1
|
|
for i in range(len(s)):
|
|
if s[i] == i:
|
|
return i
|
|
return -1
|
|
```
|
|
|
|
Either this is easy or there's something I'm not understanding, this takes O(n) time and O(1) space.
|
|
|
|
That the integers are distinct is important... if we find a magic index in i=0 and i=3, we can discard all the next numbers as long as s[i] < 3, until we reach 6. But that takes O(n) anyway, we're still iterating the string.
|
|
|
|
## Hints
|
|
|
|
Brute force algorithm probably runs in O(n) time, if we're trying to beat that, how should it be? O(logn) or O(1). What sort of algorithms have that runtime? Sorting algorithms? But we're not searching for something specific, the magic index could be anywhere. And if we sort, we lose the info on the position.
|
|
|
|
We could start by taking half the string, and discarding those numbers > len(s)/2. But we still have to iterate and check, we might find a magic index early on and break the loop but if we don't, worst case scenario is O(n) anyway. In the example, when taking the second half of the string `[2,1,6,9]`, we can quickly discard 2,1 because the index in the second part must be > len(s)/2 = 4. But we still have to iterate...
|
|
|
|
## More hints
|
|
|
|
> example: [0,4,8,3,2,1,6,9] -> 0, 3, 6
|
|
|
|
Can you solve it in O(logn)? Don't see how... log8 = 3, can I intuitively solve this in 3 steps? Ideally, I'd write an array of len=8 of ascending order, and substract, and find a 0 value. But array substraction isn't possible in O(1), and neither is finding in a string.
|
|
|
|
If I have 3 questions to resolve this problem, the numbers aren't sorted....
|
|
|
|
## More hints 2
|
|
|
|
Binary search has a runtime of O(logn), can we apply a form of binary search to the problem? [Geeksforgeeks binary search](https://www.geeksforgeeks.org/binary-search/), searching for '23' in an already sorted array, it's a recursive function. In our case we don't know the number we're looking for, so how can we search? It could be anywhere. And if we start sorting the array, only that step takes O(nlogn)
|
|
|
|
## More hints 3
|
|
|
|
Given a specific index and value, can you identify if the magic index would be before or after it? I guess I could if the array is sorted...
|
|
|
|
Wait: if the integers are distinct, that means if we find '4' at index 1, then both index=4 and value=1 are discarded. So each time we iterate, we either find the magic index or discard 2 options.
|
|
|
|
* input = s = [7,4,5,3,2,0,6,1]
|
|
* i=0, v=7. i=7 and v=0 discarded
|
|
* i=1, v=4. i=4 and v=1 discarded
|
|
|
|
```python
|
|
def magic_index(s):
|
|
if not s:
|
|
return -1
|
|
for i in range(len(s)):
|
|
if s[i] == i:
|
|
return i
|
|
s.remove(i) # remove value with the index
|
|
del s[s[i]] # remove index with the value
|
|
return -1
|
|
```
|
|
|
|
Something like this, assuming i is present in s and the index s[i] exists. But still, this only eliminates 2 possibilities for each iteration. Can I solve this in 3 steps?
|
|
|
|
So if in step=0 we find a 7, we separate two branches:
|
|
|
|
1. delete s[0], 7 is deleted, delete s[7], 1 is deleted, etc
|
|
2. delete 0 at index (i=5), delete 5 at index (i=2), etc
|
|
3. We either find a magic index, or find a position where there's already a 0. return -1
|
|
|
|
* s = [7,4,5,3,2,0,6,1]
|
|
* s = [0,4,5,3,2,0,6,0]
|
|
* s = [0,0,5,3,0,0,6,0]
|
|
* s = [0,0,0,3,0,0,6,0]
|
|
* s = [0,0,0,3,0,0,6,0]
|
|
* return 3 and 6
|
|
|
|
Previous algorithm doesn't work. This branching in 2 is indeed O(logn) and O(n) space. Tests passed. It might be possible to have O(1) space but don't know how.
|
|
|
|
If the integers are non-distinct, the algorithm has to run in O(n) time
|
|
|
|
## Solution
|
|
|
|
I read the problem wrong... damn it. The array is sorted :( so I guess I solved a more difficult variant of the problem, it's much easier if it's already sorted.
|
|
|
|
When looking at the middle element, the magic index is on the left side if A[mid] > mid, or on the right side if A[mid] < mid. Non-distinct solution in book p347. |