Swift solution with comments, using sliding window
Complexity\n- Time complexity: O(n*log(n)), where n is stones count\n- Space complexity: O(n)\n\n# Code\n\nclass Solution {\n    func numMovesStonesII(_ stones:
# Complexity\n- Time complexity: O(n*log(n)), where n is stones count\n- Space complexity: O(n)\n\n# Code\n```\nclass Solution {\n    func numMovesStonesII(_ stones: [Int]) -> [Int]\n    {\n        let stonesCount = stones.count\n        let stones = stones.sorted() // that gives O(n*log(n)) complexity\n\n        // distances from the edge stones to their nearest neighbors\n        let edgeDist = [stones[1] - stones[0], stones[stonesCount-1] - stones[stonesCount-2]]\n        let minEdgeDist = edgeDist.min()!, maxEdgeDist = edgeDist.max()!\n\n        // for max number of turns we first move the edge stone to the nearest position\n        // on the other side of its neighbor, losing minEdgeDist-1 empty spaces\n        // after that we will always have a group of stones on that edge,\n        // we\'ll move the last of them, losing one space per turn till the end\n        let maxMovesNumber = stones[stonesCount-1] - stones[0] - stonesCount - minEdgeDist + 2\n\n\n        // we use the sliding window with the width of stonesCount,\n        // finding max possible number of stones fit in this window.\n        // for min number of turns we just jump the remaining \n        // outside stones into this range one by one\n\n        // index for element that is left border of the sliding window\n        var leftIndex = 0\n        // index for element after right border of the sliding window\n        guard var rightIndex = stones.firstIndex(where: { $0 >= stones[0] + stonesCount })\n        else { return [0, 0] }\n        \n        var count = 0, maxCount = 0\n        while rightIndex <= stonesCount\n        {\n            count = rightIndex - leftIndex\n            maxCount = max(count, maxCount)\n            // left border always advance to the next stone\n            leftIndex += 1\n            // if we have the rightmost stone inside the window, there is \n            // no point moving further, as the count can only go down\n            if rightIndex == stonesCount { break }\n\n            // move right border by finding the next stone\n            // that is outside advanced window range.\n            // could use binary search to speed up the searching a bit,\n            // but the complexity here is just O(n), as we only traverse\n            // the array once, so didn\'t bother\n            while rightIndex < stonesCount\n            {\n                if stones[rightIndex] >= stones[leftIndex] + stonesCount { break }\n                rightIndex += 1\n            }\n        }\n        var minMovesNumber = stonesCount - maxCount\n        // one special case with all stones but one already grouped together\n        if maxCount == stonesCount - 1, minEdgeDist == 1, maxEdgeDist > 2 {\n            minMovesNumber += 1\n        }\n\n        return [minMovesNumber, maxMovesNumber]\n    }\n}\n```
['Swift', 'Sliding Window']