• No se han encontrado resultados

3. Plan de motivación a la lectura

3.3 Desarrollo del plan

2.14

Another array representation of lists is to insert as in Section 2.2, but when deleting, simply replace the deleted element by a special value "deleted," which we assume does not appear on lists otherwise. Rewrite the list operations to implement this strategy. What are the advantages and disadvantages of the approach compared with our original array representation of lists?

2.15

Suppose we wish to use an extra bit in queue records to indicate whether a queue is empty. Modify the declarations and operations for a circular queue to accommodate this feature. Would you expect the change to be worthwhile?

2.16

A dequeue (double-ended queue) is a list from which elements can be inserted or deleted at either end. Develop array, pointer, and cursor implementations for a dequeue.

2.17

Define an ADT to support the operations ENQUEUE, DEQUEUE, and ONQUEUE. ONQUEUE(x) is a function returning true or false

depending on whether x is or is not on the queue.

2.18

How would one implement a queue if the elements that are to be placed on the queue are arbitrary length strings? How long does it take to enqueue a string?

2.19

Another possible linked-list implementation of queues is to use no header cell, and let front point directly to the first cell. If the queue is empty, let

front = rear = nil. Implement the queue operations for this

representation. How does this implementation compare with the list implementation given for queues in Section 2.4 in terms of speed, space utilization, and conciseness of the code?

2.20

A variant of the circular queue records the position of the front element and the length of the queue.

a. Is it necessary in this implementation to limit the length of a queue to maxlength - 1?

b. Write the five queue operations for this implementation. c. Compare this implementation with the circular queue

implementation of Section 2.4.

2.21

It is possible to keep two stacks in a single array, if one grows from

position 1 of the array, and the other grows from the last position. Write a procedure PUSH(x, S) that pushes element x onto stack S, where S is one or the other of these two stacks. Include all necessary error checks in your procedure.

2.22

We can store k stacks in a single array if we use the data structure suggested in Fig. 2.27, for the case k = 3. We push and pop from each stack as suggested in connection with Fig. 2.17 in Section 2.3. However, if pushing onto stack i causes TOP(i) to equal BOTTOM(i-1), we first move all the stacks so that there is an appropriate size gap between each adjacent pair of stacks. For example, we might make the gaps above all stacks equal, or we might make the gap above stack i proportional to the current size of stack i (on the theory that larger stacks are likely to grow sooner, and we want to postpone as long as possible the next

reorganization).

a. On the assumption that there is a procedure reorganize to call when stacks collide, write code for the five stack operations. b. On the assumption that there is a procedure makenewtops that

computes newtop[i], the "appropriate" position for the top of stack

i, for 1 i k, write the procedure reorganize. Hint. Note that stack i could move up or down, and it is necessary to move stack i before stack j if the new position of stack j overlaps the old

position of stack i. Consider stacks 1, 2 , . . . , k in order, but keep a stack of "goals," each goal being to move a particular stack. If on considering stack i, we can move it safely, do so, and then reconsider the stack whose number is on top of the goal stack. If we cannot safely move stack i, push i onto the goal stack.

c. What is an appropriate implementation for the goal stack in (b)? Do we really need to keep it as a list of integers, or will a more succinct representation do?

d. Implement makenewtops in such a way that space above each stack is proportional to the current size of that stack.

e. What modifications of Fig. 2.27 are needed to make the implementation work for queues? For general lists?

2.23

Modify the implementations of POP and ENQUEUE in Sections 2.3 and 2.4 to return the element removed from the stack or queue. What

modifications must be made if the element type is not a type that can be returned by a function?

2.24

Use a stack to eliminate recursion from the following procedures. a. function comb ( n, m: integer ): integer;

{ computes ( ) assuming 0 ≤ m n and n ≥ 1 } begin if (n = 1) or (m = 0) or (m = n) then return (1) else

return (comb(n-1, m) + comb(n-1, m-1)) end; { comb }

Fig. 2.27. Many stacks in one array.

b. procedure reverse ( var L: LIST ); { reverse list L }

var

x: elementtype; begin

if not EMPTY(L) then begin x := RETRIEVE(FIRST(L), L); DELETE(FIRST(L), L);

reverse(L);

INSERT(x, END(L), L) end

end; { reverse }

*2.25 Can we eliminate the tail recursion from the programs in Exercise 2.24?

If so, do it.

Bibliographic Notes

Knuth [1968] contains additional information on the implementation of lists, stacks, and queues. A number of programming languages, such as LISP and SNOBOL, support lists and strings in a convenient manner. See Sammet [1969], Nicholls [1975], Pratt [1975], or Wexelblat [1981] for a history and description of many of these languages.

† Strictly speaking, the type is "LIST of elementtype." However, the

implementations of lists we propose do not depend on what elementtype is; indeed, it is that independence that justifies the importance we place on the list concept. We shall use "LIST" rather than "LIST of elementtype," and similarly treat other ADT's that depend on the types of elements.

† In this case, if we eliminate records that are "the same" we might wish to check that the names and addresses are also equal; if the account numbers are equal but the other fields are not, two people may have inadvertently gotten the same account number. More likely, however, is that the same subscriber appears on the list more than once with distinct account numbers and slightly different names and/or

addresses. In such cases, it is difficult to eliminate all duplicates.

† Even though L is not modified, we pass L by reference because frequently it will be a big structure and we don't want to waste time copying it.

† Making the header a complete cell simplifies the implementation of list operation in Pascal. We can use pointers for headers if we are willing to implement our

operations so they do insertions and deletions at the beginning of a list in a special way. See the discussion under cursor-based implementation of lists in this section. † Of course, there are many situations where we would like p to continue to

represent the position of c.

† Incidentally, it is common practice to make the header of a doubly linked list be a cell that effectively "completes the circle." That is, the header's previous field points to the last cell and next points to the first cell. In this manner, we need not check for

nil pointers in Fig. 2.14.

† Note that "consecutive" must be taken in circular sense. That is, a queue of length four could occupy the last two and first two positions of the array, for example. † For example, firstvalue = 'A' and lastvalue = 'Z' if domaintype is 'A'..'Z'.

† In the "real" knapsack problem, we are given utility values as well as weights and are asked to maximize the utility of the items carried, subject to a weight constraint. ‡ Alternatively, x could be passed by reference if y is x.

Trees

A tree imposes a hierarchical structure on a collection of items. Familiar examples of trees are genealogies and organization charts. Trees are used to help analyze

electrical circuits and to represent the structure of mathematical formulas. Trees also arise naturally in many different areas of computer science. For example, trees are used to organize information in database systems and to represent the syntactic structure of source programs in compilers. Chapter 5 describes applications of trees in the representation of data. Throughout this book, we shall use many different variants of trees. In this chapter we introduce the basic definitions and present some of the more common tree operations. We then describe some of the more frequently used data structures for trees that can be used to support these operations efficiently.

Documento similar