Data Structures in C Notes, Study notes of Data Structures and Algorithms

This book is all about DS and Algo

Typology: Study notes

2019/2020

Uploaded on 07/16/2020

kartik-sharma-16
kartik-sharma-16 🇮🇳

5

(2)

1 document

1 / 492

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
Data Structures and Algorithm Analysis
in C
by Mark Allen Weiss
PREFACE
CHAPTER 1: INTRODUCTION
CHAPTER 2: ALGORITHM ANALYSIS
CHAPTER 3: LISTS, STACKS, AND QUEUES
CHAPTER 4: TREES
CHAPTER 5: HASHING
CHAPTER 6: PRIORITY QUEUES (HEAPS)
CHAPTER 7: SORTING
CHAPTER 8: THE DISJOINT SET ADT
CHAPTER 9: GRAPH ALGORITHMS
CHAPTER 10: ALGORITHM DESIGN TECHNIQUES
CHAPTER 11: AMORTIZED ANALYSIS
页码,1/1Structures, Algorithm Analysis: Table of Contents
2006-1-27mk:@MSITStore:K:\Data.Structures.and.Algorithm.Analysis.in.C.chm::/...
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21
pf22
pf23
pf24
pf25
pf26
pf27
pf28
pf29
pf2a
pf2b
pf2c
pf2d
pf2e
pf2f
pf30
pf31
pf32
pf33
pf34
pf35
pf36
pf37
pf38
pf39
pf3a
pf3b
pf3c
pf3d
pf3e
pf3f
pf40
pf41
pf42
pf43
pf44
pf45
pf46
pf47
pf48
pf49
pf4a
pf4b
pf4c
pf4d
pf4e
pf4f
pf50
pf51
pf52
pf53
pf54
pf55
pf56
pf57
pf58
pf59
pf5a
pf5b
pf5c
pf5d
pf5e
pf5f
pf60
pf61
pf62
pf63
pf64

Partial preview of the text

Download Data Structures in C Notes and more Study notes Data Structures and Algorithms in PDF only on Docsity!

Data Structures and Algorithm Analysis

in C

by Mark Allen Weiss

PREFACE

CHAPTER 1: INTRODUCTION

CHAPTER 2: ALGORITHM ANALYSIS

CHAPTER 3: LISTS, STACKS, AND QUEUES

CHAPTER 4: TREES

CHAPTER 5: HASHING

CHAPTER 6: PRIORITY QUEUES (HEAPS)

CHAPTER 7: SORTING

CHAPTER 8: THE DISJOINT SET ADT

CHAPTER 9: GRAPH ALGORITHMS

CHAPTER 10: ALGORITHM DESIGN TECHNIQUES

CHAPTER 11: AMORTIZED ANALYSIS

Structures, Algorithm Analysis: Table of Contents 页码,1/

PREFACE

Purpose/Goals

This book describes data structures, methods of organizing large amounts of data,

and algorithm analysis, the estimation of the running time of algorithms. As

computers become faster and faster, the need for programs that can handle large

amounts of input becomes more acute. Paradoxically, this requires more careful

attention to efficiency, since inefficiencies in programs become most obvious

when input sizes are large. By analyzing an algorithm before it is actually

coded, students can decide if a particular solution will be feasible. For

example, in this text students look at specific problems and see how careful

implementations can reduce the time constraint for large amounts of data from 16

years to less than a second. Therefore, no algorithm or data structure is

presented without an explanation of its running time. In some cases, minute

details that affect the running time of the implementation are explored.

Once a solution method is determined, a program must still be written. As

computers have become more powerful, the problems they solve have become larger

and more complex, thus requiring development of more intricate programs to solve

the problems. The goal of this text is to teach students good programming and

algorithm analysis skills simultaneously so that they can develop such programs

with the maximum amount of efficiency.

This book is suitable for either an advanced data structures (CS7) course or a

first-year graduate course in algorithm analysis. Students should have some

knowledge of intermediate programming, including such topics as pointers and

recursion, and some background in discrete math.

Approach

I believe it is important for students to learn how to program for themselves,

not how to copy programs from a book. On the other hand, it is virtually

impossible to discuss realistic programming issues without including sample code.

For this reason, the book usually provides about half to three-quarters of an

implementation, and the student is encouraged to supply the rest.

The algorithms in this book are presented in ANSI C, which, despite some flaws,

is arguably the most popular systems programming language. The use of C instead

of Pascal allows the use of dynamically allocated arrays (see for instance

rehashing in Ch. 5). It also produces simplified code in several places, usually

because the and (&&) operation is short-circuited.

Most criticisms of C center on the fact that it is easy to write code that is

barely readable. Some of the more standard tricks, such as the simultaneous

assignment and testing against 0 via

if (x=y)

are generally not used in the text, since the loss of clarity is compensated by

Return to Table of Contents Next Chapter

Structures, Algorithm Analysis: PREFACE 页码,1/

context, a short discussion on complexity theory (including NP-completeness and

undecidability) is provided.

Chapter 10 covers algorithm design by examining common problem-solving

techniques. This chapter is heavily fortified with examples. Pseudocode is used

in these later chapters so that the student's appreciation of an example

algorithm is not obscured by implementation details.

Chapter 11 deals with amortized analysis. Three data structures from Chapters 4

and 6 and the Fibonacci heap, introduced in this chapter, are analyzed.

Chapters 1-9 provide enough material for most one-semester data structures

courses. If time permits, then Chapter 10 can be covered. A graduate course on

algorithm analysis could cover Chapters 7-11. The advanced data structures

analyzed in Chapter 11 can easily be referred to in the earlier chapters. The

discussion of NP-completeness in Chapter 9 is far too brief to be used in such a

course. Garey and Johnson's book on NP-completeness can be used to augment this

text.

Exercises

Exercises, provided at the end of each chapter, match the order in which material

is presented. The last exercises may address the chapter as a whole rather than a

specific section. Difficult exercises are marked with an asterisk, and more

challenging exercises have two asterisks.

A solutions manual containing solutions to almost all the exercises is available

separately from The Benjamin/Cummings Publishing Company.

References

References are placed at the end of each chapter. Generally the references either

are historical, representing the original source of the material, or they

represent extensions and improvements to the results given in the text. Some

references represent solutions to exercises.

Acknowledgments

I would like to thank the many people who helped me in the preparation of this

and previous versions of the book. The professionals at Benjamin/Cummings made my

book a considerably less harrowing experience than I had been led to expect. I'd

like to thank my previous editors, Alan Apt and John Thompson, as well as Carter

Shanklin, who has edited this version, and Carter's assistant, Vivian McDougal,

for answering all my questions and putting up with my delays. Gail Carrigan at

Benjamin/Cummings and Melissa G. Madsen and Laura Snyder at Publication Services

did a wonderful job with production. The C version was handled by Joe Heathward

and his outstanding staff, who were able to meet the production schedule despite

the delays caused by Hurricane Andrew.

I would like to thank the reviewers, who provided valuable comments, many of

Structures, Algorithm Analysis: PREFACE 页码,3/

which have been incorporated into the text. Alphabetically, they are Vicki Allan

(Utah State University), Henry Bauer (University of Wyoming), Alex Biliris

(Boston University), Jan Carroll (University of North Texas), Dan Hirschberg

(University of California, Irvine), Julia Hodges (Mississippi State University),

Bill Kraynek (Florida International University), Rayno D. Niemi (Rochester

Institute of Technology), Robert O. Pettus (University of South Carolina), Robert

Probasco (University of Idaho), Charles Williams (Georgia State University), and

Chris Wilson (University of Oregon). I would particularly like to thank Vicki

Allan, who carefully read every draft and provided very detailed suggestions for

improvement.

At FIU, many people helped with this project. Xinwei Cui and John Tso provided me

with their class notes. I'd like to thank Bill Kraynek, Wes Mackey, Jai Navlakha,

and Wei Sun for using drafts in their courses, and the many students who suffered

through the sketchy early drafts. Maria Fiorenza, Eduardo Gonzalez, Ancin Peter,

Tim Riley, Jefre Riser, and Magaly Sotolongo reported several errors, and Mike

Hall checked through an early draft for programming errors. A special thanks goes

to Yuzheng Ding, who compiled and tested every program in the original book,

including the conversion of pseudocode to Pascal. I'd be remiss to forget Carlos

Ibarra and Steve Luis, who kept the printers and the computer system working and

sent out tapes on a minute's notice.

This book is a product of a love for data structures and algorithms that can be

obtained only from top educators. I'd like to take the time to thank Bob Hopkins,

E. C. Horvath, and Rich Mendez, who taught me at Cooper Union, and Bob Sedgewick,

Ken Steiglitz, and Bob Tarjan from Princeton.

Finally, I'd like to thank all my friends who provided encouragement during the

project. In particular, I'd like to thank Michele Dorchak, Arvin Park, and Tim

Snyder for listening to my stories; Bill Kraynek, Alex Pelin, and Norman Pestaina

for being civil next-door (office) neighbors, even when I wasn't; Lynn and Toby

Berk for shelter during Andrew, and the HTMC for work relief.

Any mistakes in this book are, of course, my own. I would appreciate reports of

any errors you find; my e-mail address is [email protected].

M.A.W.

Miami, Florida

September 1992

Go to Chapter 1 Return to Table of Contents

Structures, Algorithm Analysis: PREFACE 页码,4/

direction. As an example, the puzzle shown in Figure 1.1 contains the words this,

two, fat, and that. The word this begins at row 1, column 1 (1,1) and extends to

(1, 4); two goes from (1, 1) to (3, 1); fat goes from (4, 1) to (2, 3); and that

goes from (4, 4) to (1, 1).

Again, there are at least two straightforward algorithms that solve the problem.

For each word in the word list, we check each ordered triple (row, column,

orientation) for the presence of the word. This amounts to lots of nested for

loops but is basically straightforward.

Alternatively, for each ordered quadruple (row, column, orientation, number of

characters) that doesn't run off an end of the puzzle, we can test whether the

word indicated is in the word list. Again, this amounts to lots of nested for

loops. It is possible to save some time if the maximum number of characters in

any word is known.

It is relatively easy to code up either solution and solve many of the real-life

puzzles commonly published in magazines. These typically have 16 rows, 16

columns, and 40 or so words. Suppose, however, we consider the variation where

only the puzzle board is given and the word list is essentially an English

dictionary. Both of the solutions proposed require considerable time to solve

this problem and therefore are not acceptable. However, it is possible, even with

a large word list, to solve the problem in a matter of seconds.

An important concept is that, in many problems, writing a working program is not

good enough. If the program is to be run on a large data set, then the running

time becomes an issue. Throughout this book we will see how to estimate the

running time of a program for large inputs and, more importantly, how to compare

the running times of two programs without actually coding them. We will see

techniques for drastically improving the speed of a program and for determining

program bottlenecks. These techniques will enable us to find the section of the

code on which to concentrate our optimization efforts.

1 t h i s

2 w a t s

3 o a h g

4 f g d t

Figure 1.1 Sample word puzzle

1.2. Mathematics Review

This section lists some of the basic formulas you need to memorize or be able to

derive and reviews basic proof techniques.

1.2.1. Exponents

x a^ x b^ = x a+b

xa

-- = xa-b

xb

(xa)b = xab

xn + xn = 2xn^ x2n

2 n + 2n = 2n+

1.2.2. Logarithms

In computer science, all logarithms are to base 2 unless specified otherwise.

DEFINITION:xa =b if and only if logx b =a

Several convenient equalities follow from this definition.

THEOREM 1.1.

PROOF:

Letx = logc b, y = logc a, andz = loga b. Then, by the definition of logarithms,cx =b, cy =

a, andaz =b. Combining these three equalities yields (cy)z =cx =b. Therefore,x =yz, which impliesz =x/y, proving the theorem.

THEOREM 1.2.

logab = loga + logb

PROOF:

Letx = loga, y = logb, z = logab. Then, assuming the default base of 2, 2x=a, 2y =b, 2z =

ab. Combining the last three equalities yields 2x 2 y = 2z =ab. Therefore,x +y = z, which proves the theorem.

Some other useful formulas, which can all be derived in a similar manner, follow.

loga/b = loga - logb

and multiply by 2, obtaining

Subtracting these two equations yields

Thus,S = 2.

Another type of common series in analysis is the arithmetic series. Any such series can be

evaluated from the basic formula.

For instance, to find the sum 2 + 5 + 8 +... + (3k - 1), rewrite it as 3(1 + 2+ 3 +... +k) -

(1 + 1 + 1 +... + 1), which is clearly 3k(k + 1)/2 -k. Another way to remember this is to add the first and last terms (total 3k + 1), the second and next to last terms (total 3k + 1), and so on. Since there arek/2 of these pairs, the total sum isk(3k + 1)/2, which is the same answer as before.

The next two formulas pop up now and then but are fairly infrequent.

Whenk = -1, the latter formula is not valid. We then need the following formula, which is used

far more in computer science than in other mathematical disciplines. The numbers,HN, are known

as the harmonic numbers, and the sum is known as a harmonic sum. The error in the following

approximation tends toy 0.57721566, which is known asEuler's constant.

These two formulas are just general algebraic manipulations.

1.2.4. Modular Arithmetic

We say thata is congruent tob modulon, writtena b(modn), ifn dividesa -b.

Intuitively, this means that the remainder is the same when eithera orb is divided byn. Thus,

81 61 1(mod 10). As with equality, ifa b (modn), thena +c b +c(modn)

anda d b d (modn).

There are a lot of theorems that apply to modular arithmetic, some of which require extraordinary proofs in number theory. We will use modular arithmetic sparingly, and the preceding theorems will suffice.

1.2.5. The P Word

The two most common ways of proving statements in data structure analysis are proof by induction and proof by contradiction (and occasionally a proof by intimidation, by professors only). The best way of proving that a theorem is false is by exhibiting a counterexample.

Proof by Induction

A proof by induction has two standard parts. The first step is proving abase case, that is,

establishing that a theorem is true for some small (usually degenerate) value(s); this step is almost always trivial. Next, aninductive hypothesis is assumed. Generally this means that the theorem is assumed to be true for all cases up to some limitk. Using this assumption, the theorem is then shown to be true for the next value, which is typicallyk + 1. This proves the theorem (as long ask is finite).

As an example, we prove that the Fibonacci numbers,F 0 = 1,F 1 = 1,F 2 = 2,F 3 = 3,F 4 = 5,...

,Fi =Fi-1 +Fi-2, satisfyFi < (5/3)i, fori 1. (Some definitions haveF0 = (^0) , which

shifts the series.) To do this, we first verify that the theorem is true for the trivial cases.

It is easy to verify thatF 1 = 1 < 5/3 andF 2 = 2 <25/9; this proves the basis. We assume that

the theorem is true fori = 1, 2,... ,k; this is the inductive hypothesis. To prove the

theorem, we need to show thatFk+1 < (5/3)k+1. We have

Fk + 1=Fk +Fk-

by the definition, and we can use the inductive hypothesis on the right-hand side, obtaining

Fk+1 < (5/3)k + (5/3)k-

< (3/5)(5/3)k+1 + (3/5)^2 (5/3)k+

The statementFk k^2 is false. The easiest way to prove this is to computeF 11 = 144 > 11^2.

Proof by Contradiction

Proof by contradiction proceeds by assuming that the theorem is false and showing that this

assumption implies that some known property is false, and hence the original assumption was erroneous. A classic example is the proof that there is an infinite number of primes. To prove this, we assume that the theorem is false, so that there is some largest primepk. Letp 1 ,p 2 ,.

.. ,pk be all the primes in order and consider

N =p 1 p 2 p 3.. .pk + 1

Clearly,N is larger thanpk, so by assumptionN is not prime. However, none ofp 1 ,p 2 ,... ,

pk divideN exactly, because there will always be a remainder of 1. This is a contradiction,

because every number is either prime or a product of primes. Hence, the original assumption, that pk is the largest prime, is false, which implies that the theorem is true.

int

f( int x )

{

/1/ if ( x = 0 )

/2/ return 0;

else

/3/ return( 2f(x-1) + xx );

}

Figure 1.2 A recursive function

1.3. A Brief Introduction to Recursion

Most mathematical functions that we are familiar with are described by a simple formula. For

instance, we can convert temperatures from Fahrenheit to Celsius by applying the formula

C = 5(F - 32)/

Given this formula, it is trivial to write a C function; with declarations and braces removed, the one-line formula translates to one line of C.

Mathematical functions are sometimes defined in a less standard form. As an example, we can define a functionf, valid on nonnegative integers, that satisfiesf(0) = 0 andf(x) = 2f(x - 1)

+x^2. From this definition we see thatf(1) = 1,f(2) = 6,f(3) = 21, andf(4) = 58. A function that is defined in terms of itself is calledrecursive. C allows functions to be recursive.* It is important to remember that what C provides is merely an attempt to follow the recursive spirit. Not all mathematically recursive functions are efficiently (or correctly) implemented by C's simulation of recursion. The idea is that the recursive functionf ought to be expressible in

only a few lines, just like a non-recursive function. Figure 1.2 shows the recursive implementation off.

*Using recursion for numerical calculations is usually a bad idea. We have done so to illustrate the basic points.

Lines 1 and 2 handle what is known as thebase case, that is, the value for which the function is

directly known without resorting to recursion. Just as declaringf(x) = 2f(x - 1) +x^2 is meaningless, mathematically, without including the fact thatf (0) = 0, the recursive C function doesn't make sense without a base case. Line 3 makes the recursive call.

There are several important and possibly confusing points about recursion. A common question is: Isn't this just circular logic? The answer is that although we are defining a function in terms of itself, we are not defining a particular instance of the function in terms of itself. In other words, evaluatingf(5) by computingf(5) would be circular. Evaluatingf(5) by computingf(4) is not circular--unless, of coursef(4) is evaluated by eventually computingf(5). The two most important issues are probably thehow andwhy questions. In Chapter 3, thehow andwhy issues are formally resolved. We will give an incomplete description here.

It turns out that recursive calls are handled no differently from any others. Iff is called with the value of 4, then line 3 requires the computation of 2 *f(3) + 4 * 4. Thus, a call is made to

computef(3). This requires the computation of 2 *f(2) + 3 * 3. Therefore, another call is made

to computef(2). This means that 2 *f(1) + 2 * 2 must be evaluated. To do so,f(1) is computed

as 2 *f(0) + 1 * 1. Now,f(0) must be evaluated. Since this is a base case, we know a priori

thatf(0) = 0. This enables the completion of the calculation forf(1), which is now seen to be

  1. Thenf(2),f(3), and finallyf(4) can be determined. All the bookkeeping needed to keep track of pending function calls (those started but waiting for a recursive call to complete), along with their variables, is done by the computer automatically. An important point, however, is that recursive calls will keep on being made until a base case is reached. For instance, an attempt to evaluatef(-1) will result in calls tof(-2),f(-3), and so on. Since this will never get to a base case, the program won't be able to compute the answer (which is undefined anyway). Occasionally, a much more subtle error is made, which is exhibited in Figure 1.3. The error in the program in Figure 1.3 is thatbad(1) is defined, by line 3, to bebad(1). Obviously, this doesn't give any clue as to whatbad(1) actually is. The computer will thus repeatedly make calls tobad(1) in an attempt to resolve its values. Eventually, its bookkeeping system will run out of space, and the program will crash. Generally, we would say that this function doesn't work for one special case but is correct otherwise. This isn't true here, sincebad(2) callsbad(1). Thus, bad(2) cannot be evaluated either. Furthermore,bad(3),bad(4), andbad(5) all make calls tobad (2). Sincebad(2) is unevaluable, none of these values are either. In fact, this program doesn't work for any value ofn, except 0. With recursive programs, there is no such thing as a "special case."

These considerations lead to the first two fundamental rules of recursion:

1.Base cases. You must always have some base cases, which can be solved without recursion.

2.Making progress. For the cases that are to be solved recursively, the recursive call must always be to a case that makes progress toward a base case.

Throughout this book, we will use recursion to solve problems. As an example of a nonmathematical use, consider a large dictionary. Words in dictionaries are defined in terms of other words. When we look up a word, we might not always understand the definition, so we might have to look up words in the definition. Likewise, we might not understand some of those, so we might have to continue this search for a while. As the dictionary is finite, eventually either we will come to a point where we understand all of the words in some definition (and thus understand that definition and retrace our path through the other definitions), or we will find that the definitions are circular and we are stuck, or that some word we need to understand a definition is not in the dictionary.

The recursive number-printing algorithm is correct for n 0.

PROOF:

First, ifn has one digit, then the program is trivially correct, since it merely makes a call to print_digit. Assume then thatprint_out works for all numbers ofk or fewer digits. A number ofk

  • 1 digits is expressed by its firstk digits followed by its least significant digit. But the

number formed by the firstk digits is exactly n /10 , which, by the indicated hypothesis is correctly printed, and the last digit isn mod10, so the program prints out any (k + 1)-digit number correctly. Thus, by induction, all numbers are correctly printed.

void

print_out( unsigned int n ) /* print nonnegative n */

{

if( n<10 )

print_digit( n );

else

{

print_out( n/10 );

print_digit( n%10 );

}

}

Figure 1.4 Recursive routine to print an integer

This proof probably seems a little strange in that it is virtually identical to the algorithm description. It illustrates that in designing a recursive program, all smaller instances of the same problem (which are on the path to a base case) may beassumed to work correctly. The recursive program needs only to combine solutions to smaller problems, which are "magically" obtained by recursion, into a solution for the current problem. The mathematical justification for this is proof by induction. This gives the third rule of recursion:

3.Design rule. Assume that all the recursive calls work.

This rule is important because it means that when designing recursive programs, you generally don't need to know the details of the bookkeeping arrangements, and you don't have to try to trace through the myriad of recursive calls. Frequently, it is extremely difficult to track down the actual sequence of recursive calls. Of course, in many cases this is an indication of a good use of recursion, since the computer is being allowed to work out the complicated details.

The main problem with recursion is the hidden bookkeeping costs. Although these costs are almost always justifiable, because recursive programs not only simplify the algorithm design but also tend to give cleaner code, recursion should never be used as a substitute for a simplefor loop. We'll discuss the overhead involved in recursion in more detail in Section 3.3.

When writing recursive routines, it is crucial to keep in mind the four basic rules of recursion:

1.Base cases. You must always have some base cases, which can be solved without recursion.

2.Making progress. For the cases that are to be solved recursively, the recursive call must always be to a case that makes progress toward a base case.

3.Design rule. Assume that all the recursive calls work.

4.Compound interest rule. Never duplicate work by solving the same instance of a problem in separate recursive calls.

The fourth rule, which will be justified (along with its nickname) in later sections, is the

reason that it is generally a bad idea to use recursion to evaluate simple mathematical functions, such as the Fibonacci numbers. As long as you keep these rules in mind, recursive programming should be straightforward.

Summary

This chapter sets the stage for the rest of the book. The time taken by an algorithm confronted with large amounts of input will be an important criterion for deciding if it is a good algorithm. (Of course, correctness is most important.) Speed is relative. What is fast for one problem on one machine might be slow for another problem or a different machine. We will begin to address these issues in the next chapter and will use the mathematics discussed here to establish a formal model.

Exercises

1.1 Write a program to solve the selection problem. Letk =n/2. Draw a table showing the running

time of your program for various values ofn.

1.2 Write a program to solve the word puzzle problem.

1.3 Write a procedure to output an arbitrary real number (which might be negative) using only

print_digit for I/O.

1.4 C allows statements of the form

#includefilename

which readsfilename and inserts its contents in place of theinclude statement.Include statements may be nested; in other words, the filefilename may itself contain aninclude statement, but, obviously, a file can't include itself in any chain. Write a program that reads in a file and outputs the file as modified by theinclude statements.

1.5 Prove the following formulas:

a. logx 0

b. log(ab) =b loga

1.6 Evaluate the following sums:

General programming style is discussed in several books. Some of the classics are [5], [7], and [9].

  1. M. O. Albertson and J. P. Hutchinson,Discrete Mathematics with Algorithms, John Wiley & Sons, New York, 1988.
  2. Z. Bavel,Math Companion for Computer Science, Reston Publishing Company, Reston, Va., 1982.
  3. R. A. Brualdi,Introductory Combinatorics, North-Holland, New York, 1977.
  4. W. H. Burge,Recursive Programming Techniques, Addison-Wesley, Reading, Mass., 1975.
  5. E. W. Dijkstra,A Discipline of Programming, Prentice Hall, Englewood Cliffs, N.J., 1976.
  6. R. L. Graham, D. E. Knuth, and O. Patashnik,Concrete Mathematics, Addison-Wesley, Reading,

Mass., 1989.

  1. D. Gries,The Science of Programming, Springer-Verlag, New York, 1981.
  2. P. Helman and R. Veroff,Walls and Mirrors: Intermediate Problem Solving and Data Structures,

2d ed., Benjamin Cummings Publishing, Menlo Park, Calif., 1988.

  1. B. W. Kernighan and P. J. Plauger,The Elements of Programming Style, 2d ed., McGraw- Hill,

New York, 1978.

  1. B. W. Kernighan and D. M. Ritchie,The C Programming Language, 2d ed., Prentice Hall,

Englewood Cliffs, N.J., 1988.

  1. D. E. Knuth,The Art of Computer Programming, Vol. 1: Fundamental Algorithms, 2d ed.,

Addison-Wesley, Reading, Mass., 1973.

  1. E. Roberts,Thinking Recursively, John Wiley & Sons, New York, 1986.
  2. F. S. Roberts,Applied Combinatorics, Prentice Hall, Englewood Cliffs, N.J., 1984.
  3. A. Tucker,Applied Combinatorics, 2d ed., John Wiley & Sons, New York, 1984.

Go to Chapter 2 Return to Table of Contents

CHAPTER 2:

ALGORITHM ANALYSIS

An algorithm is a clearly specified set of simple instructions to be followed to

solve a problem. Once an algorithm is given for a problem and decided (somehow)

to be correct, an important step is to determine how much in the way of

resources, such as time or space, the algorithm will require. An algorithm that

solves a problem but requires a year is hardly of any use. Likewise, an algorithm

that requires a gigabyte of main memory is not (currently) useful.

In this chapter, we shall discuss

How to estimate the time required for a program.

How to reduce the running time of a program from days or years to fractions

of a second.

The results of careless use of recursion.

Very efficient algorithms to raise a number to a power and to compute the

greatest common divisor of two numbers.

2.1. Mathematical Background

The analysis required to estimate the resource use of an algorithm is generally a

theoretical issue, and therefore a formal framework is required. We begin with

some mathematical definitions.

Throughout the book we will use the following four definitions:

DEFINITION: T(n) = O(f(n)) if there are constants c and n 0 such that T(n) cf

(n) when n n 0.

DEFINITION: T(n) = (g(n)) if there are constants c and n 0 such that T(n)

cg(n) when n n 0.

DEFINITION: T(n) = (h(n)) if and only if T(n) = O(h(n)) and T(n) = (h(n)).

DEFINITION: T(n) = o(p(n)) if T(n) = O(p(n)) and T(n) (p(n)).

Previous Chapter Return to Table of Contents Next Chapter

Structures, Algorithm Analysis: CHAPTER 2: ALGORITHM ANALYSIS 页码,1/