Sort and Binary Search O(nlgn)
IntuitionSort and analyze.Approach
Sort the stones.
To obtain the max moves, we choose the endpoint that has a smaller gap with its (sole) neighbour stone, in o
# Intuition
Sort and analyze.
# Approach
1. Sort the stones.
2. To obtain the max moves, we choose the endpoint that has a smaller gap with its (sole) neighbour stone, in order to diminish the least space. The max moves = total gaps - min(leftmost gap, rightmost gap).
3. To obtain the min moves, we need to binary search.
4. For each stones, we should try to make it as the leftmost stone when the movements are completed. In this way, the rightmost stone is its position + len - 1. If the rightmost stone is beyond the initial rightmost one, it's impossible, and we will skip it.
5. We use binary search to find how many existing stones will be covered in this way, and here outcome = len - covered.
6. We should also try to make it the rightmost stone when done. It's similar.
7. There is an exception: when the initial leftmost or rightmost stone can't be moved, we must skip it. We need to determine this case: when its gap with its neighbour is 0, and there is at most 1 consecutive gap segment, it's that case.
8. The result is the min of the outcomes.
# Complexity
- Time complexity:
O(nlgn)
- Space complexity:
O(1)
# Code
```javascript []
/**
 * @param {number[]} stones
 * @return {number[]}
 */
var numMovesStonesII = function(stones) {
    stones.sort((a, b) => a - b)
    const minVal = stones[0], maxVal = stones.at(-1)
    const len = stones.length
    const gapCount = 1 + maxVal - minVal - len
    
    let emptyCount = 0, hasEnoughEmpty = false
    for (let i = 1; i < len; i++) {
        if (stones[i] > stones[i - 1] + 1) {
            emptyCount++
            if (emptyCount > 1) {
                hasEnoughEmpty = true
                break
            }
        }
    }


    const gapL = stones[1] - minVal - 1
    const gapR = maxVal - stones.at(-2) - 1
    const maxResult = gapCount - Math.min(gapL, gapR)

    
    let minResult = maxResult
    const leftStart = gapL === 0 && !hasEnoughEmpty ? 1 : 0
    const rightStart = gapR === 0 && !hasEnoughEmpty ? len - 2 : len - 1
    if (rightStart === len - 1) {
        for (let i = leftStart; i < len; i++) {
            const from = stones[i], to = from + len - 1
            if (to > maxVal)   break

            let low = i, high = len - 1
            // find latest low where
            // stones[low] <= to
            while (low < high) {
                const mid = Math.ceil((low + high) / 2)
                if (stones[mid] <= to)  low = mid
                else    high = mid - 1
            }

            const covered = 1 + low - i
            const outcome = len - covered
            minResult = Math.min(minResult, outcome)
        }
    }
    
    if (leftStart === 0) {
        for (let i = rightStart; i > -1; i--) {
            const from = stones[i], to = from - len + 1
            if (to < minVal)   break

            let low = 0, high = i
            // find earliest high where
            // stones[high] >= to
            while (low < high) {
                const mid = Math.floor((low + high) / 2)
                if (stones[mid] >= to)  high = mid
                else    low = mid + 1
            }

            const covered = 1 + i - high
            const outcome = len - covered
            minResult = Math.min(minResult, outcome)
        }
    }


    return [minResult, maxResult]
};
```
['JavaScript']