High-level Algorithm: repeatedly insert the next unsorted number A[j] into its correct position in the sorted subarray A[1..j-1].
for j ← 2 to n do
insert A[j] in its correct position in A[1..j-1]
Given input array of numbers 12, 8, 1, 10, 4, 7, 9, 2, the array values at the beginning of each successive iteration are
12 8← 1 10 4 7 9 2
8 12 1← 10 4 7 9 2
1 8 12 10← 4 7 9 2
. . .
. . .
. . .
1 4 7 8 9 10 12 2←
1 2 4 7 8 9 10 12
Low-level Algorithm:
A[0] ← -∞
for j ← 2 to n do
{
oldAj ← A[j]
i ← j-1
while A[i] > oldAj do
{
A[i+1] ← A[i]
i ← i-1
}
A[i+1] ← oldAj
}
Note: This algorithm illustrates how a sentinel helps simplify the code.
High-level algorithm version:
for j ← 2 to n do
insert A[j] in its correct position in A[1..j-1]
low-level algorithm version:
We do this by labeling each statement with the amount of time it takes to execute once. To do this, work outwards from the innermost blocks.
1. O(1) A[0] ← -∞
2. O(n^2) for j ← 2 to n do
3. O(n) {
4. O(1) oldAj ← A[j]
5. O(1) i ← j-1
6. O(n) while A[i] > oldAj do
7. O(1) {
8. O(1) A[i+1] ← A[i]
9. O(1) i ← i-1
10. }
11. O(1) A[i+1] ← oldAj
12. }
Thus, the worst-case running time of insertion sort is \(O(n^2)\).Note: Instead of labeling each statement by the amount of time it takes to execute once, we can also label it by the amount of time it takes to execute throughout the algorithm.
The algorithm consists of the procedure insertion-sort
that calls procedure insert
.
// Precondition: Array A contains integers.
// Postcondition: Array A contains the original numbers in sorted order.
insertion-sort(A) {
n ← A.length
if n > 1 then {
insertion-sort(A[1..n-1])
insert(n)
}
}
// Precondition: Subarray A[1..j-1] is sorted.
// Postcondition: Subarray A[1..j] contains original elements in A[1..j] in sorted order.
insert(j) {
if j=1 then return
else if A[j-1] < A[j] then return
else {
swap A[j-1] & A[j]
insert(j-1)
}
}