This preview shows pages 1–13. Sign up to view the full content.
This preview has intentionally blurred sections. Sign up to view the full version.
View Full DocumentThis preview has intentionally blurred sections. Sign up to view the full version.
View Full DocumentThis preview has intentionally blurred sections. Sign up to view the full version.
View Full DocumentThis preview has intentionally blurred sections. Sign up to view the full version.
View Full DocumentThis preview has intentionally blurred sections. Sign up to view the full version.
View Full DocumentThis preview has intentionally blurred sections. Sign up to view the full version.
View Full Document
Unformatted text preview: us," new— 2 Getting Started This chapter will familiarize you with the framework we shall use throughout the
book to think about the design and analysis of algorithms. It is self—contained, but
it does include several references to material that will be introduced in Chapters
3 and 4. (It also contains several summations, which Appendix A shows how to
solve.) We begin by examining the insertion sort algorithm to solve the sorting problem
introduced in Chapter 1. We deﬁne a “pseudocode” that should be familiar to read—
ers who have done computer programming and use it to show how we shall specify
our algorithms. Having speciﬁed the algorithm, we then argue that it correctly sorts
and we analyze its running time. The analysis introduces a notation that focuses
on how that time increases with the number of items to be sorted. Following our
discussion of insertion sort, we introduce the divide—andconquer approach to the
design of algorithms and use it to develop an algorithm called merge sort. We end
with an analysis of merge sort’s running time. I 2.1 Insertion sort Our ﬁrst algorithm, insertion sort, solves the sorting problem introduced in Chap—
ter 1: Input: A sequence ofn numbers (a1,a2, . . . , an). Output: A permutation (reordering) (ai, a5, . . . , (1;) of the input sequence such I l I
thata1 gazsnEan. The numbers that we wish to sort are also known as the keys. In this book, we shall typically describe algorithms as programs written in a
pseudocode that is similar in many respects to C, Pascal, or Java. If you have been
introduced to any of these languages, you should have little trouble reading our a1
gorithms. What separates pseudocode from “real” code is that in pseudocode, we . Chapter 2 Getting Started Figure 2.1 Sorting a hand of cards using insertion sort. employ whatever expressive method is most clear and concise to specify a given al
gorithm. Sometimes, the clearest method is English, so do not be surprised if you
come across an English phrase or sentence embedded within a section of “real”
code. Another difference between pseudocode and real code is that pseudocode
is not typically concerned with issues of software engineering. Issues of data ab
straction, modularity, and error handling are often ignored in order to convey the
essence of the algorithm more concisely. .We start with insertion sort, which is an efﬁcient algorithm for sorting a small
number of elements. Insertion 5011 works the way many people sort a hand of
playing cards. We start with an empty left hand and the cards face down on the
table. We then remove one card at a time from the table and insert it into the
correct position in the left hand. To ﬁnd the correct position for a card, we compare
it with each of the cards already in the hand, from right to left, as illustrated in
Figure 2.1. At all times, the cards held in the left hand are sorted, and these cards
were originally the top cards of the pile on the table. Our pseudocode for insertion sort is presented as a procedure called INSERTION
SORT, which takes as a parameter an array A[1..n] containing a sequence of
length n that is to be sorted. (In the code, the number n of elements in A is denoted
by length[A].) The input numbers are sorted in place: the numbers are rearranged
within the array A, with at most a constant number of them stored outside the
array at any time. The input array A contains the sorted output sequence when
INSERTIONSORT is ﬁnished. ' 2 .1 Insertion sort I7 Figure 2.2 The operation of lNSERTIOMSORT on the array A = (5, 2, 4. 6, l, 3). Array indices
appear above the rectangles, and values stored in the array positions appear within the rectangles.
(aHe) The iterations of the for loop of lines 1—8. In each iteration, the black rectangle holds the
key taken from A[ j ], which is compared with the values in shaded rectangles to its left in the test of
line Shaded arrows show array values moved one position to the right in line 6, and black arrows
indicate where the key is moved to in line 8. (f) The ﬁnal sorted array. V INSERTIONSORT(A) for j <— 2 to lengthM]
d0 key <— A[j]
D Insert A[ j ] into the sorted sequence A[1 . . j —— I].
i <— j — 1
whilei > 0 and _A[i] > key
do A[i+1] <— A[i]
i <— i —1 A[i +1] é—key OOﬂQMbLDNtd Loop invariants and the correctness of insertion sort Figure 2.2 shows how this algorithm works for A =‘ (5, 2, 4, 6, 1, 3). The in
dex j indicates the “current card” being inserted into the hand. At the beginning
of each iteration of the “outer” for loop, which is indexed by j, the subarray con—
sisting of elements A[1 . . j — 1] constitute the currently sorted hand, and elements
A[ j + l . . n] correspond to the pile of cards still on the table. In fact, elements
A[l .. j — l] are the elements originally in positions 1 through j — 1, but now in
sorted order. We state these properties of A[1 . . j — l] formally as a loop invariant: At the start of each iteration of the for loop of lines 1—8, the subarray
A[1 . . j— 1] consists of the elements originally in AU .. j — l] but in sorted
order. We use 100p invariants to help us understand why an algorithm is correct. We
must show three things about a loop invariant: 18 Chapter 2 Getting Started Initialization: It is true prior to the ﬁrst iteration of the loop. Maintenance: If it is true before an iteration of the loop, it remains true before the
next iteration. Termination: When the loop terminates, the invariant gives us a useful property
that helps show that the algorithm is correct. When the ﬁrst two properties hold, the loop invariant is true prior to every iteration
of the loop. Note the similarity to mathematical induction, where to prove that a
property holds, you prove a base case and an inductive step. Here, showing that
the invariant holds before the ﬁrst iteration is like the base case, and showing that
the invariant holds from iteration to iteration is like the inductive step. The third property is perhaps the most important one, since we are using the loop
invariant to show correctness. It also differs from the usual use of mathematical in
duction, in which the inductive step is used inﬁnitely; here, we stop the “induction”
when the loop terminates. Let us see how these properties hold for insertion sort. Initialization: We start by showing that the loop invariant holds before the ﬁrst
loop iteration, when j = 2.1 The subarray A[1.. j — 1], therefore, consists
of just the single element A[l], which is in fact the original element in A[1].
Moreover, this subarray is sorted (trivially, of course), which shows that the
loop invariant holds prior to the ﬁrst iteration of the loop. Maintenance: Next, we tackle the second property: showing that each iteration
maintains the loop invariant. Informally, the body of the outer for loop works
by moving A[j — 1], AU — 2], AU — 3], and so on by one position to the right
until the proper position for A[ j] is found (lines 4—7), at which point the value
of A[ j ] is inserted (line 8). A more formal treatment of the second property
would require us to state and show a loop invariant for the “inner” while loop.
At this point, however, we prefer not to get bogged down in such formalism,
and so we rely on our informal analysis to show that the second property holds
for the outer loop. Termination: Finally, we examine what happens when the loop terminates. For
insertion sort, the outer for loop ends when j exceeds n, i.e., when j = n + 1.
Substituting n + 1 for j in the wording of loop invariant, we have that the
subarray A[l . . It] consists of,the elements originally in AU . . n], but in sorted _________________4______._————————— 1When the loop is a for loop, the moment at which we check the loop invariant just prior to the ﬁrst
iteration is immediately after the initial assignment to the loopcounter variable and just before the
ﬁrst test in the loop header. In the case of lNSERTION—SORT, this time is after assigning 2 to the
variable j but before the ﬁrst test of whether‘j 5 length[A]. 2.1 Insertion sort 19 order. But the subarray A[l . . n] is the entire array! Hence, the entire array is
sorted, which means that the algorithm is correct. We shall use this method of loop invariants to show correctness later in this chapter and in other chapters as well. Pseudocode conventions We use the following conventions in our pseudocode. l.
J Indentation indicates block structure. For example, the body of the for loop
that begins on line 1 consists of lines 2—8, and the body of the While loop that
begins on line 5 contains lines 6—7 but not line 8. Our indentation style applies
to if—thenelse statements as well. Using indentation instead of conventional
indicators of block structure, such as begin and end statements, greatly reduces
clutter while preserving, or even enhancing, clarity.2 The looping constructs while, for, and repeat and, the conditional constructs
if, then, and else have interpretations similar to those in Pascal.3 There is one
subtle difference with respect to for loops, however: in Pascal, the value of the
loop—counter variable is undeﬁned upon exiting the loop, but in this book, the
loop counter retains its value after exiting the loop. Thus, immediately after a
for loop, the loop counter’s value is the value that ﬁrst exceeded the for loop
bound. We used this property in our correctness argument for insertion sort.
The for loop header in line 1 is for j <— 2 to length[A], and so when this loop
terminates, j = length[A]+1 (or, equivalently, j = n+1, since n = length[A]). . The symbol “D” indicates that the remainder of the line is a comment. A multiple assignment of the form i <— j <— e assigns to both variables 1' and j
the value of expression e; it should be treated as equivalent to the assignment
j <— e followed by the assignment 1‘ {— j. . Variables (such as i, j, and key) are local to the given procedure. We shall not use global variables without explicit indication. . Array elements are accessed by specifying the array name followed by the in— dex in square brackets. For example, A[i] indicates the i th element of the ar
ray A. The notation .” is used to indicate a range of values within an ar 2In real programming languages, it is generally not advisable to use indentation alone to indicate
block structure, since levels of indentation are hard to determine when code is split across pages. 3Most block—structured languages have equivalent constructs, though the exact syntax may differ
from that of Pascal. 20 Chapter 2 Getting Started ray. Thus, A[l . . j] indicates the subarray of A consisting of the j elements
A[1].A[2], m , AU] 7. Compound data are typically organized into objects, which are composed of
attributes or ﬁelds. A particular ﬁeld is accessed using the ﬁeld name followed
by the name of its object in square brackets. For example, we treat an array as
an object with the attribute length indicating how many elements it contains. To
specify the number of elements in an array A, we write lengthMl. Although we
use square brackets for both array indexing and object attributes, it will usually
be clear from the context which interpretation is intended. A variable representing an array or object is treated as a pointer to the data
representing the array or object. For all ﬁelds f of an object x, setting y +— x
causes f [y] = f [x]. Moreover, if we now set f [x] +~ 3, then afterward not
only is f [x] = 3, but f [y] = 3 as well. In other words, x and y point to (“are”)
the same object after the assignment y <— x. ' Sometimes, a pointer will refer‘to no object at all. In this case, we give it the
special value NIL. 8. Parameters are passed to a procedure by value: the called procedure receives
its own copy of the parameters, and if it assigns a value to a parameter, the
change is not seen by the calling procedure. When objects are passed, the
pointer to the data representing the object is copied, but the object’s ﬁelds are
not. For example, if x is a parameter of a called procedure, the assignment
x <— y within the called procedure is not visible to the calling procedure. The
assignment f [x] +— 3, however, is visible. ' 9. The boolean operators “and” and “or” are short circuiting. That is, when we
evaluate the expression “x and y” we ﬁrst evaluate x. If x eValuates to FALSE,
then the entire expression cannot evaluate to TRUE, and so we do not evaluate y.
If, on the other hand, x evaluates to TRUE, we must evaluate y to determine the
value of the entire expression. Similarly, in the expression “x or y” we evaluate
the expression y only if x evaluates to FALSE. Shortcircuiting operators allow
us to write boolean expressions such as “x aé NIL and f [x] = y” without
worrying about what happens when we try to evaluate f [x] when x is NIL. Exercises 2.11
Using Figure 2.2 as a model, illustrate the operation of INSERTIONSORT on the
arrayA = (31,41,59,26,41,58). ' t 2 .2 Analyzing algorithms 21 2.1 2
Rewrite the INSERTIONSORT procedure to sort into nonincreasing instead of non decreasing order. 2 .1 3
Consider the searching problem: Input: A sequence of n numbers A = (a1, a2, .. . , an) and a value 1). Output: An index i such that v 2 AU] or the special value NIL if 1) does not
appear in A. Write pseudocode for linear searc , which scans through the sequence, looking
for 1). Using a loop invariant, prove that your algorithm is correct. Make sure that
your loop invariant fulﬁlls the three necessary properties. 2.1 4 Consider the problem of adding two nbit binary integers, stored in two n—element
arrays A and B. The sum of the two integers should be stored in binary form in
an (n + l)element array C. State the problem formally and write pseudocode for adding the two integers. // 2.2 Analyzing algorithms Analyzing an algorithm has come to mean predicting the resources that the algo
rithm requires. Occasionally, resources such as memory, communication band
width, or computer hardware are of primary concern, but most often it is compu—
tational time that we want to measure. Generally, by analyzing several candidate
algorithms for a problem, a most efﬁcient one can be easily identiﬁed. Such anal
ysis may indicate more than one viable candidate, but several inferior algorithms
are usually discarded in the process. Before we can analyze an algorithm, we must have a model of the implemen
tation technology that will be used, including a model for the resources of that
technology and their costs. For most of this book, we shall assume a generic one
processor, randomaccess machine (RAM) model of computation as our imple‘
mentation technology and understand that our algorithms will be implemented as
computer programs. In the RAM model, instructions are executed one after an—
other, with no concurrent operations. In later chapters, however, we shall have
occasion to investigate models for digital hardware. Strictly speaking, one should precisely deﬁne the instructions of the RAM model
and their costs. To do so, however, would be tedious and would yield little insight
into algorithm design and analysis. Yet We must be careful not to abuse the RAM 22 Chapter 2 Getting Started model. For example, what if a RAM had an instruction that sorts? Then we could
sort in just one instruction. Such a RAM would be unrealistic, since real comput—
ers do not have such instructions. Our guide, therefore, is how real computers are
designed. The RAM model contains instructions commonly found in real com
puters: arithmetic (add, subtract, multiply, divide, remainder, ﬂoor, ceiling), data
movement (load, store, copy), and control (conditional and unconditional branch,
subroutine call and return). Each such instruction takes a constant amount of time. The data types in the RAM model are integer and ﬂoating point. Although we
typically do not concern ourselves with precision in this book, in some applications
precision is crucial. We also assume a limit on the size of each word of data. For
example, when working with inputs of size n, we typically assume that integers are
represented by c lg n bits for some constant c _>_ 1. We require c 2 1 so that each
word can hold the value of n, enabling us to index the individual input elements,
and we restrict c to be a constant so that the word size does not grow arbitrarily. (If
the word size could grow arbitrarily, we could store huge amounts of data in one
word and operate on it all in constant time—clearly an unrealistic scenario.) _ Real computers contain instructions not listed above, and such instructions rep
resent a gray area in the RAM model. For example, is exponentiation a constant
time instruction? In the general case, no; it takes several instructions to compute x"
when x and y are real numbers. In restricted situations, however, exponentiation is
a constanttime operation. Many computers have a “shift left” instruction, which
in constant time shifts the bits of an integer by k positions to the left. In most
computers, shifting the bits of an integer by one position to the left is equivalent to
multiplication by 2. Shifting the bits by k positions to the left is equivalent to mul
tiplication by 2". Therefore, such computers can compute 2" in one constant—time
instruction by shifting the integer l by k positions to the left, as long as k is no more
than the number of bits in a computer word. We will endeavor to avoid such gray
areas in the RAM model, but we will treat computation of 2k as a constanttime
operation when k is a small enough positive integer. In the RAM model, we do not attempt to model the memory hierarchy that is
common in contemporary computers. That is, we do not model caches or virtual
memory (which is most often implemented with demand paging). Several compu—
tational models attempt to account for memoryhierarchy effects, which are some—
times signiﬁcant in real programs on real machines. A handful of problems in this
book examine memoryhierarchy effects, but for the most part, the analyses in this
book will not consider them. Models that include the memory hierarchy are quite a
bit more complex than the RAM model, so that they can be difﬁcult to work with.
Moreover, RAMmodel analyses are usually excellent predictors of performance
on actual machines. Analyzing even a simple algorithm in the RAM model can be a challenge. The
mathematical tools required may include combinatorics, probability theory, alge 7i 2.2 Analyzing algorithms braic dexterity, and the ability to identify the most signiﬁcant terms in a formula.
Because the behavior of an algorithm may be different for each possible input, we
need a means for summarizing that behavior in simple, easily understood formulas. Even though we typically select only one machine model to analyze a given al—
gorithm, we still face many choices in deciding how to express our analysis. We
would like a way that is simple to write and manipulate, shows the important char—
acteristics of an algorithm’s resource requirements, and suppresses tedious details. Analysis of insertion sort The time taken by the INSERTION—SORT procedure depends on the input: sorting a
thousand numbers takes longer than sorting three numbers. Moreover, lNSERTlONe
SORT can take different amounts of time to sort two input sequences of the same
size depending on how nearly sorted they already are. In general, the time taken
by an algorithm grows with the size of the input, so it is traditional to describe the
running time of a program as a function of the size of its input. To do so, we need
to deﬁne the terms “running time” and “size of input” more carefully. The best notion for input size depends on the problem being studied. For many
problems, such as sorting or computing discrete Fourier transforms, the most nat—
ural measure is the number of items in the input—for example, the array size n
for sorting. For many other problems, such as multiplying two integers, the best
measure of input size is the total number of bits needed to represent the input in
ordinary binary notation. Sometimes, it is more appropriate to describe the size of
the input with two numbers rather than one. For instance, if the input to an algo
rithm is a graph, the input size can be described by the numbers of vertices and
edges in the graph. We shall indicate which input size measure is being used with
each problem we study. The running time of an algorithm on a particular input is the number of primitive
operations or “steps” executed. It is convenient to deﬁne the notion of step so
that it is as machineindependent as possible. For the moment, let us adopt the
following view. A constant amount of time is required to execute each line of our
pseudocode. One line may take a different amount of time than another line, but
we shall assume that each execution of the ith line takes time (1», where c, is a
constant. This viewpoint is in keeping with the RAM model. and it also reﬂects
how the pseudocode would be implemented on most actual computers.4 4There are some subtleties here. Computational steps that we specify in English are often variants
of a procedure that requires more than just a constant amount of time. For example, later in this
book we might say "sort the points by .rcoordinate," which. as we shall see. takes more than a
constant amount of time. Also, note that a statement that calls a subroutine takes constant time.
though the subroutine. once invoked. may take more. That is. we separate the process of calling the
subroutine—passing parameters to it, etc—from the process of executing the subroutine. 24 Chapter 2 Getting Started ' In the following discussion, our expression for the running time of INSERTION
SORT will evolve from a messy formula that uses all the statement costs 0, to a
much simpler notation that is more concise and more easily manipulated. This
simpler notation will also make it easy to determine whether one algorithm is more
efﬁcient than another. We start by presenting the INSERTIONSORT procedure with the time “cost”
of each statement and the number of times each statement is executed. For each
j = 2, 3, . . . , n, where n = length[A], we let t j be the number of times the while
loop test in line 5 is executed for that value of j. When a for or while loop exits in
the usual way (i.e., due to the test in the loop header), the test is executed one time
more than the loop body. We assume that comments are not executable statements,
and so they take no time. INSERTION—SORT(A) cost times
1 forj <— 2t0 length[A] c1 n
2 d0key<—A[j] 62 n—l
3 I> Insert A[ j ] into the sorted
sequence All . .j — l]. 0 n ——1
4 i <— j —— 1 C4 n —— 1
5 Whilei > 0 and A[i] > key c5 237:2 tj
6 do A[i +1] <— A[i] c5 27:4” — l)
7 i <— i —1 C7 23:20} _1)
8 A[i+l]<—key C3 11—1 The running time of the algorithm is the sum of running times for each statement
executed; a statement that takes Cf steps to execute and is executed n times will
contribute cm to the total running time.5 To compute T(n), the running time of
INSERTION—SORT, we sum the products of the cost and times columns, obtaining T(n) = cln + cz(n — l) +C4(n — 1) + C5 Zr, +Cﬁz(tj — 1)
’=2 '=2 +C7Z(tj '— 1)+C8(n— 1) .
'=2 Even for inputs of a given size, an algorithm’s running time may depend on
which input of that size is given. For example, in INSERTION—SORT, the best _______‘_________________.___———————————— 5This characteristic does not necessarily hold for a resource such as memory. A statement that
references m words of memory and is executed n times does not necessarily consume mn words of memmy in total. 2.2 Analyzing algorithms 25 case occurs if the array is already sorted. For each j = 2, 3, . . . , n, we then ﬁnd
that A[i] 5 key in line 5 when i has its initial value of j — 1. Thus tj . = l for
j = 2, 3, . . . , n, and the bestcase running time is T(n) = Cin+62(n—1)+C4(n—1)+05(n—1)+Cs(n—1)
(Ci+62+C4+Cs+Cs)n—(C2+C4+¢‘5+Cs) This running time can be expressed as an + b for constants a and b that depend on
the statement costs Ci; it is thus a linear function of 11. If the array is in reverse sorted order—that is, in decreasing order—«the worst
case results. We must compare each element AU] with each element in theentire
sorted subarray A[l , . j — 1], and so tj = j forj = 2, 3, . . . , n. Noting that (see Appendix A for a review of how to solve these summations), we ﬁnd that in
the worst case, the running time of IN SERTIONSORT is T(n) Cln +c2(n — l) +c4(n — l) +C5 — 1) . —1 ~
+ (‘5 + C7 + C301 — 1)
Ca (CZ—5+329+§21)n2+(ci+c2+ca+%53—%+Cs)n (02+C4+65+Cs) This worstcase running time can be expressed as an2 + bn + c for constants a, b.
and c that again depend on the statement costs c,; it is thus a quadratic function
of n. Typically, as in insertion sort, the running time of an algorithm is ﬁxed for a
given input, although in later chapters we shall see some interesting “randomized”
algorithms whose behavior can vary even for a ﬁxed input. Worstcase and averagecase analysis In our analysis of insertion sort, we looked at both the best case, in which the input
array was already sorted, and the worst case, in which the input array was reverse
sorted, For the remainder of this book, though, we shall usually concentrate on 26 C hapter 2 Gertin g Started ﬁnding only the worstcase running time, that is, the longest running time for any
input of size n. We give three reasons for this orientation  The worstcase running time of an algorithm is an upper bound on the running
time for any input. Knowing it gives us a guarantee that the algorithm will never
take any longer. We need not make some educated guess about the running time
and hope that it never gets much worse. ' For some algorithms, the worst case occurs fairly often. For example, in search
ing a database for a particular piece of information, the searching algorithm’s
worst case will often occur when the information is not present in the database.
In some searching applications, searches for absent information may be fre
quent.  The “average case” is often roughly as bad as the worst case. Suppose that we
randomly choose n numbers and apply insertion sort. How long does it take to
determine where in subarray All . . j —— 1] to insert element A[ j]? On average,
half the elements in AU . . j — l] are less than A[j], and half the elements are
greater. On average, therefore, we check half of the subarray A[l . . j — 1]. so
r_, = j / 2. If we work out the resulting averagecase running time, it turns out to
be a quadratic function of the input size, just like the worst—case running time. In some particular cases, we shall be interested in the averagecase or expected
running time of an algorithm; in Chapter 5, we shall see the technique of prob
abilistic analysis, by which we determine expected running times. One problem
with performing an averagecase analysis, however, is that it may not be apparent
what constitutes an “average” input for a particular problem. Often, we shall as
sume that all inputs of a given size are equally likely. In practice, this assumption
may be violated, but we can sometimes use a randomized algorithm, which makes
random choices, to allow a probabilistic analysis. Order of growth We used some simplifying abstractions to ease our analysis of the INSERTION—
SORT procedure. First, we ignored the actual cost of each statement, using the
constants c, to represent these costs. Then, we observed that even these constants
give us more detail than we really need: the worstcase running time is an2 +bn +c
for some constants a, b, and c that depend on the statement costs ('5. We thus
ignored not only the actual statement costs, but also the abstract costs c,. We shall now make one more simplifying abstraction. It is the rate of growth,
or order of growth, of the running time that really interests us. We therefore con
sider only the leading term of a formula (e.g., anz), since the lowerorder terms
are relatively insigniﬁcant for large n. We also ignore the leading term’s constant
coefﬁcient, since constant factors are less signiﬁcant than the rate of growth in 2.3 Desiynm almrithms 27"
r. t 3 detemiining computational efﬁciency for large inputs. Thus. we write that inser
tion sort, for example, has a worstcase running time of @013) (pronounced “theta
of n—squared”). We shall use Q—notation informally in this chapter; it will be de—
ﬁned precisely in Chapter 3. We usually consider one algorithm to be mor: efficient than another if its worst
ease running time has a lower order of growt} . Dueito constant factors and lower~
order terms, this evaluation may be in error ft r Sﬂlull inputs. But for large enough
inputs, a @(HZ) algorithm, for example, will run more quickly in the worst case
than a @023) algorithm. Exercises 2.21
Express the function 123/ 1000 i 100n2  100n + 3 in terms of @—notation. 2.22 Consider sorting n numbers stored in array A by ﬁrst ﬁnding the smallest element
of A and exchanging it with the element in AU]. Then ﬁnd the second smallest
element of A, and exchange it with A[2]. Continue in this manner for the ﬁrst 11 — 1
elements of A. Write pseudoeode for this algorithm, which is known as selection
sort. What loop invariant does this algorithm maintain? Why does it need to run
for only the ﬁrst n  1 elements, rather than for all n eleme its? Give the bestease
and worstcase running times of selection sort in (anotation. 2.23 Consider linear search again (see Exercise 2.13). How many elements of the in—
put sequence need to be checked on the average, assuming that the element being
searched for is equally likely to be any element in the array? How about in the
worst case? What are the average—case and worst—case running times of linear
search in (~)notation? Justify your answers. 2.24
How can we modify almost any algorithm to have a good bestcase running time? _________________,_____4_.___———————————— 2.3 Designing algorithms There are many ways to design algorithms. Insertion sort uses an incremental ap
proach: having sorted the subarray A[l . . j — l]. we insert the single element AU]
into its proper place. yielding the sorted subarray A[l . . j]. ...
View
Full
Document
This note was uploaded on 03/20/2008 for the course CSC 530 taught by Professor Steffenheber during the Spring '08 term at N.C. State.
 Spring '08
 SteffenHeber
 Algorithms

Click to edit the document details