Lesson 5
Trees, Searching and Sorting
Taught
Connectivity and Trees
Connectivity
Definition 5.1 (Connected graph).
An undirected graph is connected if every vertex is reachable from every other vertex. By the proposition on walks and simple paths, equivalently, every pair of distinct vertices is joined by a simple path.
The graph of Figure 5.1 is disconnected: no path leads from to . Its vertices split into and .
Definition 5.2 (Connected component).
A connected component is a maximal connected induced subgraph: no further vertex of the graph can be included while keeping it connected. A graph is connected exactly when it has one component, and an isolated vertex forms a component by itself.
Theorem 5.3 (Edges of a connected graph).
A connected simple undirected graph with vertices and edges has .
Discussion.
The first proof grows the graph from one vertex. At each stage connectivity supplies an edge leaving the part reached so far, and that edge brings in exactly one new vertex, so reaching all vertices uses distinct edges of the graph.
The second proof is an induction on that removes a vertex of smallest degree . If , counting edge-ends gives directly and no induction is needed. If , removing a vertex of degree one loses exactly one edge, and what is left is still connected, because a vertex of degree one cannot lie in the middle of a path. The hypothesis then applies to the smaller graph. The hypothesis has to be stated for every connected graph on vertices, since it is applied to a graph that the proof constructs.
Proof.
Choose one vertex and mark it reached. If fewer than vertices are reached, connectivity guarantees an edge from some reached vertex to an unreached one; otherwise no path could lead out of the reached set. Mark that new vertex and keep the connecting edge. Each step reaches exactly one new vertex and keeps an edge not kept before. After steps all vertices are reached, and we have found distinct edges of . Therefore .
A second proof, by induction, removes a vertex at each step.
Proof.
For each integer , let be the statement: every connected simple undirected graph with vertices has at least edges.
Base case, . A simple graph with one vertex has no pair of distinct vertices to join, so it has edges. It is connected, and .
Step. Fix and assume . Take an arbitrary connected simple graph with and , and let be its smallest degree. Because , a vertex of degree zero would have no path to any other vertex, contradicting connectivity, so . Either or .
Case . Every one of the vertices has degree at least , so . Counting edge-ends gives . Therefore , so . This case needs neither the removal of a vertex nor .
Case . Some vertex has exactly one edge, say . Remove and that edge, and call the result . No other edge is removed, because no other edge touches , so has vertices and edges.
Before applying we check that is connected. Take distinct vertices of . Since is connected, a simple path joins to in . Neither endpoint is , because and remain. Nor can be an internal vertex of the path: the path would have to enter along one edge and leave along a second, distinct edge, and has only the edge . So the path uses neither nor its edge, and it is a path in . This holds for every pair , so is connected; when , has a single vertex and is connected by definition.
Now satisfies the hypotheses of : it is simple, connected, and has vertices. Hence , and adding to both sides gives . Both cases prove from , and with induction proves the theorem for every .
A connected graph with five vertices needs at least four edges, and a path through all five vertices attains the bound. Adding one edge to that path gives five edges and creates a cycle; the theorem does not claim that every connected graph has exactly edges.
Definition 5.4 (Articulation vertex, bridge and vertex cut).
An articulation vertex is a vertex whose deletion, together with its edges, increases the number of connected components. A bridge, also called an isthmus, is an edge whose deletion increases that number. A vertex cut of a connected graph is a set of vertices whose deletion disconnects it, provided at least two vertices remain afterwards.
In Figure 5.1, the edge is a bridge: deleting it separates from and . The vertex is an articulation vertex: deleting it leaves and in separate components. All three edges of the left component are bridges too, since that component is a path through four vertices.
Strong Connectivity
For a directed graph, weak connectivity means connectivity once the directions of the arcs are ignored. Requiring directed routes both ways is a stronger condition.
Definition 5.5 (Strong connectivity and the reduced graph).
A directed graph is strongly connected if for every there is a directed walk from to and one from to . A strongly connected component is a maximal strongly connected induced subgraph. The reduced graph, or condensation, has one vertex for each strongly connected component, and an arc whenever and some arc of the original graph goes from a vertex of to a vertex of .
In Figure 5.2, the left graph is strongly connected. In the right one every arc points away from and towards , so each of its four vertices is a strongly connected component by itself.
In Figure 5.3, and are each strongly connected, and the only arcs between them point from the first set to the second. The reduced graph has two vertices and one arc . There cannot be an arc back: that would give reachability both ways and merge the two into one component.
Proposition 5.6 (The reduced graph has no directed cycle).
The reduced graph of any directed graph contains no simple directed cycle.
Discussion.
A cycle through several components would let every vertex in any one of them reach every vertex in the others and come back, so all of them would lie in a single strongly connected component. That contradicts maximality. The proof replaces each arc of the reduced graph by a walk in the original graph.
Proof.
The reduced graph has no loops, since its arcs join distinct components. Suppose were a simple directed cycle of it with . Each arc comes from an arc of the original graph from some vertex of to some vertex of , and within each component any two vertices are joined by walks both ways. Concatenating, any vertex of can reach any vertex of , and any vertex of can reach any vertex of around the rest of the cycle. So induces a strongly connected subgraph larger than , contradicting the maximality of .
Show that a simple undirected graph on vertices with more than edges is connected, and give a disconnected graph with exactly edges.
Find the strongly connected components and the reduced graph of the graph in Figure 4.8 of Lesson 4.
Trees
Definition 5.7 (Tree and forest).
A tree is a connected acyclic undirected graph. A forest is an undirected graph whose components are trees.
The tree on the left of Figure 5.4 has seven vertices and six edges.
Proposition 5.8 (Counting the edges of a forest).
An acyclic undirected graph with vertices, edges and components satisfies .
Discussion.
The proof is an induction on the number of edges, and the step removes one. In an acyclic graph every edge is a bridge: if its endpoints were still joined after deleting it, that route together with the edge would be a cycle. So deleting an edge lowers by one and raises by one, and is unchanged. The base case, a graph with no edges, has every vertex as its own component.
Proof.
For every vertex is its own component, so and . Assume the formula for all acyclic graphs with edges, and take one with edges. Delete one edge. Its endpoints cannot remain joined by another path, or that path together with the deleted edge would be a cycle. Hence the component containing the edge splits in two and the number of components rises from to . The remaining graph is acyclic with edges, so the hypothesis gives .
In particular a forest with vertices and components has edges.
Theorem 5.9 (Six characterisations of a tree).
Let be a finite simple undirected graph with vertices. The following are equivalent.
- is connected and acyclic.
- is acyclic and has edges.
- is connected and has edges.
- is acyclic, and adding any missing edge creates exactly one simple cycle.
- is connected, and deleting any edge disconnects it.
- Every pair of distinct vertices is joined by exactly one simple path.
In particular each condition characterises a tree. For the clauses about adding or deleting an edge are vacuous.
Discussion.
Six conditions are proved equivalent by a cycle of implications, , so that each follows from each. The proof uses two facts. One is the relation between a cycle and two different paths between the same vertices: a cycle gives two routes around it, and two different paths give a cycle where they separate and rejoin. The other is the edge count for acyclic graphs, which converts between “has edges” and “has one component”. The last implication, , uses the growing argument from the edge bound for connected graphs: it produces edges forming a connected acyclic spanning subgraph, and a graph with only edges has no others.
Proof.
. Connectivity supplies a simple path between every pair. If two different simple paths joined the same pair, follow them from the common start until they first diverge, and then along the first until it next meets the second; the two segments between those meeting points form a cycle. Thus the path is unique.
. is connected by 6. The unique simple path between the endpoints of an edge is that edge itself, so deleting it leaves the endpoints with no path between them.
. If had a cycle, deleting one of its edges would leave the endpoints of that edge connected along the rest of the cycle, so it would not disconnect . So is acyclic. By connectivity a simple path joins the endpoints of any missing edge, and adding the edge closes the path into a simple cycle. Now is connected and acyclic, so by that path is unique, and every simple cycle through the new edge consists of the new edge and a simple path of between its endpoints; so the new simple cycle is unique.
. If had two components, adding an edge between them would create no cycle, contrary to 4. Thus , and since is acyclic the edge count gives .
. Since is acyclic, the edge count gives . One component means that is connected.
. Because is connected, start at one vertex and repeatedly add an edge to a new vertex until every vertex is reached, as in the first proof of the edge bound. The chosen edges form a spanning subgraph with exactly edges, one for each vertex other than the first. It is connected, since every vertex was joined to one reached earlier, and acyclic, since each edge was added to a new vertex and so cannot close a cycle among those already reached. Since itself has exactly edges, it has no others, so is this subgraph and is acyclic.
In the tree of Figure 5.4 the unique path from to is , of length . Removing separates it into two components of sizes and . Adding the missing edge creates the unique cycle .
Rooted Trees
Definition 5.10 (Out-arborescence).
An out-arborescence rooted at is a directed graph in which there is exactly one directed simple path from to each vertex, and whose underlying undirected graph is a tree. Equivalently, has in-degree zero, every other vertex has in-degree one, and every vertex is reachable from . It has arcs.
Orienting the tree of Figure 5.4 away from , as , , , , and , gives the out-arborescence with root on the right of the figure.
Definition 5.11 (Rooted and binary trees).
A rooted tree is a tree with one vertex chosen as its root. For a vertex , the vertex before on the unique path from to is the parent of , and is a child of its parent. A vertex with no children is a leaf, and the others are internal. The depth of is , the length of the path from to , and the height of the tree is the largest depth of a vertex. The subtree at consists of and every vertex whose path from passes through , rooted at .
A binary tree is a rooted tree in which every vertex has at most two children, each labelled as a left child or a right child, with at most one of each. It is full if every internal vertex has exactly two children.
Orienting each edge of a rooted tree from parent to child gives an out-arborescence rooted at , since the unique path from to in the tree becomes the unique directed path. The subtrees at the children of the root of a binary tree are again binary trees, of height one less at most.
Proposition 5.12 (How large a binary tree of given height can be).
A binary tree of height at most has at most leaves and at most vertices.
Discussion.
A binary tree consists of its root and at most two smaller binary trees below it, so both counts are proved by induction on the height. Each subtree at a child of the root has height at most , so by induction each has at most leaves and vertices, and there are at most two of them. The leaves of the whole tree are the leaves of the subtrees, unless the root has no children, and the vertices are those of the subtrees plus the root. The recurrence with is the Tower of Hanoi recurrence shifted by one.
Proof.
We use induction on . A binary tree of height is a single vertex, which is a leaf: leaves and vertices.
Let and suppose the claim holds for height at most . If the root has no children the tree has one leaf and one vertex. Otherwise the root is not a leaf, and every other vertex lies in the subtree at exactly one of the at most two children of the root; each such subtree is a binary tree of height at most . So there are at most leaves, and at most vertices.
Corollary 5.13 (Height of a binary tree).
A binary tree with vertices has height at least , and a binary tree with leaves has height at least .
Proof.
If the height is , the proposition gives , so , and since is an integer, . Likewise gives , and so .
Show that every tree with at least two vertices has at least two leaves, that is, vertices of degree one, and that a full binary tree with leaves has exactly internal vertices.
Count the binary trees with , , and vertices, where two binary trees are the same only if they have the same shape including the left and right labels. Find a recurrence for the number with vertices by considering the sizes of the two subtrees at the root.
Searching
To search an array for a key is to return an index with , or to report that there is none. This is the find(k) operation of the set interface on an array.
Linear Search
Linear search inspects the entries in order and returns the first index whose key equals . The entries need not be in any order.
Linear Search
Input: an array T[0], …, T[n − 1] and a key x.
Output: an index i with key(T[i]) = x, or −1 if there is none.
for i ≝ 0 to n − 1 do
if key(T[i]) = x then return i
return −1
def linear_search(T, x):
for i in range(len(T)):
if T[i] == x:
return i
return -1
T = [7, 3, 11, 2, 5]
print(linear_search(T, 2), linear_search(T, 4)) # 3 -1
The invariant before the pass for is that does not occur among . It holds trivially at , a pass that does not return extends it by one position, and when the loop ends without returning it says that does not occur at all. The variant is .
Proposition 5.14 (Cost of linear search).
Linear search on an array of entries makes at most comparisons with , and exactly when does not occur. If occurs exactly once, at each of the positions with equal likelihood, the mean number of comparisons is . So linear search takes time in the worst case and on average.
Discussion.
Each pass makes one comparison, and the pass for is reached only if was not among the first entries, so the count is the position at which the search stops, plus one. The worst case is an absent key, which runs through all passes. For the mean, a key at position costs comparisons, and averaging is the sum of an arithmetic progression divided by .
Proof.
The loop makes one comparison per pass and at most passes, and it makes all when no entry has key . If occurs exactly once, at position , the search stops after the st comparison. Averaging over the equally likely positions and using the sum of an arithmetic progression,
Each comparison and each pass cost a bounded number of elementary operations, so the running time is in both the worst case and the mean.
On an unsorted array no comparison algorithm does better. We prove this with an adversary, as in the second proof of the proposition on finding a maximum.
Proposition 5.15 (Searching an unsorted array).
Every deterministic algorithm that decides whether occurs in an array of entries, and learns about the entries only by comparing them with , compares with all entries on some input.
Discussion.
An adversary answers every comparison as though the entry compared were larger than , and fixes the entries only as far as those answers require. If the algorithm stops before comparing with some entry, that entry is still free. The adversary then sets it equal to if the algorithm answered “absent”, and to anything else if it answered “present at that entry”, and in either case the answers given stay true while the output becomes wrong.
Proof.
Answer every comparison of with an entry as . Suppose that on these answers the algorithm stops having compared with only some of the entries, and let be one it never compared. Every compared entry can be given a key larger than , consistently with all the answers. If the algorithm reports that does not occur, give the key : the answers are unchanged, so a deterministic algorithm gives the same wrong report. If it reports an index , then either was compared and has key larger than , or and we give a key different from ; in both cases the report is wrong. So on some input every entry is compared with .
Suppose that is absent with probability , and otherwise lies at each of the positions with probability . Find the mean number of comparisons made by linear search.
A sentinel search appends at the end of the array before searching, so that the loop needs no test of the index against . Write it in Python using append and pop, show that it is correct, and count its comparisons of keys and of indices, compared with the linear search above.
Binary Search
When the array is sorted we can search it by bisection, as in Lesson 3, halving a range of positions instead of an interval of reals.
Let be sorted in nondecreasing key order, meaning . Binary search for a target key keeps a candidate interval of positions , that is . It compares with the key at the middle position . If they are equal it returns ; if is smaller it sets ; otherwise it sets . It returns when the interval is empty, .
Binary Search
Input: an array T[0], …, T[n − 1] sorted by key, and a key x.
Output: an index m with key(T[m]) = x, or −1 if there is none.
L ≝ 0; R ≝ n
while L < R do
m ≝ L + ⌊(R − L) / 2⌋
if x = key(T[m]) then return m
if x < key(T[m]) then R ≝ m
else L ≝ m + 1
return −1
def binary_search(T, x):
L, R = 0, len(T)
while L < R:
m = L + (R - L) // 2
if x == T[m]:
return m
if x < T[m]:
R = m
else:
L = m + 1
return -1
T = [2, 3, 5, 7, 11, 13, 17]
print(binary_search(T, 11), binary_search(T, 4)) # 4 -1
The invariant is: if occurs in , then at least one matching index lies in . It holds initially, when the interval is the whole array. Sortedness maintains it: if then every position from on has key at least and can be discarded, and symmetrically when . At termination either a match has been returned or is empty, and then by the invariant does not occur. The length is a variant: it strictly decreases at every pass. We count one comparison per pass, a three-way comparison of with whose outcomes are smaller, equal and larger.
Theorem 5.16 (Binary search takes comparisons).
For , binary search on a sorted array of length makes at most passes of its loop.
Discussion.
The proof is the one for the convergence of bisection, with the length of the interval in place of its width. Because the middle position is removed from the interval, a pass leaves at most of the positions rather than exactly half. After passes the length is therefore at most , which is zero once , and the least such is .
Proof.
Let at the start of a pass, so that . If the pass does not return, the new interval is , of length , or , of length . So after each pass the length is at most , and since for , after passes it is at most . For we have , so the length is and the loop has stopped.
The bound is attained, for example by an unsuccessful search for a key smaller than every key in the array: it moves left at every pass, and the length goes down to .
For the positions tested form the binary tree of Figure 5.5: the root is the first position tested, and the left and right children of a position are the ones tested next when the target is smaller or larger. A search that stops at depth has made comparisons.
Proposition 5.17 (Mean cost of a successful search).
Let and suppose the target is one of the keys, each equally likely, with all keys distinct. The mean number of comparisons made by binary search is
Discussion.
For every interval that arises has odd length and splits into two equal halves, so the tree of tested positions is full, with positions at depth for , and a target at depth is found after comparisons. That gives the sum. To evaluate we proceed as for the geometric sum and compare with , whose terms are those of shifted by one place with the multiplier lowered by one, so that the difference is a geometric sum.
Proof.
An interval of length with has middle position with positions on each side, so by induction on the positions tested form a full binary tree with positions at depth , for . The target at a position of depth is found at the th comparison, so the total over the equally likely targets is .
For ,
by the geometric sum. So with , and induction gives : at both sides are , and . Finally , and dividing by gives the stated mean.
The mean is . For arbitrary interval halving gives an worst case, by the theorem. For a lower bound on the mean, the positions found within comparisons are those of depth less than in the tree of tested positions, and by the proposition on binary trees there are at most of them. Take for ; then , so fewer than half the positions are found within comparisons. At least half need more than , and the mean over equally likely targets is .
A Lower Bound for Searching
A deterministic comparison search algorithm can be pictured as a fixed binary decision tree of all its possible executions, in which each internal vertex is a comparison the algorithm makes. The algorithm walks down the tree from the root: the first comparison it makes is at the root, and according to the outcome it continues at one of the two children. It stops on reaching a leaf, which records its output, so there must be a leaf for every possible output. The number of comparisons on a given input is the depth of the leaf reached, and the worst-case number is the height of the tree.
Theorem 5.18 (Searching needs comparisons).
Every deterministic algorithm that searches a set of items with distinct keys for a given key, using only comparisons of keys, makes at least comparisons on some input.
Discussion.
The proof counts outputs. A search can end in different ways, one for each stored item and one for “not present”, and each must appear at some leaf of the decision tree. A binary tree with that many leaves has height at least by the corollary on heights, and height is the worst-case number of comparisons.
Proof.
The algorithm has possible outputs, the stored items and the report that no item has the key, and each occurs for some input, so its decision tree has at least leaves. By the corollary on heights of binary trees its height is at least , and some input follows a path of that length.
So binary search makes the least possible number of comparisons, up to a constant factor. The same argument with any fixed number of outcomes per comparison in place of two still gives . Hashing does better because it is not a comparison algorithm: reading a direct access array at an index computed from the key can go to any one of its slots in a single step.
Modify binary_search so that it returns the least index with , or if there is none, still in time. State the invariant, and use your function to count the entries of a sorted array lying in an interval .
A programmer writes L = m in place of L = m + 1. Give an array and a key for which the modified loop never terminates, and say which part of the termination argument fails.
Sorting
The Sorting Problem
We sort records, each carrying a key that can be compared with other keys. A comparison sort learns about the keys only by comparing them, as in the comparison model.
Definition 5.19 (Sorting, in place and stable).
A sort returns the same records as its input, rearranged in nondecreasing key order. A sort is in place if it uses auxiliary cells besides the input array, apart from stack space for recursion if that is being counted separately. A sort is stable if records with equal keys keep their input order.
Checking only that the output keys are in order is not enough to certify a sort: the output must also be a rearrangement of the input records, with none lost or duplicated. Stability can be forced in any comparison sort by comparing the pairs lexicographically, first by key and then by index, though storing the original indices may change the space requirements.
Example 5.20 (Stable and unstable).
Sorting stably by the numeric key gives : the two records with key remain in their original relative order. A routine that returns has sorted the records, but not stably.
A rearrangement of records is a permutation, written as the list of the input positions in output order, and there are of them. A brute-force method tests permutations one by one until it finds an arrangement in increasing order of pairwise distinct keys. A candidate may need comparisons of adjacent keys to certify that it is sorted, and there are candidates.
An inversion of an array is a pair of positions with .
A sorted array has no inversions, and an array of distinct keys in decreasing order has all possible ones, one for each unordered pair of positions. An exchange of two adjacent records with removes exactly one inversion, that pair itself, since the relative order of every other pair is unchanged.
Selection Sort
Selection sort repeatedly removes the largest remaining key and puts it at the next final position from right to left. Having already sorted the largest items into the subarray A[i+1:], it scans A[:i+1] for the largest item not yet placed and swaps it with A[i].
Selection Sort
Input: an array A[0], …, A[n − 1].
Output: the same array, sorted.
for i ≝ n − 1 down to 1 do
m ≝ i
for j ≝ 0 to i − 1 do
if key(A[m]) < key(A[j]) then m ≝ j
swap A[m] and A[i]
def selection_sort(A):
for i in range(len(A) - 1, 0, -1): # O(n) passes
m = i # O(1) index of the largest so far
for j in range(i): # O(i) search A[:i] for a larger item
if A[m] < A[j]: # O(1)
m = j # O(1) new largest found
A[m], A[i] = A[i], A[m] # O(1) swap
A = [5, 2, 9, 1, 5, 6]
selection_sort(A)
print(A) # [1, 2, 5, 5, 6, 9]
The invariant before the pass for is that A[i+1:] holds the largest records in sorted order, and that every key in A[:i+1] is at most every key in A[i+1:]. The pass moves the largest key of A[:i+1] to position , which extends the sorted suffix by one. When the loop ends, at , the suffix A[1:] is sorted and A[0] holds the smallest key.
Finding the maximum among remaining keys takes comparisons, which is the least possible by the proposition on finding a maximum, so the method uses
comparisons, even on an input that is already sorted. It performs at most swaps. It is in place: apart from the array it uses the names i, j and m.
On , selection chooses , then , then . Each chosen maximum occupies its final, rightmost open position, and the comparison counts are .
Selection sort, swapping as above, is not stable. On the records , compared by key alone, the first pass finds at position as the maximum and swaps it with at position , giving ; the second pass makes no change, and ends before .
Bubble Sort
A left-to-right pass of bubble sort compares with and exchanges them if they are out of order. The largest key in the scanned part moves to its right end, so a full pass places the maximum of the active prefix at its final position. Repeating on successively shorter prefixes sorts the array. If a pass makes no exchange, the array is already sorted and the algorithm can stop early.
Bubble Sort
Input: an array T[0], …, T[n − 1].
Output: the same array, sorted.
for last ≝ n − 1 down to 1 do
changed ≝ false
for j ≝ 0 to last − 1 do
if key(T[j]) > key(T[j + 1]) then
swap T[j] and T[j + 1]; changed ≝ true
if not changed then stop
def bubble_sort(T):
for last in range(len(T) - 1, 0, -1):
changed = False
for j in range(last):
if T[j] > T[j + 1]:
T[j], T[j + 1] = T[j + 1], T[j]
changed = True
if not changed:
return
The invariant before the pass with a given last is that T[last+1:] holds the largest records in sorted order, each at least every key in T[:last+1].
For , the worst-case number of comparisons is , and the best case, on a sorted input, is with the early stop. Over the orders of distinct keys, counted equally, the mean number of comparisons is still . For the lower bound, the smallest key begins in the last half of the array, at one of the positions from on, in at least half of the orders. A left-to-right pass moves the smallest key left by at most one position, so on those orders at least passes are needed, and each of those passes makes at least comparisons. Thus at least half the orders cost at least comparisons, and the mean is . The upper bound holds on every input.
Proposition 5.23 (Exchanges in bubble sort).
The number of exchanges bubble sort makes on an array equals its number of inversions. Over the orders of distinct keys, counted equally, the mean number of inversions is .
Discussion.
For the first statement, every exchange removes exactly one inversion, and the algorithm stops with a sorted array, which has none; so the number of exchanges is the number of inversions at the start. For the mean, pair each order with its reverse. A pair of positions is inverted in exactly one of the two, so the inversion counts of an order and of its reverse add up to . The reversal pairs the orders off, so the average over all of them is half of .
Proof.
Bubble sort exchanges and only when , and such an exchange of adjacent records removes exactly one inversion. It stops with a sorted array, which has no inversions. So if the input has inversions, it makes exactly exchanges.
For an order of distinct keys let be its number of inversions, and let be the reverse order. For each of the pairs of keys, exactly one of and lists the larger key first, so . Reversal is a bijection from the set of orders to itself, so summing over all orders,
and the mean is .
Exchanges happen only on strict inversions, so records with equal keys are never exchanged with each other and keep their original order: bubble sort is stable. Small keys near the right end move slowly, one position per pass, and are sometimes called turtles; large keys near the left move quickly to the right and are called hares. Bidirectional bubble sort alternates the direction of its passes to move turtles faster, and gnome sort steps back to recheck the previous adjacent pair after each exchange.
Insertion Sort
Insertion sort treats T[0..i-1] as sorted and inserts T[i] into that prefix by shifting the larger records one position right.
Insertion Sort
Input: an array T[0], …, T[n − 1].
Output: the same array, sorted.
for i ≝ 1 to n − 1 do
x ≝ T[i]; j ≝ i − 1
while j ⩾ 0 and key(x) < key(T[j]) do
T[j + 1] ≝ T[j]; j ≝ j − 1
T[j + 1] ≝ x
def insertion_sort(T):
for i in range(1, len(T)): # O(n) passes
x = T[i] # the record to insert
j = i - 1
while j >= 0 and x < T[j]: # O(i) shift larger records right
T[j + 1] = T[j]
j = j - 1
T[j + 1] = x # fill the gap
The invariant before the pass for is that T[0..i-1] holds the first input records in sorted order. The final assignment T[j+1] = x places the saved record in the gap left by the shifts, and it is essential: writing anywhere else would duplicate one record and lose another. The while test relies on and stopping as soon as j >= 0 is False, so that T[-1] is never read.
On , save , shift right and write at index , giving . Next save , shift right and write at index , giving .
Insertion sort is in the worst case, on an input in decreasing order, where the pass for shifts all records of the prefix, and on an already sorted input, where every pass makes one comparison and no shift.
In Place and Stable
Selection sort, bubble sort and insertion sort are all in place: each uses a constant amount of space besides the array, and acts on the array only by comparisons and by exchanging or moving records. Bubble sort and insertion sort are stable, insertion sort because a record is never shifted past one with an equal key. Selection sort, as implemented above, is not.
Show that insertion sort performs exactly as many shifts T[j + 1] = T[j] as the input has inversions, and deduce the mean number of shifts over the orders of distinct keys. Give an input of length on which selection sort makes swaps and insertion sort makes no shift.
Run bubble sort and insertion sort on , keeping the two s apart as and . Record the array after each pass of each algorithm, count the comparisons and exchanges or shifts, and confirm that both outputs keep before .
Merge Sort
Merge sort splits the array in half, sorts each half recursively, and merges the two sorted halves into one.
Proposition 5.25 (Merging two sorted arrays).
Two sorted arrays of lengths can be merged into one sorted array with at most key comparisons. If ties take the record from the left array first, the merge is stable.
Discussion.
The smallest remaining record of the whole is always at the front of one of the two arrays, because each array is sorted, so one comparison of the two front records decides which comes next. Each comparison outputs one record, and once one array is exhausted the rest of the other is copied without comparing. At least one record, the last, is output without a comparison, which gives . For stability, when the two front keys are equal, taking the left one keeps records from the left array ahead of equal records from the right.
Proof.
Keep a cursor at the first unmerged element of each array. While both arrays have elements left, compare their current heads and copy the smaller one to the output. Its key is no larger than any remaining key, because each input array is already sorted. So the output stays sorted, and no record is lost or copied twice. Each comparison advances one cursor, so after at most comparisons one array must be empty; the other array’s sorted tail is then appended without further comparisons. On equal keys, choosing the left head first preserves the original order between the two arrays, while the order within each array is unchanged.
Merge
Input: sorted arrays X and Y.
Output: a sorted array Z holding the records of X and Y.
i ≝ 0; j ≝ 0; Z ≝ empty
while i < length(X) and j < length(Y) do
if key(X[i]) ⩽ key(Y[j]) then append X[i] to Z; i ≝ i + 1
else append Y[j] to Z; j ≝ j + 1
append the remaining part of X, then of Y, to Z
return Z
Merge Sort
Input: an array A.
Output: a sorted array holding the records of A.
if length(A) ⩽ 1 then return A
h ≝ ⌊length(A) / 2⌋
return Merge(Merge Sort(A[0..h − 1]), Merge Sort(A[h..length(A) − 1]))
def merge(X, Y):
i, j, Z = 0, 0, []
while i < len(X) and j < len(Y):
if X[i] <= Y[j]:
Z.append(X[i])
i = i + 1
else:
Z.append(Y[j])
j = j + 1
return Z + X[i:] + Y[j:]
def merge_sort(A):
if len(A) <= 1:
return A
h = len(A) // 2
return merge(merge_sort(A[:h]), merge_sort(A[h:]))
print(merge_sort([15, 5, 64, 8, 12, 6, 4, 35])) # [4, 5, 6, 8, 12, 15, 35, 64]
On an array of records with comparable keys, merge sort returns a sorted rearrangement of the input, and choosing the left head on equal keys makes it stable. It uses time and auxiliary storage. For , its worst-case number of key comparisons is exactly .
Discussion.
Correctness is an induction on whose step is the merge proposition: the recursive calls are on shorter arrays, so by the hypothesis they return sorted rearrangements of the halves, and merging those gives a sorted rearrangement of the whole. For the time, we count level by level: the arrays at one level are disjoint pieces of the input, so merging all of them costs , and halving gives about levels. For the exact count, the merge proposition allows a merge of two halves of size up to comparisons, which gives the recurrence . It remains to find an input that attains the bound at every merge, and to solve the recurrence.
Proof.
We prove correctness by induction on . For the array is already sorted. If , both halves have fewer than elements, so the recursive calls terminate and, by induction, return sorted rearrangements of their halves. The merge proposition combines those into a sorted rearrangement of the whole input. Its tie rule preserves the order of equal-key records across the halves, so induction also proves stability. The recursion terminates because every call on more than one record is made on strictly shorter arrays.
At any fixed level of the recursion the subarrays are disjoint parts of the original array, so their lengths add up to at most . Merging each costs time proportional to its length, so the total work per level is . Halving gives at most levels of merging, and therefore time. At any instant the arrays alive along the current chain of calls have lengths at most together with their merge outputs, so at most auxiliary storage is present at once; the recursion stack has frames.
When , a merge of two sorted halves of equal size can need all comparisons the merge proposition allows: give the left half the records of odd rank and the right half those of even rank, so that their sorted values alternate until only one remains. Apply the same odd–even assignment recursively within each half. Arranging the input by these assignments makes every merge attain its worst case. Writing for the worst case at size , this gives and
We claim . At both sides are . Assuming the formula at ,
With this is . It is a count of key comparisons, and an array of one record makes none.
Write an iterative merge sort that merges adjacent runs of length , then , then , and so on, with no recursion. Prove it correct with a loop invariant on the run length, and show that it makes at most comparisons.
Quicksort
Quicksort chooses a pivot, partitions the array so that smaller keys precede the pivot and larger or equal keys follow it, and then recursively sorts the two sides. For the partition in place, with the pivot initially at position , the invariant before inspecting position is: positions hold keys smaller than the pivot, positions hold keys larger than or equal to it, and positions are unexamined. A newly found smaller record is swapped into position and is increased. At the end the pivot is swapped with the record at position . Partitioning records compares each of the other records with the pivot once.
The pseudocode below sorts the half-open slice of the original array in place; no slice is copied. The partition moves only records strictly smaller than the pivot to its left.
Quicksort
Input: an array A and positions L ⩽ R.
Output: A with A[L..R − 1] sorted.
Sort(A, L, R):
if R − L ⩽ 1 then return
choose q uniformly from L, …, R − 1; swap A[L] and A[q]
pivot ≝ A[L]; p ≝ L
for k ≝ L + 1 to R − 1 do
if key(A[k]) < key(pivot) then
p ≝ p + 1; swap A[p] and A[k]
swap A[L] and A[p]
Sort(A, L, p); Sort(A, p + 1, R)
The statement from random import randrange makes randrange available: randrange(L, R) returns an integer chosen uniformly from , the same range as range(L, R).
from random import randrange
def sort_slice(A, L, R):
if R - L <= 1:
return
q = randrange(L, R)
A[L], A[q] = A[q], A[L]
pivot = A[L]
p = L
for k in range(L + 1, R):
if A[k] < pivot:
p = p + 1
A[p], A[k] = A[k], A[p]
A[L], A[p] = A[p], A[L]
sort_slice(A, L, p)
sort_slice(A, p + 1, R)
def quicksort(A):
sort_slice(A, 0, len(A))
If the pivot has rank among distinct keys, meaning keys are smaller, the number of comparisons satisfies
For the input , take the first value as pivot, with no random swap. The partition puts the five smaller values before it and after it, and the in-place result is . Recursively sorting the two sides gives . The order within each side before the recursion depends on the partition routine.
Proposition 5.28 (Worst and best case of quicksort).
For distinct keys, any run of quicksort makes at most key comparisons. This bound is attained when every pivot is the smallest or largest key in its current subarray, so the worst-case number of comparisons is . If every pivot splits its subarray as evenly as possible, the number of comparisons is .
Discussion.
For the upper bound we count pairs of keys. Two keys are compared only when one of them is the pivot, and a pivot is left out of all later calls, so no pair is compared twice; there are pairs. When every pivot is extreme, one side of each partition is empty, and the recurrence becomes , which sums to the same number. For even splits, the depth of the recursion is logarithmic and each level costs at most comparisons, which gives ; the matching lower bound needs a count of how many levels still have large subarrays, and how many comparisons each such level makes.
Proof.
Partitioning a subarray of size compares its pivot with each of the other keys exactly once. A given pair of keys can be compared only when one of them is the pivot, and that pivot is then excluded from all recursive subarrays. Thus no pair is compared twice, and there are at most comparisons. If every pivot is extreme, one recursive side is empty and the other has size . The count obeys with , and hence
For balanced splits, the recursion has levels. At any level the active subarrays are disjoint, so their sizes add up to at most and their partitions make at most comparisons in total. This proves .
For the reverse bound, number the levels from . A balanced split of records leaves at least records on either side. Applying this at each level shows that every subarray at level has at least records. For this is at least , so no branch has yet ended at a call on one record. Before such a level at most pivots have been removed, and the remaining records all belong to at most active subarrays. A subarray of size uses comparisons, so level uses at least comparisons. There are such levels for large , which proves .
The in-place partition is not stable in general. The recursive calls use stack space for balanced splits and when the splits are as uneven as possible.
With a random pivot the cost is an average over the random choices, in the sense of the expectation defined for hashing in the last lesson.
Remark (Expected cost of a randomized algorithm).
The expectation used for hashing is an average over one uniform choice. Randomized quicksort makes a uniform choice at every call, and its expected cost is defined in the same way one call at a time: if a call on keys chooses the pivot rank with probability , and the rest of the run then has expected cost , the expected cost of the call is its own comparisons plus . Equivalently, it is the average over all complete runs, each run weighted by the product of the probabilities of the choices it makes.
Theorem 5.29 (Expected cost of randomized quicksort).
Fix any input order of distinct keys. At every recursive call choose the pivot uniformly from the current subarray, and compare it once with each other key there. Let be the expected total number of key comparisons. Then
In particular . The expectation is over the pivot choices; the input order is fixed and need not be random.
Discussion.
The proof has four steps. Conditioning on the rank of the first pivot turns the expectation into a recurrence in which depends on all of through their sum. Writing the recurrence at and at and subtracting removes the sum, leaving a first-order recurrence with variable coefficients, of the kind the summation factor of the last lesson solves; the example there is the same recurrence with in place of . Here the answer is checked by induction instead. Finally the bounds on give the growth rate.
Proof.
Step 1: condition on the first pivot. A subarray of size at most needs no comparisons, so . For the first partition costs exactly comparisons. The pivot is equally likely to have any rank among the keys. Rank leaves smaller keys on the left and larger keys on the right. Because every later pivot is chosen uniformly within its own subarray, the expected costs of those two calls are and , whatever their internal order. Averaging over the possible ranks gives
since both and run once through as runs through .
Step 2: remove the sum. Multiply this equation by , and write the same equation for multiplied by :
Subtract the second from the first. The two sums differ only by , while . Therefore , that is,
Step 3: solve by induction. At the claimed expression is . Assume for some . Substituting into the last recurrence and using ,
Dividing by gives .
Step 4: the growth rate. By the bounds on the harmonic numbers, . The upper bound gives . The lower bound gives , and for all sufficiently large the term exceeds twice the rest, so . Thus .
For three distinct keys, a smallest or largest first pivot costs two comparisons and leaves a call on two keys costing one more, for a total of . A middle first pivot costs two comparisons and leaves only calls on one key, for a total of . The three ranks are equally likely, so . The formula agrees: .
Show that on the input quicksort with the first record always taken as pivot makes comparisons, and that with a uniformly random pivot the probability of making all is for .
Two keys of ranks are compared by randomized quicksort exactly when the first pivot chosen from the keys of ranks is one of the two. Deduce that they are compared with probability , and use linearity of expectation to give a second proof that .
Lower Bounds for Comparison Sorting
A deterministic comparison sort is described, like a comparison search, by a binary decision tree: each internal vertex is a comparison, the two branches below it are its two outcomes, and each leaf records the rearrangement the algorithm outputs. An algorithm may ask a comparison whose result is already forced by earlier answers, and then one branch below it is reached by no input.
The comparison tree of Figure 5.6 names the compared records rather than their changing positions in the array. Its six reachable leaves are the six possible strict orders; the crossed leaves are impossible by transitivity.
Theorem 5.31 (Worst-case lower bound for sorting).
Every deterministic sorting algorithm that learns about distinct, otherwise arbitrary keys only by comparing pairs makes at least comparisons on some input. Hence its worst-case number of comparisons is .
Discussion.
As for searching, the proof counts outputs, and there are now of them. Two inputs whose keys are in different relative orders need different rearrangements, so they must reach different leaves, and the decision tree has at least reachable leaves. The corollary on heights of binary trees then bounds the height below by . For the growth rate, half of the factors of are at least , so is at least about .
Proof.
There are possible relative orders of the distinct input keys: choices for the rank of the first record, for the second, and so on. Represent the algorithm by its binary decision tree. Each input follows one path from the root to a leaf, and the length of that path is its number of comparisons. Two different relative orders cannot end at the same leaf: that leaf prescribes one output rearrangement for both, and for at least one of them it is wrong. Therefore the tree has at least reachable leaves, and after removing the unreachable branches it is a binary tree with at least leaves. By the corollary on heights its height satisfies .
The last factors of are each at least , so and
Removing the branches that no input can follow, and contracting each vertex left with one child, turns the decision tree into a full binary tree with one leaf per possible outcome.
Merge sort makes at most comparisons, so it is asymptotically optimal among comparison sorts in the worst case.
The mean number of comparisons over the equally weighted orders of distinct keys is the mean depth of the leaves of the decision tree, one leaf per order. A full binary tree with leaves is balanced if its leaf depths differ by at most one.
Proposition 5.32 (Balanced trees minimise the total depth).
Among full binary trees with leaves, a balanced one has the least sum of leaf depths, and in a balanced full binary tree with leaves every leaf has depth at least .
Discussion.
The first claim is proved by an exchange that lowers the depth sum of an unbalanced tree. If some leaf is at least two levels above the deepest leaves, move a deepest pair of sibling leaves together with their parent into the place of , and put where their parent was. Three depths change, and the sum falls. The depth sum is a nonnegative integer, so the exchange can be repeated only finitely often, and it stops at a balanced tree. For the second claim, look at the shallowest leaf: every level above it is full, and all leaves lie on its level or the next, which bounds by twice the size of its level.
Proof.
Take a full binary tree that is not balanced. Choose deepest sibling leaves , at depth , with parent , and a leaf at depth . Exchange the subtree consisting of , and with the leaf . The tree remains a full binary tree with the same leaves, and move to depth , and moves to depth . The sum of these three depths changes from to , a decrease of , and no other depth changes. Repeating the operation reaches a balanced tree, because the nonnegative integer depth sum strictly decreases each time. Every tree with leaves can be transformed in this way into a balanced one with no larger depth sum, and all balanced full binary trees with leaves have the same multiset of leaf depths, as the count below shows; so a balanced tree has the least sum.
Let the shallowest leaf of a balanced full binary tree have depth . There is no leaf above depth , so every vertex at depth less than is internal with two children, and depth holds vertices. The leaves are at depth or , and those at depth are the children of the internal vertices at depth , so . At least one vertex at depth is a leaf, so . This determines , and with it the number of leaves at each depth, and every leaf has depth at least .
For example, six leaves can occur at depths and . The proposition gives the asymptotic bound: the mean depth of any full binary tree with leaves is at least , which is . The exact bound is proved differently.
Theorem 5.33 (Average lower bound for sorting).
Suppose the relative orders of distinct keys are equally likely. Every deterministic comparison sort has mean number of comparisons at least , and therefore .
Discussion.
The proof turns the leaf depths into lengths of intervals. Each leaf is reached by a word of left and right choices, and a word of length picks out a subinterval of of length by halving repeatedly. Since no leaf’s word begins another leaf’s word, the intervals do not overlap, so their lengths add up to at most . The mean depth is then bounded below by comparing the arithmetic mean of the numbers with their geometric mean. The inequality of the means was proved for two numbers in Lesson 1; the proof extends it to numbers by doubling up to a power of two and padding.
Proof.
Keep one reachable leaf of the decision tree for each of the relative orders, and let their depths be ; the mean number of comparisons is . Each path from the root to a leaf is a word of left and right choices. No leaf’s word can be a prefix of another leaf’s word, because a computation stops when it reaches a leaf. To a word of length associate the subinterval of of length obtained by choosing the left or right half at each successive letter. The intervals for distinct leaf words do not overlap, so their total length is at most :
Write and , so that . We need the inequality of arithmetic and geometric means for positive numbers, . For two numbers it is . For a list of numbers, split it into two equal halves with geometric means and arithmetic means ; by induction on , and , so the whole list has geometric mean , its arithmetic mean. For general , choose a power of two and append copies of to the list . The enlarged list still has arithmetic mean , so the power-of-two case gives , and cancelling the positive factor gives .
Substituting ,
Taking base-two logarithms and multiplying by , which reverses the inequality, gives . Finally , as in the worst-case bound.
With six equally likely outcomes, a full binary tree may have two leaves at depth and four at depth . Its mean depth is , which is at least . The same leaf-depth argument applies to the possible input orders.
Sorting algorithms that are faster than exist, such as counting sort and radix sort, but only because they use information about the keys beyond pairwise comparison, for instance that they are small integers usable as array indices.
Draw a decision tree for insertion sort on three distinct keys , and give its height and the mean depth of its leaves. Compare both with and .
Show that five distinct keys can be sorted with comparisons in the worst case, and that no comparison sort does it with .
Sorted Arrays as Sets
A sorted array implements the set interface. Building it is sorting, by merge sort. find(k) is binary search, . The smallest and largest keys are at the two ends, and find_next(k) and find_prev(k) are binary searches for the position where would go. Inserting or deleting while keeping the order shifts up to items.
| Data structure | build(X) | find(k) | insert(x), delete(k) | find_min(), find_max() | find_prev(k), find_next(k) |
|---|---|---|---|---|---|
| Array | |||||
| Sorted array | |||||
| Direct access array | |||||
| Hash table | (e) | (e) | (a)(e) |
Each entry is an bound; (e) marks an expected bound and (a) an amortized one. For the sorted array, find is optimal among comparison algorithms by the lower bound for searching, and the bound does not apply to the hash table, which is not a comparison algorithm.
Implement the set interface on a sorted array as a class Sorted_Array_Set, using merge_sort in build and binary search in find, find_next and find_prev, with the running times of the table.
Exercises on Connectivity and Trees
How many spanning subgraphs of are trees?
Prove that every connected graph with at least two vertices has a vertex that is not an articulation vertex.
Let , and let be integers. Show that there is a tree with vertex degrees if and only if and .
Let be subtrees of a tree , meaning subgraphs that are trees, any two of which have at least one vertex in common. Prove that some vertex lies in every .
The radius of a connected graph is , the least over all vertices of the greatest distance from to another vertex. Let be the cycle on vertices, with vertices and edges and . Find the radius and the diameter of , of and of .
A directed graph has vertices. Its underlying undirected graph has one connected component of size , and has one strongly connected component of size ; its other strongly connected components are smaller.
- Suppose vertices are reachable from a vertex . Is in the component of size ? Explain.
- Is in the strongly connected component of size ? Explain.
- Suppose vertices are reachable from a vertex , and vertices are reachable from once every arc is reversed. Is in the strongly connected component of size ? Explain.
- Prove that for every vertex of the component of size , there is a directed walk from to or one from to .
The divisibility graph has vertices and an edge between whenever one of them divides the other.
- Draw . How many connected components does it have, and what is the size of its largest clique?
- Prove that for every , a graph with no loops in which every vertex has degree at least contains a simple cycle through at least vertices.
Exercises on Searching
A narrow island runs north–south for kilometres, and a searcher must locate a friend to the nearest kilometre. A tracking device tells the searcher whether the friend is north or south of the current position, but not how far, and a teleporter jumps to any given kilometre in constant time. If the friend is kilometres from the nearer end of the island, at kilometre or , describe an algorithm that finds the friend after visiting locations.
A ridge is given as an array of distinct altitudes. A point is a good collection point if it is lower than all its neighbours in the array. Design an algorithm running in time that finds a good collection point, prove it correct, and prove its running time.
Exercises on Sorting
Implementations of insertion sort and merge sort run on the same machine. On inputs of size , insertion sort takes steps and merge sort takes steps. For which values of does insertion sort beat merge sort?
Person , for , enters a room at time and leaves at time , all the being distinct. The lights are off at the start of the day; the first person to enter switches them on, and a person who leaves an empty room switches them off. Given , we want the number of times the lights are switched on. Design, and prove correct and costed,
- a algorithm, and
- an algorithm.
For each scenario choose selection sort, insertion sort or merge sort, and justify the choice by asymptotic running time.
- A data structure maintains an extrinsic order on items, with
D.get_at(i)in worst-case time andD.set_at(i, x)in worst-case time. Sort the items of in place. - A static array holds references to comparable objects, any two of which take time to compare. Sort the references so that the objects appear in nondecreasing order.
- A sorted array of integers, each fitting in a machine word, has had exchanges made between pairs of adjacent items. Re-sort it.
- Describe the principle of merge sort, and show the steps it takes to sort the array .
- Insertion sort can be seen as a merge sort in which each step splits an array of size into one of size , the element to be inserted, and one of size . By solving the appropriate recurrence, show that this recursive insertion sort takes time, assuming that merging two arrays takes time.
- Show that merge sort on a linked list takes time, and that it can be done with auxiliary space apart from the recursion, showing how. A programmer who can merge arrays only with extra space proposes to convert arrays to linked lists before sorting them, to save space. Comment on this strategy.
- Suppose that quicksort always partitions into two parts of relative sizes and , for a constant . Ignoring rounding, find the least depth of a leaf in the recursion tree as a function of and .
- How long does the quicksort of these notes take if all the keys are equal? Explain.
- What are the advantages and disadvantages of choosing the pivot at random? How does it affect the worst-case and the average-case running time?
How would you construct an input that makes randomized quicksort take quadratic time, without access to the state of the random number generator?
Merge sort can be implemented as the usual two-way merge sort, or as a three-way merge sort that splits its input into three and sorts each part recursively.
- Find the worst-case number of comparisons needed to merge two sorted arrays of length .
- Find the worst-case number of comparisons needed to merge three sorted arrays of length , both by merging all three at once and by merging in pairs.
- Using these, and solving suitable recurrences, find the total number of comparisons made by two-way and by three-way merge sort.
- If comparisons dominate the cost, which would you expect to be faster on an arbitrary array?
Describe an algorithm, running in time strictly better than , that takes a positive integer and a set of positive integers and decides whether two distinct elements of add up to exactly . Give its running time.
Each of computers has run the same computation, and we want to know whether strictly more than of them arrived at the same result. The only available query takes two computers and reports whether they produced the same result. Design an algorithm that decides this with queries, prove it correct, and prove the bound on the number of queries.
Find an asymptotically tight upper bound for the recurrence , , and explain your answer.
Check Yourself
Fresh questions on the whole lesson — none of them is worked out above. Work each one out on paper before opening Python; the box only tells you whether you got there.
Answers are checked in your browser, as often as you like. Nothing is sent anywhere and
nothing is kept but your own work. A formula may be written with the symbols themselves or
with ~ & | -> <-> ^, and \and, \or, \to expand as you type.
A forest has vertices and components. How many edges does it have?
What is the largest number of leaves a binary tree of height can have?
What is the least height of a binary tree with vertices?
At most how many passes does binary search make on a sorted array of length ?
How many comparisons does linear search make on an array of entries that does not contain ?
How many inversions does the array have?
How many comparisons does selection sort make on an array of records?
What is the worst-case number of key comparisons made by merge sort on records?
What is the expected number of comparisons made by randomized quicksort on four distinct keys?
What is the smallest integer with ?
In how many relative orders can distinct keys arrive?
Which of the three quadratic sorts of these notes is not stable as implemented?
At most how many comparisons does quicksort make on distinct keys?
At most how many comparisons does merging sorted arrays of lengths and take?
How many comparisons does bubble sort with the early stop make on an already sorted array of records?
How many shifts does insertion sort make on ?