Useful Foundation APIs

swapAt

  • swap the value in an array by passing the index

Bubble Sort

func bubbleSort(input: inout [Int]) {
    for i in 0..<input.count {
        for j in 1..<input.count {
            if input[j] < input[j - 1] {
                input.swapAt(j, j-1)
            }
        }
    }
}

var input = [4, 5, 2, 1, 6, 8, 9, 12, 13]
bubbleSort(input: &input)

Every steps compares 2 values and swapped it

Red = Sorted Item

Time complexity is O(n^2)

Space complexity is O(1)

Insertion Sort

Apple Foundation uses TimSort (InsertionSort + MergeSort)

Insertion Sort is another O(N^2) quadratic running time algorithm

On large datasets it is very inefficient – but on arrays with 10-20 items it is quite good. (Apple uses TimSort, It items is less than 64 then use Insertion Sort. -> https://github.com/apple/swift/blob/387580c995fc9844d4f268723bd55e22440b1a3d/stdlib/public/core/Sort.swift#L462)

A huge advantage is that it is easy to implement

It is more efficient than other quadratic running time sorting procedures such as bubble sort or selection sort

It is an adaptive algorithm – It speeds up when array is already substantially sorted

It is stable so preserves the order of the items with equal keys

Insertion sort is an in-place algorithm – does not need any additional memory

It is an online algorithm – It can sort an array as it receives the items for example downloading data from web

Hybrid algorithms uses insertion sort if the subarray is small enough: Insertion sort is faster for small subarrays than quicksort

Variant of insertion sort is shell sort

Sometimes selection sort is better: they are very similar algorithms

Insertion sort requires more writes because the inner loop can require shifting large sections of the sorted portion of the array

In general insertion sort will write to the array O(N^2) times while selection sort will write only O(N) times

For this reason selection sort may be preferable in cases where writing to memory is significantly more expensive than reading (such as with flash memory)

https://www.udemy.com/course/algorithms-and-data-structures-in-java-part-ii/learn/lecture/4901832#questions

func insertSort(_ input: inout [Int]) {
    var i = 1
    for i in 1..<input.count {
        var j = i
        while j-1 >= 0, input[j] < input[j-1] {
            input.swapAt(j, j-1)
            j-=1
        }
    }
}
var input = [-9, 4, -9, 5, 8, 12]
insertSort(&input)
print(input)


var input2 = [-9, 4, -9, 5, 8, 12, -249, 241, 4, 2, 12, 24140, 539, 3, 0, -2314]
insertSort(&input2)
print(input2)

Merge Sort

Divide and Conquer

Break down into small subproblem, Use recursion.

It needs two functions

  • split – divide into two array
  • merge – merging two array into the sorted array
func mergeSort(input: inout [Int]) {
    //Base case
    if input.count == 1 {
        return
    }
    let midIndex = input.count / 2
    var left = Array(repeating: 0, count: midIndex)
    var right = Array(repeating: 0, count: input.count - midIndex)
    
    //Split into 2 sub array (create sub problems)
    split(input: &input, left: &left, right: &right)
    
    //Recursive - Divide and Conquer
    mergeSort(input: &left)
    mergeSort(input: &right)
    
    //Merge all together
    merge(input: &input, left: &left, right: &right)
}
func split(input: inout [Int], left: inout [Int], right: inout [Int]) {
    let leftCount = left.count
    for (index, item) in input.enumerated() {
        if index < leftCount {
            left[index] = item
        }
        else {
            right[index - leftCount] = item
        }
    }
}
func merge(input: inout [Int], left: inout [Int], right: inout [Int]) {
    var mergeIndex = 0
    var leftIndex = 0
    var rightIndex = 0
    
    while (leftIndex < left.count && rightIndex < right.count) {
        if left[leftIndex] < right[rightIndex] {
            input[mergeIndex] = left[leftIndex]
            leftIndex += 1
        }
        else if rightIndex < right.count {
            input[mergeIndex] = right[rightIndex]
            rightIndex += 1
        }
        mergeIndex += 1
    }
    if leftIndex < left.count {
        while mergeIndex < input.count {
            input[mergeIndex] = left[leftIndex]
            mergeIndex += 1
            leftIndex += 1
        }
    }
    if rightIndex < right.count {
        while mergeIndex < input.count {
            input[mergeIndex] = right[rightIndex]
            mergeIndex += 1
            rightIndex += 1
        }
    }
}

Split Function

Calculate midIndex

Split them into two subarray

Time complexity: O(nlogn)

Space complexity: O(n)

  • Because It needs a copy values when merging two array into the one sorted array

Quick Sort

func partition(_ input: inout [Int], low: Int, high: Int) -> Int {
    let pivot = input[low]
    var l = low
    var h = high
    while l < h {
        while (input[l] <= pivot && l < h) {
            l += 1
        }
        while (input[h] > pivot) {
            h -= 1
        }
        if l < h {
            input.swapAt(l, h)
        }
    }
    //Put pivot to the right position
    input.swapAt(low, h)
    return h
}

func quickSort(_ input: inout [Int], low: Int, high: Int) {
    //base
    if low >= high {
        return
    }
    //pivot
    let pivot = partition(&input, low: low, high: high)
    
    //left
    quickSort(&input, low: low, high: pivot - 1)

    //right
    quickSort(&input, low: pivot + 1, high: high)
}

var input = [2, 3, 1, 4, 9]
quickSort(&input, low: 0, high: input.count - 1)
print(input)

Quick Sort is also Divide and Conquer.

Time Complexity

  • Average Time Complexity is O(nlogn)

Space Complexity

  • O(logn) -> Extra space for the call stack in the recursive call
  • O(n) -> worst case

Binary Search

Search a sorted list!

//Binary Search
func findIndex(array: [Int], value: Int) -> Int {
    var min = 0
    var max = array.count - 1
    
    while min <= max {
        let mid = min + (max - min) / 2
        
        if value == array[mid] {
            return mid
        }
        //search right side
        if value > array[mid] {
            min = mid - 1
        }
        //search left side
        else {
            max = mid + 1
        }
    }
    return -1
}

let sortedArray = Array(0...100)
findIndex(array: sortedArray, value: 49)
findIndex(array: sortedArray, value: 78)
findIndex(array: sortedArray, value: 22)
findIndex(array: sortedArray, value: 89)

One response to “Swift, Sorting and Binary Search Algorithm”

  1. What sorting algorithm does Swift use in iOS? – Shawn Avatar

    […] Swift, Sorting and Binary Search Algorithm […]

    Like

Leave a comment

Previous Post
Next Post

Quote of the week

"People ask me what I do in the winter when there's no baseball. I'll tell you what I do. I stare out the window and wait for spring."

~ Rogers Hornsby