0% found this document useful (0 votes)
337 views42 pages

Stack

Data Structures Stack Chap

Uploaded by

Abdul Maalik
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF or read online on Scribd
0% found this document useful (0 votes)
337 views42 pages

Stack

Data Structures Stack Chap

Uploaded by

Abdul Maalik
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF or read online on Scribd
You are on page 1/ 42
6.36 Data Structures 2. [Find new value of REAR.) If FRONT := NULL, then: [Queue Set FRONT := I and REAR := 1 Else if REAR = N, then: Set REAR Else; Set REAR := REAR + 1. [End of If stracture.} 3. Set QUEUE[REAR] := 4, Return. (TEM. [This inserts new element.] Procedure 6.14: QDELETE(QUEUE, N, FRONT, REAR, ITEM) This procedure deletes an element from a queue and ITEM. 1. [Queue already empty} If FRONT := NULL, then: Write: UNDERFLOW, and Return. 2. Set ITEM := QUEUE[FRONT]. 3. [Find new value of FRONT.] If FRONT = REAR, then: [Queue has only one element to start.] signs it to the variable Set FRONT := NULL and REAR := NULL, Else if FRONT = N, then: Set FRONT := Else: Set FRONT := FRONT + 1 {End of If structure.} 4. Return. 6.11 LINKED REPRESENTATION OF QUEUES In this section we discuss the linked representation of a queue. A linked queue is a queue implemented as a linked list with two pointer variables FRONT and REAR pointing to the nodes which is in the FRONT and REAR of the queue. The INFO ficlds of the list hold the elements of the queue and the LINK fields hold pointers to the neighboring elements in the queue. Fig. 6.22 illustrates the linked representation of the queue shown in Fig. 6.16(a). aes | | —{ cco | | —+{ poo |x Front Rear Fig, 6.22 Stacks, Queues, Recursic START AAAI I | | Fig. 6.27 INFO PRN _LINK 1 BBB 2 [ 6 stant [5 Le 7 Ln —— 3 | 0DD 4 4 AVAIL | 2 4 | GEE 4 8 1 es [aaa 1] { 4 | 6 | ccc 2 3 7 10 8 | GGG 5 0 9 | FFF 4 3 10 1" " 12 12 0 | Fig. 6.28 The main property of the one-way list representation of a priority queue is that the element in the queue that should be processed first always appears at the beginning | of the one-way list. Accordingly, it is a very simple matter to delete and process an ! element from our priority queue, The outline of the algorithm foltows. Algorithm 6.17: This algorithm deletes and processes the first element in a priority queue whieh appears in memory as a one-way list. Set ITEM := INFO[START].-[This saves the data in; the first node] Delete first node from the list. Process ITEM. Exit Beyer 6.6 Data Structures Minimizing Overflow There is an essential difference between underflow and overflow in dealing with stacks. Underflow depends exclusively upon the given algorithm and the given input data, and hence there is no direct control by the programmer. Overflow, on the other hand, depends upon the arbitrary choice of the programmer for the amount of memory space reserved for each stack, and this choice does influence the number of times overflow may occur. Generally speaking, the number of elements in a stack fluctuates as elements are added to or removed from a stack. Accordingly, the particular choice of the amount of memory for a given stack involves a time-space tradeoff. Specifically, initially reserving a great deal of space for each stack will decrease the number of times overflow may occur; however, this may be an expensive use of the space if most of the space is seldom used. On the other hand, reserving a small amount of space for each stack may increase the number of times overflow occurs: and the time required for resolving an overflow, such as by adding space to the stack. may be more expensive than the space saved. Various techniques have been developed which modify the array representation of stacks so that the amount of space reserved for more than one stack may be more efficiently used, Most of these techniques lie beyond the scope of this text. We do illustrate ane such technique in the following example. Example 6.3 Suppose a given algorithm requires two stacks, A and B. One can define an array STACKA with 1, elements for stack A and an array STACKS with n, elements for stack B. Overflow will occur when either stack A contains more than nm, elements or stack B contains more than n, elements. Suppose instead that we define a single array STACK with a = m, +n, elements for stacks A and B together. As pictured in Fig. 6.6, we define STACK[1] as the bottom of stack A and let A “grow” to the right, and we define STACK{n] as the bottom of stack B and let B “grow” to the left. In this case, overflow will occur only when A and B together have more than n =n, +n, elements. This technique will usually decrease the number of times overflow occurs even though we have not increased the total amount of space reserved for the two stacks. In using this data structure, the operations of PUSH and POP will need to be modified. 12) 8 4 n-9 9-2 n-1 oo CI EF SuackA Stack 8 Fig, 6.6 Stacks, Queues, Recursion 6.33 | ‘AAA BB } occ DD j | (a) | Bae }—-[ ec }—-[b00 i i ©) | } | 588 (eee |—-| po) EGE FFF | | © | ee. 000 cee |} —-[ FFF @) | Fig. 6.19 i Representation of Queues Queues may be represented in the computer in various ways, usually by means at one-way lists or linear arrays. Unless otherwise stated or implied, each of our queues will be maintained by a linear array QUEUE and two pointer variables: FRONT, containing the location of the front element of the queue; and REAR, containing .the location of the rear element of the queue. The condition FRONT = NULL will indicate that the queue is empty. Figure 6.20 shows the way the array in Fig. 6.19 will be stared in memory using an array QUEUE with N elements. Figure 6.20 also indicates the way elements will be deleted from the queue and the way new elements will be added to the queue. Observe that whenever an clement is deleted from the queue, the value of FRONT is increased by 1; this can be implemented by the assignment FRONT := FRONT + 1 Similarly, whenever an element is added to the queue, the value of REAR is increased by 1: this can be implemented by the assignment REAR := REAR + 1 This means that after N insertions, the rear element of the queue will occupy QUEVE[N] or, in other words; eventually the queue will occupy the last part of the array. This occurs even though the queue itself may not contain many elements. Suppose we want to insert an element [TEM into a queue at the time the queue does occupy the last part of the array, i.e, when REAR = N, One way (0 do this is to simply move the entire queue to the beginning of the array, changing FRONT and REAR accordingly, and then inserting ITEM as above, This procedure may be very expensive. The procedure we adopt is to assume that the array QUEUE is circular, that is, that QUEUE[!] comes after QUEVE[N] in the array. With this assumption, we insert ITEM into the queue by assigning ITEM to QUEUE[I]. Specifically, instead of increasing REAR to N + 1, we reset REAR = | and then assign QUEUE/REAR] := ITEM 68 Data Structures ‘The array representation of stack calls for the maintenance of a variable MAXSTK which gives the maximum number of elements that can be held by the stack. Also, it calls for the checking of OVERFLOW in the case of push operation (TOP=MAXSTK) and UNDERFLOW in the case of pop operation (TOP=0). In contrast, the linked representation of stacks is free of these requirements. There is no limitation on the capacity of the linked stack and hence it can support as many push operations (insertion of nodes) as the free-storage list ( the AVAIL list) can support. This dispenses With the need to maintain the MAXSTK variable and consequently on the checking of OVERFLOW of the linked stack during a push operation. Procedure 6.3: PUSH_LINKSTACK(INFO, LINK, TOP, AVAIL, ITEM) This procedure pushes an ITEM into a linked stack 1, [Available space?] If AVAIL = NULL, then Write OVERFLOW and Exit 2, [Remove first node from AVAIL list] Set NEW := AVAIL and AVAIL := LINK[AVAIL]. 3. Set INFO[NEW] := ITEM [ Copies ITEM into new node} 4, Set LINK[NEW] := TOP [New node points to the original top node in the stack] 5. Set TOP = NEW [Reset TOP to point to the new node at the top of the stack] 6. Exit. Procedure 6.4: POP_LLINKSTACK(INFO, LINK, TOP, AVAIL, ITEM) This procedure deletes the top element of a linked stack and assigns it to the variable [TEM 1. [Stack ha IF TO! an item to be removed?} NULL then Write: UNDERFLOW and Exit. 2. Set ITEM := INFO[TOP] [Copies the top clement of stack into ITEM } 3. Set TEMP := TOP and TOP = LINKITOP] [Remember the old value of the TOP pointer in TEMP and reset TOP to point to the next element in the stack } 4. [Return deleted node to the AVAIL list] Set LINK[TEMP] = AVAIL and AVAIL = TEMP. 5. Exit. However, the condition TOP = NULL may be retained in the pop procedure to prevent deletion from an empty linked stack and the condition AVAIL = NULL to check for available space in the free-storage list. Example 6.4 Consider the linked stack shown in Fig. 6.7, the snapshots of the stack structure on execution of the following operations are shown in Fig. 6.10: Chapter Six Stacks, Queues, Recursion 6.1 INTRODUCTION The linear lists and linear arrays discussed in the previous chapters allowed one to insert and delete elements at any place in the list—at the beginning, at the end, or in the middle, There are certain frequent situations in computer science when one wants to restrict insertions and deletions so that they can take place only at the beginning or the end of the list, not in the middle. Two of the data structures that are useful in such situations are stacks and queues. A stack is a linear structure in which items may be added or removed only at one end. Figure 6.1 pictures three everyday examples of such a structure: a stack of dishes, a stack of pennics and a stack of folded towels. Observe that an item may be added or removed only from the top af any of the stacks. This means, in particular, that the last item to be added to a stack is the first item to be removed. Accordingly, stacks are also called last-in first-out (LIFO) lists. Other names used for stacks are “piles” and “push-down lists.” Although the stack may seem to be a very restricted type of data structure, it has many important applications in computer science A queue is a linear list in which items may be added only at one end and items may be removed only at the other end, The name “queue” likely comes from the everyday use of the term. Consider a queue of people waiting at a bus stop, as pictured in Fig, 6.2. Each new person who comes takes his or her place at the end of the line, and when the bus comes, the people at the front of the line board first. Clearly, the first person in the line is the first person to leave. Thus queues are also called first-in first-out (FIFO) lists. Another example of a queue is a batch of jobs waiting to be processed, assuming no job has higher priority than the others. The notion of recursion is fundamental in computer science. This topic is introduced in this chapter because one way of simulating recursion is by means of a stack structure. Stacks, Queues, Recursion 6.4 LINKED REPRESENTATION OF STACKS In this section we discuss the representation of stacks using a one-way list or singly linked list. The advantages of linked lists over arrays have already been underlined. It is in this perspective that one appreciates the use of a linked list for stacks in comparison to that of arrays. The linked representation of a stack, commonly termed linked stack is a stack that is implemented using a singly linked list. The INFO fields of the nodes hold the clements of the stack and the LINK fields hold pointers to the neighboring element in the stack. The START pointer of the linked list behaves as the TOP pointer variable of the stack and the null pointer of the last node in the list signals the bottom of stack. Figure 6.7 illustrates the linked representation of the stack STACK shown in Fig. 6.5. Top (START) “Lim fw [| a INFO LINK Top of stack Bottom of stack Fig, 6.7 A push operation into STACK is accomplished by inserting a node into the front or start of the list and a pop operation is undertaken by deleting the node pointed to by the START pointer. Figure 6.8 and Fig. 6.9 illustrate the push and pop operation on the linked stack STACK shown in Fig. 6.7. Push’ WWW' inte STACK STACK betoro Push oporation: Top XXX yy | {zzz ]= ‘STACK aftor Push operation “Top www Xxx [yy -[ azz [x Fig. 6.8 Pop from STACK STACK Betore pop operation: Yop me Le} ae STACK af |—-[ zz |= Stacks, Queues, Recursion 65) Procedure 6.1: PUSH(STACK, TOP, MAXSTK, ITEM) This procedure pushes an ITEM onto a stack. 1, [Stack already filled?) If TOP = MAXSTK, then: Print: OVERFLOW, and Return. 2. Set TOP := TOP + |. [Increases TOP by 1.] 3. Set STACK[TOP] := ITEM. [Inserts ITEM in new TOP position.) 4. Return. Procedure 6.2: POP(STACK, TOP, ITEM) This procedure deletes the top element of STACK and assigns it to the variable ITEM, 1, [Stack has an item to be removed?} If TOP = 0, then: Print: UNDERFLOW, and Return. 2. Set ITEM := STACK[TOP]. [Assigns TOP clement to ITEM.} 3. Set TOP := TOP = }. [Decreases TOP by 1.] 4. Return. Frequently, TOP and MAXSTK are global variables; hence the procedures may be called using only PUSH(STACK, ITEM) and POP(STACK, ITEM) respectively. We note that the value of TOP is changed before the insertion in PUSH but the value of TOP is changed after the deletion in POP. Example 6.2 (a) Consider the stack in Fig, 6.5. We simulate the operation PUSH(STACK, WWW): 4. Since TOP = 3, contral is transferred to Step 2. | 2, TOP =3+1=4 | 3. STACK{TOP] = STACK(4] = WWW. 4. Return. Note that WWW is now the top element in the stack. | (b) Consider again the stack in Fig. 6.5. This time we simulate the operation POP(STACK, ITEM): 1. Since TOP = 3, control is transferred to Step 2. | 2. ITEM = ZZZ. 3. TOP =3-1=2. | 4. Return. Observe that STACK[TOP] = STACK[2] = YYY is now the top element in the stack. Stacks, Queues, Recursion 6.37 In the case of insertion into a linked queue, a node borrowed from the AVAIL list and carrying the item to be inserted is added as the last node of the linked list representing the queue. The REAR pointer is updated to point to the last node just added to the list. In the case of deletion, the first node of the list pointed to by FRONT is deleted and the FRONT pointer is updated to point to the next node in the list. Fig. 6.23 and Fig. 6.24 illustrate the insert and delete operations on the queue shown in Fig. 6.22. "EEE'into queve O: \y AAR Bee eee {ooo [x] —+{ eee |x | t { I I Front Fear pear \ Ue Fig. 6.23 -{ cco poo | eee [x Rear Fig. 6.24 The array representation of a queue suffers from the drawback of limited queue capacity. This in turn calls for the checking of OVERFLOW condition every time an insertion is made into the queue. Also, duc to the inherent disadvantage of the array data structure in which data movement is expensive, the maintenance of the queue calls for its circular implementation. In contrast, the linked queue is not limited in capacity and therefore as many nodes as the AVAIL list can provide, may be inserted into the queue. This dispenses with the need to check for the OVERFLOW condition during insertion. Also, unlike the array representation, the linked queue functions as a linear queue and there is no need to view it as ‘circular’ for efficient management of space. Procedure 6.15: LINKQ_INSERT(INFO,LINK, FRONT. REAR,AVAILTEM). his procedure inserts an [TEM into a linked queue 1. [Available space?] If AVAIL = NULL, then Write OVERFLOW and Exit 2, [Remove first node from AVAIL list] Set NEW := AVAIL and AVAIL := LINK[AVAIL] 3. Set INFO[NEW] := ITEM and LINK|NEW|=NULL ‘opies ITEM into new node] 6.28 Data Structures (b) Restore the top values of the stacks, That is, set the parameters and local variables equal to the top values on the stacks, and set ADD equal to the top value on the stack STADD. (e) Go to Step ADD. Observe that the translation of “Step K. Call P” does depend on the value of K, but that the translation of “Step J. Return” does not depend on the value of J. Accordingly, one need translate only one Return statement, for example, by using Step L. Return. as above and then replace every other Return statement by Go to Step L. This will simplify the translation of the procedure Towers of Hanoi, Revisited Consider again the Towers of Hanoi problem, Procedure 6.11 is a recursive solution to the problem for n disks. We translate the procedure into. a nonrecursive solution. In order to keep the steps analogous, we label the beginning statement TOP := NULL as Step 0, Also. only the Retum statement in Step 5 will be translated, as in (3) on the preceding page, Procedure 6.12: TOWER(N, BEG, AUX, END) This is a nonrecursive solution to the Towers of Hanoi, problem for iv disks Jhieh is obtained by translating the recursive solution TN, STBEG, TAUX, STEND and STADD will correspond, respe: to the variables N, BEG, AUX, END and ADD. 0. Set TOP := NULL. 1. If N =1, then: (a) Write: BEG > END. (b) Go to Step 5. [End of If structure.] 2, [Translation of “Call TOWER(N - 1, BEG, END, AUX).”] (a) (Push current values and new return address onto stacks.] (i) Set TOP := TOP + 1. Gi) Set STN[TOP] := N, STBEG|TOP] := BEG, STAUX{TOP] := AUX, STEND[TOP] := END, STADD[TOP] (b) [Reset parameters,] Get N:= N= 1, BEG := BEG, AUX := END, END := AUX. (©) Go to Step 1. 3. Write: BEG — END. 4, [Translation of “Call TOWER(N — 1, AUX, BEG, END) (a) [Push current values and new return address onto stacks.] 16 | Data Structures In order to apply the reduction step again, the algorithm removes the top values, 6 and 12, from the stacks, leaving | LOWER: 1 UPPEI and then applies the reduction step to the corresponding sublist A[6], A[7], «.+, Al12]. The reduction step changes this list as in Fig. 6.9. Observe that the second | sublist has only one element. Accordingly, the algorithm pushes only the boundary values 6 and 10 of the first sublist onto the stacks to yield | LOWER: 1,6 UPPER: 4, 10 | And so on. The algorithm ends when the stacks do not contain any sublist to be processed by the reduction step. | | Ale), AT), ANB]. ALS. AUTO), ALTA}, AL12}. | 77, 60, 9,88, 66, 77,60, $5, | 66, 77, (60. 55, 99 | | 6, 77, 60, 8,5, 99 | First sublist Second sublist | Fig. 6.13 The formal statement of our quicksort algorithm follows (on pages 6.16-6.17). For notational convenience and pedagogical considerations, the algorithm is divided into two parts. The first part gives a procedure, called QUICK, which executes the above reduction step of the algorithm, and the second part uses QUICK to sort the entire list, Observe that Step 2(c) (iii) is unnecessary. It has been added to emphasize the symmetry between Step 2 and Step 3. The procedure does not assume the elements of A are distinct. Otherwise, the condition LOC # RIGHT in Step 2{a) and the condition LEFT # LOC in Step 3(a) could be omitted. The second part of the algorithm follows (on page 6.17). As noted above, LOWER and UPPER are stacks on which the boundary values of the sublists are stored. (As usual, we use NULL = 0.) Procedure 6.7: QUICK(A, N, BEG, END, LOC) Here A is an array with N elements. Parameters BEG and END contain the boundary values of the sublist of A to which this procedure applies. LOC keeps irack of the position of the first clement A[BEG] of the sublist during the procedure. The local variables LEFT and RIGHT will contain the boundary values of the list of elements that have not been scanned. 1, {Initialize.] Set LEFT := BEG, RIGHT := END and LOC » 2. [Scan from right to left.] (a) Repeat while A[LOC) < A[RIGHT] and LOC # RIGHT: RIGHT := RIGHT - 1. [End of loop.) BEG. Stacks, Queues, Recursion 6.21 Observe that the first procedure evaluates N! using an iterative loop process. The second procedure, ‘on the other hand, is a recursive procedure, since it contains a call to itself. Some programming languages, notably FORTRAN, do not allow such recursive subprograms. Suppose P is a recursive procedure. During the running of an algorithm or a program which contains P, we associate a level number with cach given execution of procedure P as follows. The original execution of procedure P is assigned level 1; and each time procedure P is executed because of a recursive call, its level is | more than the level of the execution that has made the recursive call. In Example 6.8, Step 1 belongs to level I, Hence Step 2 belongs to level 2, Step 3 to level 3, Step 4 to level 4 and Step 5 to level 5. On the other hand, Step 6 belongs to level 4, since it is the result of a return from level 5. In other words, Step 6 and Step 4 belong to the same level of execution. Similarly, Step 7 belongs to level 3, Step 8 to level 2, and the final step, Step 9, to the original level 1. ‘The depth of recursion of a recursive procedure P with a given set of arguments refers to the maximum level number of P during its execution. Fibonacci Sequence The celebrated Fibona i sequence (usually denoted by Fo, Fj, Fo 0, 1. 1, 2, 3, 5,8, 13, 21, 34, 55, ... ‘That is, Fy =0 and F, = | and each succeeding term is the sum of the two preceding terms. For example, the next two terms of the sequence are 34455=89 and 55 +89 = 144 A formal definition of this function follows: .) is as follows: Definition 6.2: (Fibonacci Sequence) (a) If n = Oorn =I, then F, =n, (b) If r> 1, then F, a+ Fat This is another example of a recursive di ition, since the definition refers to itself when it uses F,_> and F,. , Here (a) the base values are 0 and 1, and (b) the value of F, is defined in terms of smaller values of m which are closer to the base values. Accordingly, this function is well- defined. A procedure for finding the nth term F, of the Fibonacci sequence follows. Procedure 6.10: FIBONACCIIFIB, N) ‘This procedure calculates Fy and returns the value in the first parameter FIB. . IfN=OorN = 1, then: Set FIB := N, and Return. Call FIBONACCI(FIBA. N — 2). . Call FIBONACCI(FIBB, N = 1). . Set FIB := FIBA + FIBB . Return, This is another example of a recursive procedure, since the procedure contains a call to itself, In fact, this procedure contains two calls to itself. We note (sce Solved Problem 6.17) that one can also write an iterative procedure to calculate F, which does not use recursion. 6.14 Dato Structures ‘Symbol Scanned | STACK Expression P a) A ( A @ 4 Cs A @ ¢ Cre A ; @ 8 (+e AB | @ ¢ Cee AaB @ ¢ C+ ABC a - Cee ABC: a) ¢ (+O ABC: @) 0 C+ 0- ABC-D ao C+ O- C1 ABG+D i (1) E C+ e- cr ABC-DE 027 (+#€-(/ Tt) ABC + DE (1a) F (+C-(Cr Tt] aABoc+OEF (14) ) (+ (- ABC-DEFT?/ (5) (*#C-5 ABC+DEFTS (16) G (+e ABCG+DEFT/G (7) (+ ABC-DEFT/IG.: -~ (8) + (+s ABG+DEFTIG+— (19) (ae ABC-.DEFT/G--H (20) ) LABG+DEFT/G+-~Het Fig. 6.12 6.6 QUICKSORT, AN APPLICATION OF STACKS Let A be a list of n data items. "Sorting A” refers to the operation of rearranging the elements of A so that they are in some logical order, such as numerically ordered when A contains numerical data, or alphabetically ordered when A contains character data. The subject of sorting, ineluding various sorting algorithms, is treated mainly in Chapter 9. This section gives only one sorting algorithm, called quicksort, in order to illustrate an application of stacks. ‘Quicksort is an algorithm of the divide-and-conquer type. That is, the problem of sorting a set is, teduced to the problem of sorting two smaller sets. We illustrate this “reduction step” by means of a specific example. Suppose A is the following list of 12 numbers: 33, 11, 55, 77, 90, 40, 60, 99, 88, The reduction step of the quicksort algorithm finds the final position of one of the numbers; in this illustration, we use the first number, 44. This is accomplished as follows. Beginning with the Jast number, 66, scan the list from right to left, comparing cach number with 44 and stopping at the first number less than 44. The number is 22. Interchange 44 and 22 to obtain the list @) 33, 11. 55, 77, 90, 40, 60, 99, @) 88, 66 (Observe that the numbers 88 and 66 to the right of 44 are each greater than 44.) Beginning with 22, next scan the list in the opposite direction, from left to right, comparing each number with 44 and stopping at the first number greater than 44. The number is 55. Interchange 44 and 55 to obtain the list Stacks, Queues, Recursion 6.45, 22, 33 11, 77. 90, 40, 60, 99, 65) 88. 66 (Observe that the numbers 22, 33 and 11 to the left of 44 are each less than 44.) Beginning this time with 55, now scan the list in the original direction, from right to left, until mecting the first number less than 44. It is 40. Interchange 44 and 40 to obtain the list 22, 33, 11, GO) 77, 90, G4) 60, 99, 55, 88, 66 (Again, the numbers to the right of 44 are each greater than 44.) Beginning with 40, scan the list from left to right. The first number greater than 44 is 77. Interchange 44 and 77 to obtain the list 22, 33, 11, 44, 90, a 60, 99, 55, 88, 66 (Again, the numbers to the left of 44 are each less than 44.) Beginning with 77, scan the list from right to left seeking a number less than 44. We do not meet such a number before meeting 44. This means all numbers have been scanned and compared with 44. Furthermore, all numbers less than 44 now form the sublist of numbers to the left of 44, and all numbers greater than 44 now form the ‘sublist of numbers to the right of 44, as shown below: 22,33, 11, 40, 90, 77, 60, 99, 55, 88 66 First sublist Second sublist Thus 44 is correctly placed in its final position, and the (ask of sorting the original list A has now been reduced to the task of sorting each of the above subli The above reduction step is repeated with each sublist containing 2 or more clements. Since we can process only one sublist at a time, we must be able to keep track of some sublists for future processing. This is accomplished by using two stacks, called LOWER and UPPER, to temporarily “hold” such sublists. That is, the addresses of the first and fast elements of each sublist, called its boundary values, are pushed onto the stacks LOWER and UPPER, respectively: and the reduction step is applied to a sublist only after its boundary values are removed from the stacks. The following example illustrates the way the stacks LOWER and UPPER are used. Example 6.8 Consider the above list A with n = 12 elements. The algorithm begins by pushing the boundary values 1 and 12 of A onto the stacks to yield LOWER: 1 UPPER: 12 In order to apply the reduction step, the algorithm first removes the top values 1 and 12 from the stacks, leaving LOWER: (empty) UPPER: (empty) and then applies the reduction step to the corresponding list A{1], A[2], .... A[12]. The reduction step, as executed above, finally places the first element, 44, in A[5]. Accordingly, the algorithm pushes the boundary values 1 and 4 of the first sublist and the boundary values 6 and 12 of the second sublist onto the stacks to yield LOWER: 1,6 UPPER: 4, 12 Data Structures Divide-and-Conquer Algorithms Consider a problem P associated with a set S. Suppose A is an algorithm which partitions § into smaller sets such that the solution of the problem P for $ is reduced to the solution of P for one or more of the smaller sets. Then A is called a divide-and-conquer algorithm Two examples of divide-and-conquer algorithms, previously treated, are the quicksort algorithm in Sec. 6.6 and the binary search algorithm in Sec. 4.7. Recall that the quicksort algorithm uses a reduetion step to find the location of a single element and to reduce the problem of sorting the entire set to the problem of sorting smaller sets. The binary search algorithm divides the given sorted set into two halves so that the problem of searching for an item in the entire set is reduced to the problem of searching for the item in one of the two halves. A divide-and-conquer algorithm A may be viewed as a recursive procedure. The reason for this is that the algorithm A may be viewed as calling itself when it is applied to the smaller sets, The base criteria for these algorithms are usually the one-element sets. For example, with a sorting algorithm, a one-clement set is automatically sorted; and with a searching algorithm, a one-element set requires only a single comparison. Ackermann Function The Ackermann function is a function with two arguments each of which can be assigned any nonnegative integer: 0, 1, 2,.... This function is defined as follows: Definition 6.3: (Ackermann Function) (a) If m = 0, then A(m, n) = + 1. (b) If m #0 but = 0, then A(m, n) = Atm - 1, 1). (c) If m #0 and n #0, then A(m, n) = A(m = 1, AGn.n = 1) Once more, we have a recursive definition, since the definition refers to itself in parts (b) and (c). Observe that A(m, n) is explicitly given only when m = 0, The base criteria are the pairs OO OD. OL 2, (0,3), sere Os me ove Although it is not obvious from the definition, the value of any AQ, n) may eventually be expressed in terms of the value of the function on one or more of the base pairs. The value of A(1, 3) is calculated in Solved Problem 6.18. Even this simple case requires 15 steps. Generally speaking, the Ackermann function is too complex to evaluate on any but a trivial example. Its importance comes from its use in mathematical logic. The function is stated here mainly to give another example of a classical recursive function and to show that the recursion part of a definition may be complicated. 6.8 TOWERS OF HANOI “The preceding section gave examples of some recursive definitions and procedures. This section shows how recursion may be used as a tool in developing an algorithm to solve a particular problem. The problem we pick is known as the Towers of Hanoi problem. Stacks, Queues, Recursion 6.25 () TOWER(N = 1, BEG, END, AUX) (2) TOWER(1, BEG, AUX, END) or BEG — END 3) TOWER(N - |, AUX, BEG, END) Observe that each of these three subproblems may be solved directly or is essentially the same as the original problem using fewer disks. Accordingly, this reduction process does yield a recursive solution to the Tawers of Hanoi problem. Figure 6.17 contains a schematic diagram of the above recursive solution for TOWER(, A, B,C) TOWER(1, A, C,B)....A +B oo AG... AG TOWERU,B, A.C)... BC me AoB... coe AB TOWER(,€, B, A)... CA Nocece C58... C48 TOWER(1, AC, 8)... A+B TOWER(A, A, B, C) - see -- AC TOWER(1, 8, A.C)...8 3 C a B.C, A) aT TOWER(, C, B.A)... CA BALBOA TOWERG, B, x SS Bac TOWER(1, A, C,8)...A 9B a Nec RAO Ae — TOWER(1, B, A, C)...B > G Hig. 6.47 Recursive Solution to Towers of Hanoi Problem for n = & Observe that the recursive solution for n = 4 disks consists of the following 15 moves: A+B AC B2C A+B CA CB AGB AGC BOC B2A C+A BC ASB ABC BSC In general, this recursive solution requires fln) = 2" - 1 moves for n disks. We summarize our investigation with the following formally written procedure. Stacks, Queues, Recursion 6.27 function subprogram. (This is no loss in generality, since function subprograms can easily be written as subroutine subprograms.) We also assume that a recursive call to P comes only from the procedure P. (The treatment of indirect recursion lies beyond the scope of this text.) The translation of the recursive procedure P into a nonrecursive procedure works as follows. First of all, one defines: ()_ A stack STPAR for each parameter PAR (2)_ A stack STVAR for each local variable VAR (3) A local variable ADD and a stack STADD to hold retum addresses Each time there is a recursive call to P, the current values of the parameters and local variables are pushed onto the corresponding stacks for future processing. and cach time there is a recursive return to P, the values of parameters and local variables for the current execution of P are restored from the stacks. The handling of the return addresses is more complicated; it is done as follows. Suppose the procedure P contains a recursive Call P in Step K. Then there are two return addresses associated with the execution of this Step K: () There is the current retum address of the procedure P, which will be used when the current level of execution of P is finished executing. (2) There is the new return address K + 1, which is the address of the step following the Call P and which will be used to return to the current level of execution of procedure P. Some texts push the first of these two addresses, the current return address, onto the retum address stack STADD, whereas some texts push the second address, the new return address K + 1, onto STADD. We will choose the latter method, since the translation of P into a nonrecursive procedure will then be simpler. This also means, in particular, that an empty stack STADD will indicate a return to the main program that initially called the recursive procedure P. (The alternative translation which pushes the current return address onto the stack is discussed in Solved Problem 6.21.) The algorithm which translates the recursive procedure P into a nonrecursive procedure follows. It consists of three parts: (1) preparation, (2) translating each recursive Call P in procedure P and (3) translating cach Return in procedure P. (1) Preparation. (a) Define a stack STPAR for each parameter PAR, a stack STVAR for each local v: VAR, and a local variable ADD and a stack STADD to hold return addresses. (b) Set TOP := NULL. (2) Translation of “Step K. Call P-” (a) Push the current values of the parameters and local variables onto the appropriate stacks, and push the new return address [Step] K + | onto STADD. (b) Reset the parameters using the new argument values. (c) Go to Step I. [The beginning of the procedure P.] (3) Translation of “Step J. Return.” (a) If STADD is empty, then: Return. [Control is returned to the main program.] ble Stacks, Queues, Recursion 6.23 Suppose three pegs, labeled A, B and C, are given, and suppose on peg A there are placed a finite number n of disks with decreasing size. This is pictured in Fig. 6.14 for the ease # = 6. The object of the game is to move the disks from peg A to peg C using peg B as an auxiliary. The rules of the game are as follows: (a) Only one disk may be moved at a time. Specifically, only the top disk on any peg may be moved to any other peg. {b) Atno time can a larger disk be placed on a smaller disk. A 8 c Fig. 6.44 Initial Setup of Towers of Hanoi with n = 6 Sometimes we will write X — Y to denote the instruction “Move top disk from peg X to peg Y." where X and ¥ may be any of the three pegs. The solution to the Towers of Hanoi problem for # = 3 appears in Fig. 6.15, Observe that it consists of the following seven move: n= 3: Move top disk from peg A to peg C. Move top disk from peg A to peg B. Move top disk from peg C to peg B. Move top disk from peg A to peg C. Move top disk from peg B to peg A. Move top disk from peg B to peg C. Move top disk from peg A to peg C. (a) Inital (Ase @)A>B @)C>B A BC A BG A 8 ¢ A Bc (A> (BA (6)B>C (ASC Fig. 6.15 6.24 Data Structures In other words, n=3 ASC, ASB, C3B, ASC, BOA, BoC, ASC For completeness, we also give the solution to the Towers of Hanoi problem for m = | and m= 2: n=l: ASC AB, AOC, BoC Note that n= 1 uses only one move and that n = 2 uses three moves. Rather than finding a separate solution for each , we use the technique of recursion to develop a general solution. First we observe that the solution to the Towers of Hanoi problem for n > 1 disks may be reduced to the following subproblems: (1) Move the top 1 ~ | disks from peg A to peg B. (2) Move the top disk from peg A to peg C: A > C. (3) Move the top 2 ~ | disks from peg B to peg C. This reduction is illustrated in Fig. 6.16 for = 6. That is, first we move the top five disks from peg A to peg B, then we move the large disk from peg A to peg C, and then we move the top five disks from peg B to peg C. A B Cc A B c i: c oo 1 i 1 {a) initiat: n= 6 (b) Move top five disks from peg A to peg B A 8 c A 8 ¢ (c) Move top disk from peg Ato peg (d) Move top five disks from peg B topeg G Fig. 6.16 Let us now introduce the general notation TOWER(N, BEG, AUX, END) to denote procedure which moves the top n disks from the initial peg BEG to the final peg END using the peg AUX as an auxiliary. When n = 1, we have the fallowing obvious solution: TOWER(|, BEG. AUX, END) consists of the single instruction BEG — END. Furthermore, as discussed above, when n > 1, the solution may be reduced to the solution of the following three subproblems: 6.40 Data Structures The procedures which insert and delete elements in deques and the variations on those procedures are given as supplementary problems. As with queties, a complication may arise (a) when there is overflow, that is, when an element is to be inserted into a deque which is already full, or (b) when there is underflow, that is, when an element is to be deleted from a deque which is empty. The procedures must consider these possibilities. 6.13 PRIORITY QUEUES A priority queue is a collection of elements such that each element has been assigned a priority and such that the order in which elements are deleted and processed comes from the following rules: (1) An element of higher priority is processed before any element of lower priority. (2) Two elements with the same priority are processed according to the order in whieh they were added to the queue. A prototype of a priority queue is a timesharing system: programs of high priority are processed first, and programs with the same priority form a standard queue. There are various ways of maintaining a priority queue in memory. We discuss two of them here: one uses a one-way list, and the other uses multiple queues. The ease or difficulty in adding elements to or deleting them from a priority queue clearly depends on the representation that one chooses. One-Way List Representation of a Priority Queue One way to maintain a priority queue in memory is by means of a one-way list, as follows: (a) Each node in the list will contain three items of information: an information field INFO, a priority number PRN and a link number LINK. (b) A node X precedes a node Y in the list (1) when X has higher priority than Y or (2) when both have the same priority but X was added to the fist before Y. This means that the order in the one-way list corresponds to the order of the priority queue. Priority number priority. s will operate in the usual way: the lower the priori number, the higher the Example 6.13 Figure 6.27 shows a schematic diagram of a priority queue with 7 elements. The diagram does not tell us whether BBB was added to the list before or after DDD. On | the other hand, the diagram does tell us that BBB was inserted before CCC, because : BBB and CCC have the same priority number and BBB appears before CCC in the list. 1 Figure 6.28 shows the way the priority queue may appear in memory using linear arrays INFO, PRN and LINK. (See Sec. 5.2.) 6.46 Data Structures 8, B Bs By Bs Be (a) 8, B. Bs Bs Be (b) Fig. 6.31 6.6 A Programming language provides two functions ALLOCATE (X) and FREE(X) for the maintenance of linked list structures. ALLOCATE(X) allots a node with address X for use in the linked list structure and FREE(X) frees the node with address X used in the application to the AVAIL list. Assuming the AVAIL list to be maintained as a linked stack, write procedures to implement the functions ALLOCATE and FREE. With the AVAIL list maintained as a linked stack, the procedure ALLOCATE(X) performs a pop operation on the linked stack to release the top node whose address is X. Also, the procedure FREE(X) performs a push operation on the linked stack, inserting the node that has been deleted from the application and whose address is X, to the top of the stack. Procedure ALLOCATE(X) 1. If AVAIL = NULL then NO_MORE_NODES 2, X = AVAIL [Allot top node of AVAIL to X] 3. AVAIL = LINK(AVAIL) [Reset AVAIL to point to the next node} 4, Exit Procedure FREE(X) 1, LINK(X) = AVAIL 2. AVAIL = X 3, Exit Polish Notation 6.7 Translate, by inspection and hand, each infix expression into its equivalent postfix expression: (a) (A -B) « (DIE) (b) (A+B TDME-F)+G () A * (B + DYE-F + (G+ HR) Using the order in which the operators are executed, translate each operator from infix to postfix notation, (We use brackets [ ] to denote a partial translation.) (a) (A - B)(D/E) = [AB-]«[DE/] = AB - DE/+ (b) (A+ BT DME - F) + G= (A+ [BDT)/EF -] + G = [ABDT+/(EF-] + G = [ABDT +EF = /] + G = ABDT+EF/G + (©) As(B + D) /E - Fe(G + H/K) = As[BD +J/E - Fe(G + [HK/]) = [ABD + *VE - Fe[GHK/ +] 6.4 Oata Structures || a 8 a [a] A A A A @ io) © @ ©) © Fig. 6.4 Similarly, after completing the processing of C, we remove folder B ftom the stack, leaving the stack as pictured in Fig, 6.4(c), and continue to process B. Finally, after completing the processing of B, we remove the last folder, A, from the stack, leaving the empty stack pictured in Fig, 6.4(0, and continue the processing of our original project A. Observe that, at each stage of the abave processing, the stack automatically maintains the arder that is required to complete the processing. An important example of such a processing in computer science is where A is a main program and B, C and D are subprograms called in the order given. 6.3 ARRAY REPRESENTATION OF STACKS Stacks may be represented in the computer in various ways, usually by means of a one-way list or a linear array. Unless otherwise stated or implied, each of our stacks will be maintained by a linear array STACK; a pointer variable TOP, which contains the location of the top element of the stack: and a variable MAXSTK which gives the maximum number of elements that can be held by the stack. The condition TOP = 0 or TOP = NULL will indicate that the stack is empty. Figure 6.5 pictures such an array representation of a stack. (For notational convenience, the array is drawn horizontally rather than vertically.) Since TOP = 3, the stack has three elements, XXX, YYY and ZZZ; and since MAXSTK = 8 there is room for 5 more items in the stack. ‘STACK mT =y 1 TOP | 3 __J MAXSTK Fig. 6.5 The operation of adding (pushing) an item onto a stack and the operation of removing (popping) an item from a stack may be implemented, respectively, by the following procedures, called PUSH and POP. In executing the procedure PUSH, one must first test whether there is room in the stack for the new item; if not, then we have the condition known as overflow. Analogously, in executing the procedure POP, one must first test whether there is an element in the stack to be deleted; if not, then we have the condition known as underflow. Stacks, Queues, Recursion 6.19 A recursive procedure with these two properties is said to be well-defined. Similarly, a function is said to be recursively defined if the function definition refers to itself. Again, in order for the definition not to be circular, it must have the following two properties: (There must be certain arguments, called base valves, for which the function does not refer to itself. 2) Each time the function does refer to itself, the argument of the function must be closer to a base value A recursive function with these two properties is also said to be well-defined. The following examples should help clarify these ideas. Factorial Function The product of the positive integers from 1 ton, inclusive, is called “n factorial” and is usually denoted by nt: 2-3... u= 2 1) It is also convenient to define 0! = 1, so that the function is defined for all nonnegative integers. Thus we have Ol=1 Ist 2te4-2 " nm Ba1-2-3=6 4=1-2-3-4=24 Stel 4 1-2:354+5-6=720 and so on, Observe that S!=S-4t=5-24=120 and 6! =6-S!=6- 120=720 This is tue for every positive imeger n; that is, Ce Accordingly, the factorial function may also be defined as follows: Definition 6.1: (Factorial Function) (a) In = 0, then n! = 1. (b) f'n > 0, then al=n- (nD! Observe that this definition of x! is recursive, since it refers to itself when it uses (2 — 1)! However, (a) the value of mt! is explicitly given when a =0 (thus O is the base value); and (b) the value of a! for arbitrary n is defined in terms of a smaller value of m which is closer to the base value 0. Accordingly. the definition is not circular, or in other words, the procedure is well- defined. Example 6.9 Let us calculate 4! using the recursive definition. This calculation requires the following nine steps: 6.32 Data Structures Observe that the output consists of the following seven moves: ASC, ASB. C3B, ASC BOA, BoC. ABC This agrees with the solution in Fig. 6.15. Summary The Towers of Hanoi problem illustrates the power of recursion in the solution of various algorithmic problems. This section has shown how to implement recursion by means of stacks when using a programming language—notably FORTRAN or COBOL—which does not allow recursive programs, In fact, even when using 2 programming langus upport recursion, the programmer may want to use the nonrecursive solution, since it may be much less expensive than using the recursive solution. 6.10 QUEUES A queue is a linear list of elements in which deletions can take place only at one end, called the front, and insertions can take place only at the other end, called the rear. The terms “front” and “rear” are used in describing a linear list‘only when it is implemented as a queue. Queues are also called first-in first-out (FIFO) lists, since the first element in a queue will be the first element out of the queue. In other words, the order in which elerhents enter a queue is the order in which they leave. This contrasts with stacks, which are I in first-out (LIFQ) lists. Queues abound in everyday life. The automobiles waiting to pass through an intersection, form a queue, in which the first car in line is the first car through; the people waiting in line at a bank form a queue, where the first person in line is the first person to be waited on; and so on. An important example of a qucue in computer science occurs in a timesharing system, in which programs with the same priority form a queue while waiting to be executed. (Another structure, called a priority queue, is discussed in Sec. 6.13.) Example. 6.10 Figure 6.19(a) is a schematic diagram of a queue with 4 elements: where AAA is the front element and ODD is the rear element. Observe that the front and rear elements of the queue are also, respectively, the first and last elements of the list. Suppose an element is deleted from the queue. Then it must be AAA. This yields the queue in Fig. 6.19(b), where BBB is now the front element. Next, suppose EEE is added to the queue and then FFF is added to the queue. Then they must be added at the rear of the queue, as pictured in Fig. 6.19(c). Note that FFF is now the rear element, Now suppose another element is deleted from the queue; then it must be BBB, to yield the queue in Fig, 6,19(d), And so on. Observe that in such a data structure, EEE will be deleted before FFF because it has been placed in the queue before FFF. However, EEE will have to wait until CCC and DDD are deleted. Dota Structures ‘Stack of Stack of Stack of dishes Pennies. folded towels Fig. 6.1 Bus Stop Fig. 6.2 Queue Waiting for a Bus 6.2 STACKS A stack is a list of elements in which an element may be inserted or deleted only at one end, called the top of the stack. This means, in particular, that elemenis are removed from a stack in the reverse order of that in which they were inserted into the stack. Special terminology is used for two basic operations associated with stacks: (a) “Push” is the term used to insert an element into a stack. (b) “Pop” is the term used to delete an element from a stack. ‘We emphasize that these terms are used only with stacks, not with other data structures. Example 6.1 Suppose the following 6 elements are pushed, in order, onto an empty stack: | ‘AAA, BBB, CCC, DDD, EEE, FFF Figure 6.3 shows three ways of picturing such a stack. For notational convenience, we will frequently designate the stack by writing: STACK: AAA, BBB, CCC, DDD, EEE, FFF The implication is that the right-most element is the top element. We emphasize that, regardless of the way a stack is described, its underlying property is that insertions and deletions can occur only at the top of the stack, This means EEE cannot be deleted before FFF is deleted, DDD cannot be deleted before EEE and FFF are deleted, and so on, Consequently, the elements may be popped from the stack only in the reverse order of that in which they were pushed onto the stack, 6.44 Data Structures | SOLVED PROBLEMS | PROBLEMS STACKS 6.1 Consider the following stack of characters, where STACK is allocated N = 8 memory cells: STACK: A, C,D,F, K, __. (For notational convenience, we use “___” to denote an empty memory cell.) Deseribe the stack as the following operations take place: (a) POP(STACK, ITEM) (e) POP(STACK. ITEM) (b) POP(STACK, ITEM) (ff) PUSH(STACK, R) (c) PUSH(STACK, L) (g) PUSH(STACK, S) (d) PUSH(STACK, P) (h) POP(STACK, ITEM) ‘The POP procedure always deletes the top element from the stack, and the PUSH procedure always adds the new element to the top of the stack. Accordingly: (a) (by © (ay © © (g) (hy 6.2 Consider the data in Problem 6.1. (a) When will overflow occur? (b) When will C be deleted before D? (a) Since STACK has been allocated N = 8 memory cells, overflow will occur when STACK contains & elements and there is a PUSH operation to add another clement to STACK, (b) Since STACK is implemented as a stack, C will never be deleted before D. 6.3. Consider the following stack, where STACK is allocated N = 6 memory cells: —_ STACK: AAA, DDD, EEE, FFF, GGG. Describe the stack as the following operations take place: (a) PUSH(STACK, KKK). (b) POP(STACK, ITEM), (c) PUSH(STACK, LLL), (d) PUSH(STACK, SSS), (¢) POP(STACK, ITEM) and (f) PUSH(STACK, TTT). (a) KKK is added to the top of STACK. yielding STACK: AAA, DDD, EEE, FFF, GGG, KKK Stacks, Queues, Recursion 6.45 64 65 (b) The top element is removed from STACK, yielding STACK: AAA, DDD, EEE, FFF, GGG, (©) LLL is added to the top of STACK, yielding STACK: AAA, DDD, EEE, FFF, GGG, LLL (4) Overflow occurs, since STACK is full and another element SSS is to be added to STACK. No further operations can take place until the overflow is resolved—by adding additional space for STACK, for example. Suppose STACK is allocated N = 6 memory cells and initially STACK is empty, or, in other words, TOP = 0). Find the output of the following module: 1. Set AAA ‘= 2 and BBB := 5. 2. Call PUSH(STACK, AAA). Call PUSH(STACK, 4). Call PUSH(STACK, BBB + 2) Call PUSH(STACK, 9). Call PUSH(STACK, AAA + BBB). 3. Repeat while TOP 4 0: Call POP(STACK, ITEM). Write: ITEM. [End of loop.] 4. Return, Step 1. Sets AAA = 2 and BBB = 5. Step 2, Pushes AAA = 2, 4, BBB + 2 = 7, 9 and AAA + BBB =7 onto STACK, yielding STACK: 2, 4, 7,9, 7, _ Step 3. Pops and prints the elements of STACK until STACK is empty. Since the top clement is always popped, the output consists of the following sequence: 7,9,7,4,2 Observe that this is the reverse of the order in which the elements were added to STACK. Suppose a given space S of N contiguous memory’ cells is allocated to K = 6 stacks. Describe ways that the stacks may be maintained in'S. Suppose no prior data indicate that any one stack will grow more rapidly than any of the other stacks. Then one may reserve N/K cells for each stack, as in Fig. 6.31(a), where B,, B,, ..., By denote, respectively, the bottoms of the stacks. Alternatively, one can partition the stacks into pairs and reserve 2N/K cells for each pair of stacks, as in Fig. 6.31(b). The second method may decrease the number of times overflow will occur. 6.30 Data Structures (c) (Level 3) The Step 2 recursive call [TOWER(N - 1, BEG, END, AUX)] assigns the following values to the parameters: N:=N-1=1, BEG:=BEG=A, AUX := END =B, END := AUX=C Step |, Now N = 1. The operation BEG — END implements the move AoC ‘Then control is transferred to Step 5. [For the Return,] Step 5. The stacks are not empty, so the top values on the stacks are removed, leaving Fig. 6.18(c), and are assigned as follows: N:=2, BEG:=A, AUX:=C, END:=B, ADD Control is transferred to the preceding Level 2 at Step ADD. (@) (Level 2) [Reactivated at Step ADD = 3.] Step 3. The operation BEG — END implements the move A+B Step 4, This is a recursive call. Hence the current values of the variables and the new return address (Step 5) are pushed onto the stacks as pictured in Fig. 6.18(d). (e) (Level 3) The Step 4 recursive call [TOWER(N - 1, AUX, BEG, END)] assigns the following values to the parameters: N:=N-1=1, BEG:=AUX=C, AUX :=BEG=A, END := END = B Step 1. Now N = 1. The operation BEG > END implements the move CoB ‘Then control is transferred to Step 5. [For the Return.] Step 5. The stacks are not empty; hence the top values on the stacks are removed, leaving 6.18(¢), and they are assigned as follows: N BEG =A, AUX:=C, END:=B, ADD Control is transferred to the preceding Level 2 at Step ADD. () (Level 2) (Reactivation at Step ADD = 5.] Step 5. The stacks are not empty; hence the top values on the stacks are removed, leaving 6.18(, and they are assigned as follows: No=3, BEG:=A, AUX:=B, END:=C, ADD:=3 Control is transferred to the preceding Level | at Step ADD. (g) (Level 1) [Reactivation at Step ADD = 3.] Step 3. The operation BEG + END implements the move ASC Step 4. This is a recursive call. Hence the current values of the variables and the new return address (Step 5) are pushed onto the stacks as pictured in Fig. 6.18(g). Data Structures 4, If (FRONT = NULL) then FRONT = REAR = NEW [If Q is empty then ITEM is the first element in the queue Q] else set LINK[REAR] := NEW and REAR = NEW, [REAR points to the new node appended to the end of the list] 5. Exit, Procedure 6.16: LINKQ_DELETE (INFO, LINK, FRONT, REAR, AVAIL, ITEM) This procedure deletes the front element of the linked queue and stores it in ITEM 1, [Linked queue empty’] if (FRONT = NULL) then Write: UNDERFLOW and Exit 2, Set TEMP = FRONT [If linked queue is nonempty, remember FRONT in a temporary variable TEMP] 3, ITEM = INFO (TEMP) 4, FRONT = LINK (TEMP) [Reset FRONT to point to the next element in the queue] 5, LINK(TEMP) =AVAIL and AVAIL=TEMP [retum the deleted node TEMP to the AVAIL list] 6, Exit, Example 6.12 For the linked queue shown in Fig. 6.22, the snapshots of the queue structure after the execution of the following operations are shown in Fig. 6.25. (i) Delete (ii) Delete (iii) Insert FFF Original linked queue: (i) Delete AAA aB8 coc opp Let eee) Front Rear (@) aK | BBB cece ppp | « pA! 1 i Front Front Roar 59 5.10 5.11 Linked Lists 7 Write a program which reads the social security number SSS of an employee and prints the employee's record. Test the program using (a) 165-64-3351, (b) 136-46-6262 and (c) 177- 44-5555. Write a program which reads an integer K and prints the name of each male employee when K = | or of each female employee when K = 2. Test the program using (a) K = 2, (b) K =5 and (c) K = 1. Write a program which reads the name NNN of an employee and deletes the employee's record from the structure. Test the program using (a) Davis, (b) Jones and (c) Rubin. Write a program which reads the record of a new employee and inserts the record into the file. Test the program using (a) Fletcher, 168-52-3388, Female, 21 000; and (b) Nelson, 175- 32.2468, Male, 19 000. Remark: Remember to update the header record whenever there is an insertion or a deletion. The terminology sometimes used for Step 5 is that ® will “sin| Stacks, Queues, Recursion [6.3 (a) Repeatedly pop from STACK and add to P cach operator (on the tap of STACK) which has the same precedence as or higher precedence than ®. (b) Add @ to STACK. {End of If structure.} 6. Ifa right parenthesis is encountered, then: (a) Repeatedly pop from STACK ‘and add to P each operator (on the top of STACK) until left parenthesis is encountered, (b) Remove the left parenthesis. [Do not add the left parenthesis to Py [End of If structure.] [End of Step-2 Joop.] 7. Exit. * to its own level. Example 6.7 Consider the following arithmetic infix expression Q: Q: A+(B+C-(D/ETF)*G)*H ‘We simulate Algorithm 6.6 to transform Q into its equivalent postfix expression P. First we push “(“ onto STACK, and then we add “)” to the end of Q to obtain: A + ( B+ € - ( Df —E T FY © @) @ @ ® &) (© () ) @) (0) (a) (12) (13) (14) (15) © ) + H ) (46) (17) (18) (19) (20) The elements of Q have now been labeled from left to right for easy reference. Figure 6.12 shows the status of STACK and of the string P as each element of Q is scanned. ‘Observe that (2) Each operand is simply added to P and does not change STACK. (2) The subtraction operator (-) in row 7 sends » from STACK to P before it (-) is pushed onto STACK. (3) The right parenthesis in row 14 sends 7 and then / from STACK to P, and then removes the left parenthesis from the top of STACK. (4) The right parenthesis in row 20 sends « and then + from STACK to P, and then removes the left parenthesis from the top of STACK. ‘After Step 20 is executed, the STACK is empty and PA BC+ DEFT SG *-H* + which is the required postfix equivalent of Q. Stacks, Queues, Recursion _ 63 1 [aaa 2 | BBB 3 | cee oP 4 [pp L TOP 5 | cee EEE 7 DoD s| | coc 9 BBB : “AAA Ne N (a) (b) AA | 888 | CCC | DDD | ECE | FFF oe + 2 8 «@ 8 6 7 @ 8 Not N Fig. 6.3 Diagrams of Stacks Consider again the AVAIL list of available nodes discussed in Chapter 5. Recall that free nodes were removed only from the beginning of the AVAIL list, and that new available nodes were inserted only at the beginning of the AVAIL list. In other words, the AVAIL list was implemented as a stack. This implementation of the AVAIL list as a stack is only a matter of convenience rather than an inherent part of the structure. In the following subsection we discuss an important situation where the stack is an essential tool of the processing algorithm itself. Postponed Decisions Stacks are frequently used to indicate the order of the processing of data when certain steps of the processing must be postponed until other conditions are fulfilled. This is illustrated as follows. Suppose that while processing some project A we are required to move on to project B, whose completion is required in order to complete project A. Then we place the folder containing the data of A onto a stack, as pictured in Fig. 6.4(a), and begin to process B. However, suppose that while processing B we are led to project C, for the same reason. Then we place B on the stack above A, as pictured in Fig. 6.4(b), and begin to process C. Furthermore, suppose that while processing C we are likewise led to project D. Then we place C on the stack above B, as pictured in Fig. 6.4(c), and begin to process D. On the other hand, suppose we are able to complete the processing of project D. Then the only Project we may continue to process is project C, which is on top of the stack. Hence we remove folder C from the stack, leaving the stack as pictured in Fig. 6.4(d), and continue 10 process C. 6.18 Data Structures Complexity of the Quicksort Algorithm ‘The running time of a sorting algorithm is usually measured by the number fin) of comparisons required to sort # elements. The quicksort algorithm, which has many variations, has been studied extensively. Generally speaking, the algorithm has a worst-case running time of order n°/2, but an average-case running time of order m log 2. ‘The reason for this is indicated below. The worst case occurs when the list is already sorted. Then the first element will require comparisons to recognize that it remains in the first position. Furthermore, the first sublist will be empty, but the second sublist will have — 1 elements. Accordingly, the second element will require n— | comparisons to recognize that it remains in the second position, And so on. Conse- quently, there will be a total of ne) 2 om = 00) 2 comparisons, Observe that this is equal to the complexity of the bubble sort algorithm (Sec. 4.6). The complexity fin) = Or log n) of the average case comes from the fact that, on the average, each reduction step of the algorithm produces two sublists. Accordingly: fn ne (nal tre Ze (2) Reducing the initial list places 1 element and produces two sublis (2) Reducing the two sublists places 2 elements and produces four sublists. (3) Redueing the four sublists places 4 elements and produces eight subli (4) Reducing the eight sublists places 8 elements and produces sixteen sublists. And so on. Observe that the reduction step in the kth level finds the location of 2! elements; hence there will be approximately log, n levels of reductions steps. Furthermore, cach level uses at most 1 comparisons, sof) = O(n log n). In fact, mathematical analysis and empirical evidence have both shown that fin) = 1.4 Tn log nl is the expected number of comparisons for the quicksort algorithm 6.7 RECURSION Recursion is an important concept in computer science. Many algorithms can be best described in terms of recursion. This section introduces this powerful tool, and See, 6.9 will show how recursion may be implemented by means of stacks. Suppose P is a procedure containing cither a Call statement to itself or a Call statement to a second procedure that may eventually result in a Call statement back to the original procedure P. Then P is called a recursive procedure. So that the program will not continue to run indefinitely, a recursive procedure must have the following two properties: (1) There must be certain criteria, called base criteria, for which the procedure does not call itself. (2) Bach time the procedure does call itself (directly or indirectly), it must be closer to the base criteria. 6.20 Dato Structures (1) aba 4-3! (2) 3! @) 22s (4) ei. o! (5) ob=1 (6) Uede dad q 2a2ele2 (8) 313-256 (9) 46=4-6=26 That is: Step 1. This defines 4! in terms of 3!, so we must postpone evaluating 4! until we evaluate 3! This postponement is indicated by indenting the next step. Step 2. Here 3! is defined in terms of 2!, so we must postpone evaluating 3! evaluate 2! Step 3. This defines 2! in terms of 1! Step 4. This defines 1! in Step 5. This step can explicitly evaluate O!, since 0 is the base value of the recursive definition, Steps 6 to 9. We backtrack, using 0! to find 1!, using 1! to find 2!, using 2! to find 31, and finally using 3! to find 4! This backtracking is indicated by the “reverse” indention. Observe that we backtrack in the reverse arder af the original postponed evaluations. Recall that this type of postponed processing lends itself ta the use of stacks. (See Sec. 6.2.) til we The following are two procedures that each calculate n factorial. Procedure 6.94: FACTORIAL(FACT, N) ‘This procedure calculates N! and returns the ¥: 1. If N = 0, then: Set FACT := L, and Return. 2. Set FACT := I. [Initializes FACT for loop.] 3. Repeat for K = | to N. Set FACT := K*FACT. {End of loop.} 4, Retum, jue in the variable FACT. Procedure 6.9B: FACTORIAL(FACT, N) This procedure calculates N! and returns the value in the variable FACT. 1. If N = 0, then: Set FACT , and Return. 2. Call FACTORIAL(FACT, N — 1). 3. Set FACT := N*FACT. 4. Retum. Stacks, Queues, Recursion 6.17 (b) If LOC = RIGHT, then: Return. (c) If A{LOC] > A[RIGHT], then: (® [Interchange A[LOC) and A[RIGHT].] TEMP := A[LOC). A[LOC] A[RIGHT] := TEMP. ) Set LOC := RIGHT. (iii) Go to Step 3. [End of If structure. 3. [Scan from left to right.) (a) Repeat while A{LEFT] ¢ A[LOC) and LEFT # LOC: LEFT := LEFT + 1. {End of loop.] (b) If LOC = LEFT, then: Retum. (e) If A[LEFT] > A[LOC}, then (@) [Interchange A[LEFT] and A[LOC].] (i) Set LOC (ili) Go to Step 2. [End of If structure. Algorithm 6.8: (Quicksort) This algorithm sorts an array A with N elements, 1. [Initialize.] TOP := NULL. 2, [Push boundary values of A onto stacks when A has 2 or more elements.] If N > 1, then: TOP := TOP + 1, LOWER{1] := 1, UPPER[I] = N. 3. Repeat Steps 4 to 7 while TOP # NULL. 4 {Pop sublist from stacks.] Set BEG := LOWER{TOP}, END := UPPER[TOP], ‘TOP := TOP - 1. 5, Call QUICK(A, N, BEG, END, LOC), [Procedure 6.5.] 6 {Push left sublist onto stacks when it has 2 or more elements.] Wf BEG < LOC - 1, then: TOP := TOP + 1, LOWER[TOP] := BEG, UPPER[TOP] = LOC - 1. [End of If structure.] 7. {Push right sublist onto stacks when it has 2 or more elements.] LOC + | < END, then: ‘TOP := TOP + 1, LOWER(TOP] UPPER[TOP] := END. [End of If structure,] [End of Step 3 loop.] 8. Exit. LOC + 1, 6.26 Data Structures Procedure 6.11: TOWER(N, BEG, AUX, END) This procedure gives a recursive Solution to the Towers of Hanoi problem for N disks. 1. If N= 1, then: (a) Write: BEG — END. (b) Return, [End of If structure.] 2, [Move N ~ 1 disks from peg BEG to peg AUX.] Call TOWER(N — 1, BEG, END, AUX). 3. Write: BEG — END. 4, [Move N — | disks from peg AUX to peg END.) Call TOWER(N ~ 1, AUX, BEG, END). 5. Rem. One can view this solution as 2 divide-and-conquer algorithm, since the solution for m disks is reduced to a solution for n — | disks and a solution for 2 = | disk 6.9 IMPLEMENTATION OF RECURSIVE PROCEDURES BY STACKS The preceding sections showed how recursion may be a useful tool in developing algorithms for specific problems. This section shows how stacks may be used to implement recursive procedures. It is instructive to first discuss subprograms in general. Recall that a subprogram can contain both parameters and local variables. The parameters are the variables which receive values from objects in the calling program, called arguments, and which transmit values back to the calling program. Besides the parameters and local variables, the subprogram must also keep track of the return address in the calling program, This return address is essential, since control must be transferred back to its proper place in the calling program. At the time that the subprogram is finished executing and control is transferred back to the calling program, the values of the local variables and the return address are no longer needed. Suppose our subprogram is a recursive program. Then cach level of execution of the subprogram may contain different values for the parameters and local variables and for the return address. Furthermore, if the recursive program does call itself, then these current values must be saved, since they will be used again when the program is reactivated. Suppose a programmer is using a high-level language which admits recarsion, such as Pascal. Then the computer handles the bookkeeping that keeps track of all the values of the parameters, local variables and return addresses. On the other hand, if a programmer is using a high-level language which does not admit recursion, such as FORTRAN, then the programmer must set up the necessary bookkeeping by translating the recursive procedure into a nonrecursive one. This bookkeeping is discussed below Translation of a Recursive Procedure into a Nonrecursive Procedure Suppose P is a recursive procedure. We assume that P is a subroutine subprogram rather than a xiv Preface supplementary problems. The solved problems illustrate and amplify the material, and the supplementary problems furnish a complete review of the material in the chapter. I wish to thank many friends and colleagues for invaluable suggestions and critical review of the manuscirpt. I also wish to express my gratitude to the staff of the McGraw-Hill Schaum’s Outline Series, especially Jeffrey McCartney, for their helpful cooperation. Finally, I join many other authors in explicitly giving credit to Donald E, Knuth who wrote the first comprehensive treatment of the subject of data structures, which has certainly influenced the writing of this and many other texts on the subject. Seymour Lipschutz 6.34 Data Structures QUEVE FRONT: 1 z Reana [AAA | BBB | oc | DOD 1 2 3 4 5 6 7 N (a) QUEUE FRONT:2 T AeA G ses|ccc|opo| | 1 2 3 4 8 6 F N (b) QUEUE FRONT: 2 GEARS pep | ccc | opp | cee | FFF 1 2 3 4 5 6 7 N te) QUEUE FRONT:3 REAR: ccc] opp] Eee] FFF] 1 2 38 4 &§ 6 FN (a) Fig. 6.20 Array Representation of a Queue Similarly, if FRONT = N and an clement of QUEUE is deleted, we reset FRONT = | instead of increasing FRONT to N + 1. (Some readers may recognize this as modular arithmetic, discussed in Sec. 2.2.) Suppose that our queue contains only one element, i.c., suppose that FRONT = REAR # NULL and suppose that the element is deleted. Then we assign FRONT := NULL. and REAR := NULL to indicate that the queue is empty. Example 6.11 Figure 6.21 shows how a queue may be maintained by a circular array QUEUE with N= 5 memory locations. Observe that the queue always occupies consecutive locations except when it occupies locations at the beginning and at the end of the array. If the queue is viewed as a circular array, this means that it still occupies consecutive locations, Also, as indicated by Fig. 6.21(m), the queve will be empty only when FRONT = REAR and an element is deleted. For this reason, NULL is assigned to FRONT and REAR in Fig. 6.21(m). 6.12 Data Structures The elements of P have been labeled from left to right for easy reference. Figure 6.11 shows the contents of STACK as each element of P is scanned. The final number in STACK, 37, which és assigned to VALUE when the sentinel “)” is scanned, is the value of PL Transforming Infix Expressions into Postfix Expressions Let Q be an arithmetic expression written in infix notation. Besides operands and operators, Q may also contain left_and right parentheses. We assume that the operators in Q consist only of exponentiations (7), multiplications (*), divisions (/), additions (+) and subtractions (-), and that they have the usual three levels of precedence as given above. We also assume that operators on the same level, including exponentiations, are performed from left to right unless otherwise indicated by parentheses. (This is not standard, since expressions may contain unary operators and some languages perform the exponentiations from right to left. However, these assumptions simplify our algorithm.) The following algorithm transforms the infix expression Q into its equivalent postfix expression P. The algorithm uses a stack to temporarily hold operators and left parentheses. The postfix expression P will be constructed from left to right using the operands from Q and the operators which are removed from STACK. We begin by pushing a left parenthesis onto STACK and adding a right parenthesis at the end of Q. The algorithm is completed when STACK is empty. Algorithm 6.6: POLISH(Q. P) Suppose Q is an arithmetic expression written in infix notation. This algorithm finds the equivalent postfix expression P. 1. Push “(" onto STACK, and add “)" to the end of Q. 2. Scan Q from left to right and repeat Steps 3 to 6 for each element of Q until the STACK is empty: 3. If an operand is encountered, add it to P. 4, Ifa left parenthesis is encountered, push it onto STACK, 5. If an operator ® is encountered, then: ‘ Stacks, Queues, Recursion 6.43 ‘The computer usually evaluates an arithmetic expression written in infix notation in two steps. First, it converts the expression to postfix notation, and then it evaluates the postfix expression. In each step, the stack is the main tool that is used to accomplish the given task. We illustrate these applications of stacks in reverse order. That is, first we show how stacks are used to evaluate Postfix expressions, and then we show how stacks are used to transform infix expressions into postfix expressions. Evaluation of a Postfix Expression Suppose P is an arithmetic expression written in postfix notation. The following algorithm, which uses a STACK to hold operands, evaluates P. Algorithm 6.5: This algorithm finds the VALUE of an arithmetic expression P written in postfix. notation, 1. Add a right parenthesis “)” at the end of P. [This acts as a sentinel.) 2. Scan P from left to right and repeat Steps 3 and 4 for each element of P until the sentinel “)” is encountered. 3. If an operand is encountered, put it on STACK. 4, If an operator @ is encountered, then: (a) Remove the two top elements of STACK, whére’ A’ isthe! top element and B is the next-to-top element. (b) Evaluate B® A. (e) Place the result of (b) back on STACK, [End of If structure.] [End of Step 2 loop. 5, Set VALUE equal to the top clement on STACK. 6. Exit. ‘We note that, when Step 5 is executed, there should be only one number on STACK. Example 6.6 | Consider the following arithmetic expression P written in postfix notation: | P56 BH te 1 | (Commas are used to separate the elements of P so that 5, 6, 2 is not interpreted as | the number 562.) The equivalent infix expression Q follows: Q: 5+(6+2)-12/4 Note that parentheses are necessary for the infix expression Q but not for the postfix expression P. We evaluate P by simulating Algorithm 6.5. First we add a sentinel right parenthesis at the end of P to obtain P: 5, 6, 2 + *, 12, Ae i - ) @® @ 8 @ 6) & (8) (M9) + 6.10 Date Structures Example®6.5 Suppose we want to evaluate the following parenthesis-free arithmetic expression: 2T345e2T2-12/6 First we evaluate the exponentiations to obtain 8+5%4-12/6 Then we evaluate the multiplication and division to obtain 8 + 20 - 2. Last, we evaluate the addition and subtraction to obtain the final result, 26. Observe that the expression is traversed three times, each time corresponding to a level of precedence of the operations. Polish Notation For most common arithmetic operations, the operator symbol is placed between its two operands. For example, A+B C-D E+F GH This is called infix notation, With this notation, we must distinguish between (A + Bac and A+(BsC) by using either parentheses or some operator-precedence convention such as the usual precedence levels discussed above, Accordingly, the order of the operators and operands in an arithmetic expression does not uniquely determine the order in which the operations are to be performed. Polish notation, named after the Polish mathematician Jan Lukasiewicz, refers to the notation in which the operator symbol is placed before its two operands, For example, +AB -CD *EF /GH We translate, step by step, the following infix expressions into Polish notation using brackets [ ] to indicate a partial translation: (A + B)*C = [+AB]+C = ++ ABC A+ (BeC)= A+ [*BC) = + A*BC (A + B)(C = D) = [+ABJ/[-CD] = / + AB - CD ‘The fundamental property of Polish notation is that the order in whieh the operations are 10 be performed is completely determined by the positions of the operators and operands in the expression. Accordingly, one never needs parentheses when writing expressions in Polish notation. Reverse Polish notation refers to the analogous notation in which the operator symbol is placed after its 1wo operands: AB+ CD- EF* GH/ Again, one never needs parentheses to determine the order of the operations in any arithmetic expression written in reverse Polish notation. This notation is frequently called posifix (or suffix) notation, whereas prefix notation is the term used for Polish notation, discussed in the preceding ‘paragraph. Stacks, Queues, Recursion 6.9 (]) Push BBB (ji) Pop (iii) Pop (iv) Push MMM Original linked stack: Top NS XXX yw | i zzz |x (a) (i) Push BBB Top eee |} | few | [ae [= (6) (ii) Pop Jop Top » a6 P-L 00 T -—-wr [ ]}- —[ 2zz [= © Gil) Pop (iv) Push MMM Top MMM >| YY | zzz |x (e) Fig. 6.10 6.5 ARITHMETIC EXPRESSIONS; POLISH NOTATION Let Q be an arithmetic expression involving constants and operations, This section gives an algorithm which finds the value of Q by using reverse Polish (postfix) notation. We will see that the stack is an essential tool in this algorithm. Recall that the binary operations in Q may have different levels of precedence. Specifically, we assume the following three levels of precedence for the usual five binary operations: Highest: Exponentiation (1) Next highest: Multiplication (+) and division () Lowest: Addition (+) and subtraction (-) (Observe that we use the BASIC symbol for exponentiation.) For simplicity, we assume that Q contains no unary operation (e.g., a leading minus sign). We also assume that in any parenthesis- free expression, the operations on the same level are performed from left to right. (This is not standard, since some languages perform exponentiations from right to left.)

You might also like