Chapter 2 Stacks and recursion 1 2.1 Stacks A stack is a data - - PowerPoint PPT Presentation

chapter 2 stacks and recursion
SMART_READER_LITE
LIVE PREVIEW

Chapter 2 Stacks and recursion 1 2.1 Stacks A stack is a data - - PowerPoint PPT Presentation

CS 2412 Data Structures Chapter 2 Stacks and recursion 1 2.1 Stacks A stack is a data structure in which all insertions and deletions of entries are made at one end, called top of the stack. Examples: A stack of files. DOS command


slide-1
SLIDE 1

CS 2412 Data Structures

Chapter 2 Stacks and recursion

1

slide-2
SLIDE 2

2.1 Stacks A stack is a data structure in which all insertions and deletions of entries are made at one end, called top of the stack. Examples:

  • A stack of files.
  • DOS command line

LIFO: last in first out (or first in last out FILO).

Data Structure 2014

  • R. Wei

2

slide-3
SLIDE 3

Stacks are very important data structure. In any kind of computer, there is a stack area in memory (usually in higher memory addresses). When a subprogram is called, a return address is put in the stack. In this way, the computer can return to the place after the subprogram is finished.

Data Structure 2014

  • R. Wei

3

slide-4
SLIDE 4

Main operations of stack

  • Creation (create a stack).
  • Clearance (damage a stack).
  • Push (add an element to the stack)
  • Pop (get an element from the stack)

Data Structure 2014

  • R. Wei

4

slide-5
SLIDE 5

Specification for a stack To specify a stack, we consider the required functions and the preconditions and post-conditions for each function. We will use type Stack and StackEntry to denote the type stack and the entries of the stack. In general, for a stack, all the entries should be the same type data.

Data Structure 2014

  • R. Wei

5

slide-6
SLIDE 6

Initialization: Void CreateStack(Stack *s); precondition: None. postcondition: The stack s has been created and is initialized to be empty.

Data Structure 2014

  • R. Wei

6

slide-7
SLIDE 7

Status: Boolean StackEmpty(Stack *s); precondition: The stack exists and it has been initialized. postcondition: Return TRUE if the stack is empty, FALSE otherwise. Boolean StackFull(Stack *s); precondition: The stack exists and it has been initialized. postcondition: Return TRUE if the stack is full, FALSE otherwise.

Data Structure 2014

  • R. Wei

7

slide-8
SLIDE 8

Basic operations; void Push(StackEntry item, Stack *s); precondition: The stack exists and it is not full. postcondition: The argument item has been stored at the top of the stack. void Pop(StackEntry *item, Stack *s); precondition: The stack exists and it is not empty. postcondition: The top of the stack has been removed and returned in *itme.

Data Structure 2014

  • R. Wei

8

slide-9
SLIDE 9

Other operations: int StackSize(Stack *s); precondition: The stack exists and it has been initialized. postcondition: The function returns the number of entries in the stack. int StackTop(StackEntry *item, Stack *s); precondition: The stack exists and it is not empty. postcondition: The item at the top of the stack is returned without being removed.

Data Structure 2014

  • R. Wei

9

slide-10
SLIDE 10

Termination void ClearStack(Stack *s); precondition: The stack exists and it has been initialized. postcondition: All entries in the stack have been deleted; the stack is empty. Note: The postcondition can be defined to: deleted all the entries and the stack itself.

Data Structure 2014

  • R. Wei

10

slide-11
SLIDE 11

Traversable stack: This is not a ordinary stack and the following

  • peration is not an operation of a stack.

void TraverseStack(Stack *s, void(*Visit)()); precondition: The stack exists and it has been initialized. postcondition: The function that Visit points to, has been invoked for each entry in the stack, beginning with the entry at the top and proceeding toward the bottom of stack.

Data Structure 2014

  • R. Wei

11

slide-12
SLIDE 12

Implementation of stacks Declarations #define MAXSTACK 10 //size of the stack typedef char StackEntry; //stack of char typedef struct { int top; StackEntry entry[MAXSTACK]; } Stack;

Data Structure 2014

  • R. Wei

12

slide-13
SLIDE 13

void Push(StackEntry item,Stack *s) { if(StackFull(s)) Error("Stack is full"); else s->entry[s->top++]=item; } void Pop(StackEntry *item, Stack *s) { if(StackEmpty(s)) Error("Stack is empty"); else *item=s->entry[--s->top]; }

Data Structure 2014

  • R. Wei

13

slide-14
SLIDE 14

Boolean StackEmpty(Stack *s) { return s->top <=0; } Boolean StackFull(Stack *s) { return s->top >=MAXSTACK; } void CreateStack(Stack *s) { s->top=0; }

Data Structure 2014

  • R. Wei

14

slide-15
SLIDE 15

Dynamically allocating memories: void *calloc(n,size) void free(p) void *malloc(size) void *realloc(p,size) Example: tp = (double*) malloc(n*sizeof(double)); if (tp==NULL) printf("could not allocate %d doubles\n",n);

Data Structure 2014

  • R. Wei

15

slide-16
SLIDE 16
  • Don’t assume malloc will always succed.
  • Don’t assume the storage malloc provides is initialized to zero.
  • Don’t modify the pointer returned by malloc.
  • free only pointers obtained from malloc, and don’t access the

storage after it’s been freed.

Data Structure 2014

  • R. Wei

16

slide-17
SLIDE 17

Linked stack Dynamic memory allocation can allocate memories during the run time and free the memories after use. typedef struct node { void* dataPtr; struct node* link; } STACK_NODE; typedef struct { int count; STACK_NODE* top; } STACK;

Data Structure 2014

  • R. Wei

17

slide-18
SLIDE 18

STACK* createStack (void) { STACK* stack; stack = (STACK*)malloc(sizeof (STACK)); if (stack) { stack->count = 0; stack->top = NULL; } return stack; }

Data Structure 2014

  • R. Wei

18

slide-19
SLIDE 19

bool pushStack (STACK* stack, void* dataInPtr) { STACK_NODE* newPtr; newPtr = (STACK_NODE*)malloc(sizeof(STACK_NODE)); if (!newPtr) return false; newPtr->dataPtr = dataInPtr; newPtr->link = stack->top; stack->top = newPtr; (stack->count)++; return true; }

Data Structure 2014

  • R. Wei

19

slide-20
SLIDE 20

void* popStack (STACK* stack) { void* dataOutPtr; STACK_NODE* temp; if(stack->count == 0) dataOutPtr = NULL; else { temp = stack->top; dataOutPtr = stack->top->dataPtr; stack->top = stack->top->link; free (temp); (stack->count)--; } return dataOutPtr; }

Data Structure 2014

  • R. Wei

20

slide-21
SLIDE 21

void* stackTop (STACK* stack) { if (stack->count == 0) return NULL; else return stack->top->dataPtr; } bool emptyStack (STACK* stack) { return (stack->count == 0); }

Data Structure 2014

  • R. Wei

21

slide-22
SLIDE 22

bool fullStack(STACK* stack) { STACK_NODE* temp; if((temp=(STACK_NODE*)malloc(sizeof (*(stack->top))))) { free (temp); return false; } return true; } int stackCount (STACK* stack) { return stack->count; }

Data Structure 2014

  • R. Wei

22

slide-23
SLIDE 23

STACK* destroyStack(STACK* stack) { STACK_NODE* temp; if (stack) { while(stack->top !=NULL) { free(stack->top->dataPtr); temp = stack->top; stack->top = stack->top->link; free (temp); } free (stack) return NULL; }

Data Structure 2014

  • R. Wei

23

slide-24
SLIDE 24

2.2 Introduction to recursion Stack frames for subprograms Suppose we has 4 functions A, B, C and D. The main function M first calls A and A needs to call B. Then A calls C and C calls D. Finally, M calls D, D calls D. Computer uses stack frames in memory to remember the return places.

Data Structure 2014

  • R. Wei

24

slide-25
SLIDE 25

Recursion Recursion is the name for the case when a subprogram invokes itself or invokes a series of other subprograms that eventually invokes the first subprogram again.

Data Structure 2014

  • R. Wei

25

slide-26
SLIDE 26

Stack and tree of subprogram calling Theorem During the traversal of any tree, vertices are added to or deleted from the path back to the root in the fashion of a stack. Given any stack, conversely, a tree can be drawn to portray the life history of the stack, as items are pushed onto or popped from it.

Data Structure 2014

  • R. Wei

26

slide-27
SLIDE 27

Example: To implement factorial function, there are two different ways to do it. int Factorial(int n) { int i,k=1; if(n==0) return 1; else for(i=1;i<=n;i++) k=k*i; }

Data Structure 2014

  • R. Wei

27

slide-28
SLIDE 28

Using recursion: int Factorial(int n) { if(n==0) return 1; else return n*Factorial(n-1); } The source code looks more simple if a recursion is used. But recursion uses more memory.

Data Structure 2014

  • R. Wei

28

slide-29
SLIDE 29

The towers of Hanoi There are 3 diamond needles. On the first needle are stacked 64 golden disks, each one slightly smaller than the one under it. The task is to move all the golden disks from the first needle to the third, subject to the conditions that only one disk can be moved at a time, and that no disk is ever allowed to be placed on top of a smaller disk. We describe the problem as Move(64;1,3,2) It means move 64 disk from 1 to 3 using 2 as auxiliary needle.

Data Structure 2014

  • R. Wei

29

slide-30
SLIDE 30

Use recursion solution Question: In what condition we can move the last disk to the third needle? Answer: It must be the case that the third needle is empty, the first needle only has the last disk and all others are placed on the second needle. That means we must have done Move(63;1,2,3)

Data Structure 2014

  • R. Wei

30

slide-31
SLIDE 31

After that we just need to do the following things:

  • Move the last disks to the third needle.
  • Move other disks from second needle to the third needle, i.e.,

Move(63;2,3,1)

Data Structure 2014

  • R. Wei

31

slide-32
SLIDE 32

#define DISK 64 int main(void) { Move(DISK,1,3,2); return 0; } void Move(int count, int start, int finish, int temp) { if(count > 0) Move(count-1,start,temp,finish); printf("Move a disk from %d to %d.\n",start,finish); Move(count-1,temp,finish,start); }

Data Structure 2014

  • R. Wei

32

slide-33
SLIDE 33

To study a recursion function, try a very small example to trace its action is usually useful. We can use tree to see the cases of DISK equals to 2 and 3. The tree for 3 disk is as follows.

Data Structure 2014

  • R. Wei

33

slide-34
SLIDE 34

When DISK == 64, the number of non-leaves is 1 + 2 + 4 + · · · + 263 = 20 + 21 + 22 + · · · + 263 = 264 − 1 That means there are 264 − 1 moves to finish the towers of Hanoi problem. It is easy to estimate that 264 > 1.6 × 1019. If we can move one disk at one second, then it will take about 5 × 1011 years. I.e., it will take about 50 billion years! On the other hand, the space for keeping track of 64 recursive calls is not big.

Data Structure 2014

  • R. Wei

34

slide-35
SLIDE 35

2.3 Backtracking A puzzle: How to place eight queens on a chessboard so that no

  • ne can take another.

Q Q Q Q Q Q Q Q

Data Structure 2014

  • R. Wei

35

slide-36
SLIDE 36

Q Q Q Q Q Q Q Q How to find one solutions? How to find all the solutions?

Data Structure 2014

  • R. Wei

36

slide-37
SLIDE 37

Example of four queens: Q x x x x x x x x x x Q x x x x x Q Q x x x Q x

Data Structure 2014

  • R. Wei

37

slide-38
SLIDE 38

Backtracking algorithms Used in a search problem by constructing partial solutions and then trying to extend the partial

  • solutions. The main steps:
  • Establish a partial solution of the search problem. Make sure

that the partial solutions remain consistent with the requirement of the problem.

  • Extend the partial solutions toward the completion. If a

inconsistency occurs, then the algorithm backs up by removing the most recently constructed part of the solution and try another possibility.

Data Structure 2014

  • R. Wei

38

slide-39
SLIDE 39

Algorithm for eight-queen puzzle Try to use recursion. Define a function AddQueen which add a queen in an unguarded position on the board. We can recursively call this function until a solution is found, or backtrack a position. We use queencount to denote the number of queens have put on the board. The pseudo code of the function can be:

Data Structure 2014

  • R. Wei

39

slide-40
SLIDE 40

void AddQueen(void) { for(each unguarded position p on the board) { place a queen in position p; queencount++ and mark guarded positions; if (queencount==8) print the configuration; else AddQueen(); remove the queen from position p; queencount-- and free guarded positions; } }

Data Structure 2014

  • R. Wei

40

slide-41
SLIDE 41

To implement the algorithm, we need more specification. int col[8] is used to record the position of queens. col[i] = the column the ith queen is in. Boolean colfree[8] is used to record if the column is unguarded. For diagonals, it is more difficult to record and check. We can use a 8 × 8 array to record the guarded positions, but we have a better method. It is noted that for the position (i, j) all the positions in the downward diagonal the difference of row and column indices is the same (i − j). Similarly the sum of the row and column indices is the same for all the positions in the upward diagonal. So Boolean downfree[15] is used to record the unguarded positions on the downward diagonals and Boolean upfree[15]) is used to record the unguarded positions on the upward diagonals.

Data Structure 2014

  • R. Wei

41

slide-42
SLIDE 42

2.4 Principles of recursion Design a recursion algorithm:

  • Find the key step: Find a step which will be followed with

same or similar steps towards the solution.

  • Find a stopping rule: Each recursion algorithm has to have a

stopping rule.

  • Outline the algorithm: Abstraction, specification, details.
  • Check termination: To avoid infinite loops.
  • Draw a recursion tree: To analysis the algorithm.

Data Structure 2014

  • R. Wei

42

slide-43
SLIDE 43

From a recursion tree, we can estimate the requirements for space and time for running the program. The depth of the tree indicated the space requirement. The number

  • f nodes related to the time requirement.

Data Structure 2014

  • R. Wei

43

slide-44
SLIDE 44

Tail recursion If the last-executed statement of a function is recursive call to the function itself, then this call can be eliminated by reassigning parameters to the values specific in the recursive call, and then repeating the whole function.

Data Structure 2014

  • R. Wei

44

slide-45
SLIDE 45

The algorithm of Towers of Hanoi: void Move(int count, int start, int finish, int temp) { int swap; while(count > 0) { Move(count-1,start,temp,finish); printf("Move disk %d from %d to %d.\n",count,start, finish); count--; swap=start; start=temp; temp=swap; } }

Data Structure 2014

  • R. Wei

45

slide-46
SLIDE 46

Recursion and iteration Fibonacci number: F0 = 0, F1 = 1, Fn = Fn−1 + Fn−2, for n ≥ 2. int Fibonacci(int n) { if (n<=0) return 0; else if (n==1) return 1; else return Fibonacci(n-1) + Fibonacci(n-2); }

Data Structure 2014

  • R. Wei

46

slide-47
SLIDE 47

int Fibonacci(int n) { int i; int twoback,oneback,current; if (n<=0) return 0; else if (n==1) return 1; else { twoback = 0; oneback = 1; for(i=2;i<=n;i++){ current=twoback+oneback; twoback=oneback;

  • neback=current;

} return current; } }

Data Structure 2014

  • R. Wei

47

slide-48
SLIDE 48

Make decision of using recursion by recursion tree.

  • If the recursion tree has a simple form, the iterative version

may be better.

  • If the recursion tree involves duplicate tasks, then data

structures other than stacks will be appropriate, and the need for recursion may disappear.

  • If the recursion tree appears quite bushy, with little duplication
  • f tasks, then recursion is likely the nature method.

Data Structure 2014

  • R. Wei

48

slide-49
SLIDE 49

2.5 Parsing and postponement Parsing is any logic that breaks data into independent pieces for further processing. One example is to check unmatched parentheses in an algebraic expression. ((A + B)/C (A + B)/C) We can write a program to check if there are some unmatched parentheses.

Data Structure 2014

  • R. Wei

49

slide-50
SLIDE 50

Algorithm parseparens loop (more data) read (character) if (opening parenthesis) puchStack (stack, character) else if(closing parentheses) if (emptyStack (stack)) print (Error: closing parenthesis not matched) else popStack (stack) end if end if end loop if (not emptyStack (stack)) print (Error: Opening parenthesis not matched)

Data Structure 2014

  • R. Wei

50

slide-51
SLIDE 51

Sometimes, the logic of an application requires that the usage of data be deferred until some later point. A stack may be useful when the application required that the use of data be postponed for a while. Examples:

  • infix to postfix transformation.
  • postfix expression evaluation.

Data Structure 2014

  • R. Wei

51

slide-52
SLIDE 52

An arithmetic expression can be represented in three different formats: infix, postfix and prefix: Example: Infix: A + B

  • perator between two operands

Prefix: + A B

  • perator before two operands

Postfix: A B +

  • perator after two operands

Data Structure 2014

  • R. Wei

52

slide-53
SLIDE 53

Infix: (A ∗ B + C) − E/F Prefix: − + ∗ABC/EF Postfix: AB ∗ C + EF/− One main advantage of prefix or postfix is that no parentheses are required in the expressions, that simplifies the machine reading.

Data Structure 2014

  • R. Wei

53

slide-54
SLIDE 54

Infix to postfix transformation: Suppose the infix expression is: (A ∗ B + C) − E/F We can transform step by step as follows. ((A ∗ B) + C) − (E/F) (first group each pair with their operator) ((AB∗) + C) − (EF/) (then transform each group from infix to postfix) ((AB∗)C+) − (EF/) ((AB∗)C+)(EF/)− AB ∗ C + EF/− (finally remove all parentheses)

Data Structure 2014

  • R. Wei

54

slide-55
SLIDE 55

We note that the order of operands in infix and postfix is the same. The difference is the positions of operators. The position of an operator in postfix also depends on the priority

  • f the operator.

For example: infix A + B ∗ C will be ABC ∗ + in postfix. Priority 2: ∗ / Priority 1: +− Priority 0: (

Data Structure 2014

  • R. Wei

55

slide-56
SLIDE 56

We use a stack to store operators. When we want to push in an operator into the stack, we need to check if the operators in the stack have higher priority or not. If the operators in the stack have higher or same priority, then we pop out the operands. Otherwise we simply concatenate the

  • perator to the expression.

The parentheses have the lowest priority. So we push the open parenthesis into the stack and pop out when a close parenthesis is read.

Data Structure 2014

  • R. Wei

56

slide-57
SLIDE 57

The algorithm of convert infix to postfix: loop (for each character in formula) if (character is open parenthesis) pushStack (stack, character) else if (character is close parenthesis) popStack(stack, character) loop (character not open parenthesis) concatenate character of postFixExpr popStack (stack, character) end loop else if (character is operator) /* Test priority of token to token at the top of stack */ stackTop (stack, topToken) loop (stack not empty AND priority(character) <= priority(topToken)) popStack(stack, tokenOut)

Data Structure 2014

  • R. Wei

57

slide-58
SLIDE 58

concatenate tokenOut to postFixExpr stackTop(stack, topToken) end loop pushStack(stack, token) else /*character is operand*/ concatenate token to postFixExpr end if /*input formula empty. Pop stack to postfix */ loop(stack not empty) popStack (stack, character) concatenate character to postFixExpr end loop

Data Structure 2014

  • R. Wei

58

slide-59
SLIDE 59

Evaluating postfix expressions is relatively easy. We use a stack to store operands. When we meet an operator, pop

  • ut two operands and do the calculation. The result is then pushed

to the stack. When the formula is finished, the value remains in the stack is the final answer.

Data Structure 2014

  • R. Wei

59

slide-60
SLIDE 60

loop (for each character) if (character is operand) pushStack(stack, character) else /*it is operator*/ popStack(stack, oper2 popStack(stack, oper1)

  • perator= character

value = calculate(oper1,operator,opr2) pushStack(stack, value) end if end loop popStack(stack, result) return (result)

Data Structure 2014

  • R. Wei

60