Sorting is one of the most important tasks in programming. It helps us organize data so it can be searched, processed, or displayed efficiently. Tim Sort is a highly practical sorting algorithm that combines the strengths of Insertion Sort and Merge Sort. It is designed to be fast on real-world data and is widely used in programming languages like Python and Java for their built-in sort functions.
with hands-on learning.
get the skills and confidence to land your next move.
Tim Sort works by dividing the array into small chunks called “runs” and sorting each run with a simple algorithm like Insertion Sort. Then, it merges these runs efficiently using a method similar to Merge Sort. This hybrid approach makes Tim Sort particularly effective for datasets that already have some order, which is common in real-world applications like sorting user lists, scores, or dates. For beginners, implementing Tim Sort in F# provides a great way to explore sorting, recursion, and efficient merging.
Program 1: Basic Tim Sort Using Arrays and Runs
This program demonstrates a simple Tim Sort for integer arrays. It sorts small chunks with Insertion Sort and then merges them into a fully sorted array.
open System
let minRun = 32
let insertionSort (arr:int[]) left right =
for i in left + 1 .. right do
let key = arr.[i]
let mutable j = i - 1
while j >= left && arr.[j] > key do
arr.[j + 1] <- arr.[j]
j <- j - 1
arr.[j + 1] <- key
let merge (arr:int[]) l m r =
let len1 = m - l + 1
let len2 = r - m
let left = Array.init len1 (fun i -> arr.[l + i])
let right = Array.init len2 (fun i -> arr.[m + 1 + i])
let mutable i = 0
let mutable j = 0
let mutable k = l
while i < len1 && j < len2 do
if left.[i] <= right.[j] then
arr.[k] <- left.[i]
i <- i + 1
else
arr.[k] <- right.[j]
j <- j + 1
k <- k + 1
while i < len1 do
arr.[k] <- left.[i]
i <- i + 1
k <- k + 1
while j < len2 do
arr.[k] <- right.[j]
j <- j + 1
k <- k + 1
let timSort (arr:int[]) =
let n = arr.Length
for i in 0 .. minRun .. n - 1 do
let right = Math.Min(i + minRun - 1, n - 1)
insertionSort arr i right
let mutable size = minRun
while size < n do
let mutable left = 0
while left < n do
let mid = Math.Min(left + size - 1, n - 1)
let right = Math.Min((left + 2 * size - 1), n - 1)
if mid < right then
merge arr left mid right
left <- left + 2 * size
size <- size * 2
[<EntryPoint>]
let main argv =
let numbers = [| 5; 21; 7; 23; 19; 1; 15; 3 |]
timSort numbers
numbers |> Array.iter (printf "%d ")
0This program works by sorting small runs first using Insertion Sort, which is very efficient for small arrays. Then, it merges these runs progressively to create a fully sorted array. Beginners can follow each step to see how small sorted chunks gradually combine into a complete sort.
Program 2: Tim Sort with Step-by-Step Printing
This version prints intermediate steps during the merge process. It is ideal for beginners who want to visualize how Tim Sort works internally.
open System
let minRun = 32
let insertionSortStep (arr:int[]) left right =
for i in left + 1 .. right do
let key = arr.[i]
let mutable j = i - 1
while j >= left && arr.[j] > key do
arr.[j + 1] <- arr.[j]
j <- j - 1
arr.[j + 1] <- key
printfn "After insertion sort from %d to %d: %A" left right arr
let mergeStep (arr:int[]) l m r =
let len1 = m - l + 1
let len2 = r - m
let left = Array.init len1 (fun i -> arr.[l + i])
let right = Array.init len2 (fun i -> arr.[m + 1 + i])
let mutable i = 0
let mutable j = 0
let mutable k = l
while i < len1 && j < len2 do
if left.[i] <= right.[j] then
arr.[k] <- left.[i]
i <- i + 1
else
arr.[k] <- right.[j]
j <- j + 1
k <- k + 1
while i < len1 do
arr.[k] <- left.[i]
i <- i + 1
k <- k + 1
while j < len2 do
arr.[k] <- right.[j]
j <- j + 1
k <- k + 1
printfn "After merging from %d to %d: %A" l r arr
let timSortStep (arr:int[]) =
let n = arr.Length
for i in 0 .. minRun .. n - 1 do
let right = Math.Min(i + minRun - 1, n - 1)
insertionSortStep arr i right
let mutable size = minRun
while size < n do
let mutable left = 0
while left < n do
let mid = Math.Min(left + size - 1, n - 1)
let right = Math.Min((left + 2 * size - 1), n - 1)
if mid < right then
mergeStep arr left mid right
left <- left + 2 * size
size <- size * 2
[<EntryPoint>]
let main argv =
let numbers = [| 12; 6; 7; 5; 3; 0; 11; 8 |]
timSortStep numbers
printfn "Final sorted array: %A" numbers
0Step-by-step printing helps beginners see how Tim Sort first organizes small runs and then merges them in stages. It builds intuition on why merging sorted chunks is faster than sorting the entire array at once.
Program 3: Tim Sort Using Lists Instead of Arrays
This program demonstrates Tim Sort using F# lists for a functional programming approach. It emphasizes immutability while still applying the same run-and-merge logic.
open System
let insertionSortList (lst : int list) =
let rec insert x sorted =
match sorted with
| [] -> [x]
| h :: t when x <= h -> x :: sorted
| h :: t -> h :: insert x t
List.fold (fun acc x -> insert x acc) [] lst
let rec mergeList l1 l2 =
match l1, l2 with
| [], l | l, [] -> l
| h1 :: t1, h2 :: t2 ->
if h1 <= h2 then h1 :: mergeList t1 l2
else h2 :: mergeList l1 t2
let timSortList (lst : int list) =
if List.isEmpty lst then []
else
let minRun = 32
let rec splitRuns remaining runs =
match remaining with
| [] -> List.rev runs
| _ ->
let size = min minRun (List.length remaining)
let run, rest = List.splitAt size remaining
splitRuns rest (insertionSortList run :: runs)
let runs = splitRuns lst []
let rec mergeAll rs =
match rs with
| [] -> []
| [r] -> r
| r1 :: r2 :: rest ->
mergeAll (mergeList r1 r2 :: rest)
mergeAll runs
[<EntryPoint>]
let main argv =
let numbers = [12; 5; 7; 3; 19; 1; 4; 8]
let sorted = timSortList numbers
printfn "Sorted list:"
sorted |> List.iter (printf "%d ")
0Using lists shows a functional approach where the original array is not modified. Beginners can learn how recursion and higher-order functions replace loops in F#.
Program 4: Tim Sort with Generic Data Types
This program generalizes Tim Sort for any comparable type, making it reusable for integers, floats, or strings.
open System
let insertionSortGeneric (arr:'a[]) left right =
for i in left + 1 .. right do
let key = arr.[i]
let mutable j = i - 1
while j >= left && arr.[j] > key do
arr.[j + 1] <- arr.[j]
j <- j - 1
arr.[j + 1] <- key
let mergeGeneric (arr:'a[]) l m r =
let left = arr.[l..m]
let right = arr.[m+1..r]
let mutable i = 0
let mutable j = 0
let mutable k = l
while i < left.Length && j < right.Length do
if left.[i] <= right.[j] then
arr.[k] <- left.[i]
i <- i + 1
else
arr.[k] <- right.[j]
j <- j + 1
k <- k + 1
while i < left.Length do
arr.[k] <- left.[i]
i <- i + 1
k <- k + 1
while j < right.Length do
arr.[k] <- right.[j]
j <- j + 1
k <- k + 1
let timSortGeneric (arr:'a[]) =
let n = arr.Length
let minRun = 32
for i in 0 .. minRun .. n - 1 do
let right = Math.Min(i + minRun - 1, n - 1)
insertionSortGeneric arr i right
let mutable size = minRun
while size < n do
let mutable left = 0
while left < n do
let mid = Math.Min(left + size - 1, n - 1)
let right = Math.Min(left + 2*size - 1, n - 1)
if mid < right then
mergeGeneric arr left mid right
left <- left + 2*size
size <- size * 2
[<EntryPoint>]
let main argv =
let numbers = [| "pear"; "apple"; "orange"; "banana" |]
timSortGeneric numbers
numbers |> Array.iter (printf "%s ")
0This program is useful for beginners because it shows that Tim Sort is not limited to numbers. Strings, dates, or any comparable data can be sorted with the same logic.
Program 5: Tim Sort with Custom Min Run
This version allows the user to define the minimum run size, which can impact performance. It is useful for experimenting with optimization.
open System
let insertionSortMinRun (arr:int[]) left right =
for i in left + 1 .. right do
let key = arr.[i]
let mutable j = i - 1
while j >= left && arr.[j] > key do
arr.[j + 1] <- arr.[j]
j <- j - 1
arr.[j + 1] <- key
let mergeMinRun (arr:int[]) l m r =
let left = arr.[l..m]
let right = arr.[m+1..r]
let mutable i = 0
let mutable j = 0
let mutable k = l
while i < left.Length && j < right.Length do
if left.[i] <= right.[j] then
arr.[k] <- left.[i]
i <- i + 1
else
arr.[k] <- right.[j]
j <- j + 1
k <- k + 1
while i < left.Length do
arr.[k] <- left.[i]
i <- i + 1
k <- k + 1
while j < right.Length do
arr.[k] <- right.[j]
j <- j + 1
k <- k + 1
let timSortMinRun (arr:int[]) minRun =
let n = arr.Length
for i in 0 .. minRun .. n - 1 do
let right = Math.Min(i + minRun - 1, n - 1)
insertionSortMinRun arr i right
let mutable size = minRun
while size < n do
let mutable left = 0
while left < n do
let mid = Math.Min(left + size - 1, n - 1)
let right = Math.Min(left + 2*size - 1, n - 1)
if mid < right then
mergeMinRun arr left mid right
left <- left + 2*size
size <- size * 2
[<EntryPoint>]
let main argv =
let numbers = [| 12; 4; 5; 3; 8; 7; 6 |]
timSortMinRun numbers 4
numbers |> Array.iter (printf "%d ")
0Custom min run size allows beginners to experiment and see how small changes affect the algorithm’s efficiency. It also demonstrates that Tim Sort can be tuned for different datasets.
Frequently Asked Questions (FAQ)
This section answers common beginner questions about Tim Sort in simple terms.
Q1. What makes Tim Sort different from other sorting algorithms?
Tim Sort combines Insertion Sort and Merge Sort, making it efficient for real-world data that is partially ordered.
Q2. Can Tim Sort handle negative numbers and decimals?
Yes, as long as the data can be compared, Tim Sort works for negative numbers, decimals, and even strings.
Q3. Is Tim Sort stable?
Yes, Tim Sort preserves the relative order of equal elements, which is important for certain datasets.
Q4. How does the minimum run size affect performance?
A smaller run size increases the number of merges, while a larger run size may make insertion sorting less efficient. Finding the right balance improves speed.
Q5. Should beginners learn Tim Sort?
Yes, it teaches important algorithm design concepts like hybrid sorting, runs, and efficient merging, which are useful for advanced programming.
Conclusion
Tim Sort is a practical and efficient sorting algorithm widely used in modern programming. Through multiple F# programs, we explored classic Tim Sort with arrays, step-by-step visualization, functional list-based implementation, generic types, and customizable min run size.
By practicing these examples, beginners can learn how hybrid algorithms work, how merging small sorted chunks can optimize performance, and how to apply these concepts to real-world datasets. Tim Sort not only sorts efficiently but also builds a deeper understanding of algorithmic thinking in F#.




