swifttimsort

TimSort in Swift


I am trying to implement TimSort in Swift. I have referred to both these links : This and this The code which I have converted to Swift is :

import UIKit

class ViewController: UIViewController {

var arr : [Int] = []
let run : Int = 5

override func viewDidLoad() {
    super.viewDidLoad()
    for _ in 0..<10 {
        arr.append(Int(arc4random_uniform(100)))
    }
    timSort()
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

func insertionSort(_ array:[Int]) -> [Int] {
    var a = array
    for x in 1..<a.count {
        var y = x
        while y > 0 && a[y] < a[y - 1] {
            a.swapAt(y - 1, y)
            y -= 1
        }
    }
    return a
}

func merge(leftPile: [Int], rightPile: [Int]) -> [Int] {
    var leftIndex = 0
    var rightIndex = 0

    var orderedPile = [Int]()

    while leftIndex < leftPile.count && rightIndex < rightPile.count {
        if leftPile[leftIndex] < rightPile[rightIndex] {
            orderedPile.append(leftPile[leftIndex])
            leftIndex += 1
        } else if leftPile[leftIndex] > rightPile[rightIndex] {
            orderedPile.append(rightPile[rightIndex])
            rightIndex += 1
        } else {
            orderedPile.append(leftPile[leftIndex])
            leftIndex += 1
            orderedPile.append(rightPile[rightIndex])
            rightIndex += 1
        }
    }

    while leftIndex < leftPile.count {
        orderedPile.append(leftPile[leftIndex])
        leftIndex += 1
    }

    while rightIndex < rightPile.count {
        orderedPile.append(rightPile[rightIndex])
        rightIndex += 1
    }

    return orderedPile
}

func timSort() {
    print("Unsorted : \(arr)")
    for i in stride(from: 0, to: arr.count, by: run) {
        print("i : \(min((i + run),(arr.count)))")
        arr.replaceSubrange(i..<min((i + run),(arr.count)), with: insertionSort(Array(arr[i..<min((i + run),(arr.count))])))
    }
    print("after insertion sort \(arr)")

    var runCount = run
    while runCount < arr.count{
        for x in stride(from: 0, to: arr.count, by: 2 * runCount) {
            print("x : \(x) runcount \(runCount) calc : \(x + 2 * runCount)")
            arr.replaceSubrange(x..<min((x + 2 * runCount),(arr.count)), with: merge(leftPile: Array(arr[x..<(x + runCount)]), rightPile: Array(arr[(x + runCount)..<min((x + 2 * runCount),(arr.count))])))
        }
        runCount = runCount * 2
    }

    print("Sorted : \(arr)")
}
}

The issue that I am facing is that when I execute code in both the links, it works with any run value (like run = 7), but the same isn't happening in my code.

My code only works properly when run = 5 and arr.count = 10. In all other cases, it crashes on arr.replaceSubrange(x..<min((x + 2 * runCount),(arr.count)), with: merge(leftPile: Array(arr[x..<(x + runCount)]), rightPile: Array(arr[(x + runCount)..<min((x + 2 * runCount),(arr.count))]))) this line.

I have tried various ways out but not able to get the issue. Please could someone help me point it out.


Solution

  • You need a few more min checks. In your last while loop, x + runCount can exceed arr.count, so x + runCount needs to be replaced with min(x + runCount, arr.count). With those added min checks, the code now runs for various sizes of run and arr.count:

    func timSort() {
        print("Unsorted : \(arr)")
        for i in stride(from: 0, to: arr.count, by: run) {
            print("i : \(min((i + run),(arr.count)))")
            arr.replaceSubrange(i..<min((i + run),(arr.count)), with: insertionSort(Array(arr[i..<min((i + run),(arr.count))])))
        }
        print("after insertion sort \(arr)")
    
        var runCount = run
        while runCount < arr.count{
            for x in stride(from: 0, to: arr.count, by: 2 * runCount) {
                print("x : \(x) runcount \(runCount) calc : \(x + 2 * runCount)")
                arr.replaceSubrange(x..<min(x + 2 * runCount, arr.count), with: merge(leftPile: Array(arr[x..<min(x + runCount, arr.count)]), rightPile: Array(arr[min(x + runCount, arr.count)..<min(x + 2 * runCount, arr.count)])))
            }
            runCount = runCount * 2
        }
    
        print("Sorted : \(arr)")
    }