0% found this document useful (0 votes)
33 views

COMP 1500: Computing: Ideas and Innovation Data Structures and Recursion

The document discusses data structures and recursion. It introduces data structures as abstractions that combine data with operations. Common data structures include lists and arrays. While arrays provide fixed sized contiguous memory blocks, linked lists allow dynamic sizes by connecting nodes. Binary search trees allow efficient searching by organizing nodes to keep left subtrees smaller and right subtrees larger than each node. The document uses examples like expression trees to illustrate representing non-linear data structures as trees.

Uploaded by

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

COMP 1500: Computing: Ideas and Innovation Data Structures and Recursion

The document discusses data structures and recursion. It introduces data structures as abstractions that combine data with operations. Common data structures include lists and arrays. While arrays provide fixed sized contiguous memory blocks, linked lists allow dynamic sizes by connecting nodes. Binary search trees allow efficient searching by organizing nodes to keep left subtrees smaller and right subtrees larger than each node. The document uses examples like expression trees to illustrate representing non-linear data structures as trees.

Uploaded by

Ming
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 55

COMP 1500

Computing: Ideas and Innovation


Data Structures and Recursion
COMP 1500 Winter 2019 Data Structures and Recursion 2

Data Structures
• Data structures are another abstraction
• Data structures combine data with operations on the data
• This is also called an Abstract Data Type (ADT)
• This abstraction allows us to include structured data in our
description of algorithms
• Data structures are language/computer independent
• For example, given a list of numbers, find the average
find average of List L
total = 0
for each item in L:
add item to total
average = total / size of L
COMP 1500 Winter 2019 Data Structures and Recursion 3

Lists
• We have seen one example of a Data Structure
• Lists are a collection of items, usually of the same type
• What operations would be useful for using a list?
• Add at the end
• Remove from the front
• Get each item in order
• Find a specific item
• Number of items in the list (size)
• Sort
• Possibly many others
• Which of these did we use in the previous slide?
COMP 1500 Winter 2019 Data Structures and Recursion 4

Arrays
• Of course, we can't use an abstraction in a real
programming language
• We need some way to implement them
• Lists are a fundamental data structure, so HLLs support
using them
• Many languages provide arrays
• A contiguous area (i.e. a single block) of memory
• For example, in Java
int [] data = new int[10];
• data is an array that holds 10 integer values
COMP 1500 Winter 2019 Data Structures and Recursion 5

General properties of Arrays


• Arrays typically hold one type of item
• For example, our example array data holds integers
• Arrays have a fixed size (or length) that does not change
• The array data holds 10 items
• To access a single value in an array, we use an index
• An index is an offset from the beginning of the array
• That means indexes (indices?) begin at zero, NOT one
• We use square braces to contain the index
• data[0], data[1], … data[9]
COMP 1500 Winter 2019 Data Structures and Recursion 6

Do arrays give us all we need?


• In a word, no
• Some things we would like to do with lists are not easily
done with arrays
• For example, inserting/deleting items in the middle of the
list
• We can't add a new spot in the array, or remove an existing one
• Instead, we have to shift items to make a 'hole' for a new item
• Or shift items to 'fill in' an empty spot
• Shifting items is a slow (i.e. linear) process
• Also, what happens if the array is full, and we have more
items to put in the list?
COMP 1500 Winter 2019 Data Structures and Recursion 7

Snap! lists
• Snap! also provides a block for using lists
• The list block on the Variables palette
• Along with blocks to work with lists
• Snap! lists are different from arrays
• They are not restricted to containing only one type of item
• They can grow or shrink as needed
• It is easy to insert or delete items to/from anywhere in the list
• It seems that Snap! lists have solved some of the
limitations we noted with arrays
• How do they do it?
• Linked lists!
COMP 1500 Winter 2019 Data Structures and Recursion 8

Linked Lists
• Linked lists do NOT use
contiguous memory
• Each item is contained
in a link (or node) that
connects to the next link
in a chain of nodes
• Like the links in a bicycle
chain
• These links can be taken
apart so that new links can
be added or old links
removed
https://commons.wikimedia.org/wiki/File:Racing_bicycle_chain_(detail).jpg
COMP 1500 Winter 2019 Data Structures and Recursion 9

Linked Lists
• Linked structures
• Linked lists are an elegant alternative to structures such as arrays
• Linked lists are often represented in the following manner:
• NULL is a special value meaning it points to nothing

top Pointer to next node Data

27 -38 4 36

node Last node’s pointer is NULL


COMP 1500 Winter 2019 Data Structures and Recursion 10

Using a linked list


• We can make changes to a linked list without shuffling
• For example: adding a new item at the front of the list
• We make a new node, and adjust some of the pointers

newnode

top 123

27 -38 4 36
COMP 1500 Winter 2019 Data Structures and Recursion 11

Deleting a node
• Removing a node in the middle of the list
• Point the previous node to the node after the one to delete

node to delete

top

123 27 -38 4 36
COMP 1500 Winter 2019 Data Structures and Recursion 12

Searching a linked list


• Linked lists are a sequential access data structure
• We have to start at the front and visit each node in the
chain
• This means that searching for a particular value is a linear
algorithm
• We know that binary search is much better than linear
search
• If our linked list is sorted, can we use binary search with it?
• Unfortunately, no – because there is no way to divide the list in half
• Or can we?
• We can have two links in each node to create a new data
structure
• A binary tree
COMP 1500 Winter 2019 Data Structures and Recursion 13

Binary trees
• A binary tree consists of nodes, which have two links
• The links are called children
• The children may either be empty, OR another tree (a sub-tree)
• The node that links to the child is called the parent
• Trees have a starting point called the root
• The root node has no parent
• Nodes that have no children are called leaf nodes
• Nodes in between the root and leaves are called interior
nodes
• There is a unique path from the root to each leaf node
COMP 1500 Winter 2019 Data Structures and Recursion 14

Binary trees
• An example
• The root node is labelled D
• The leaf nodes are labelled A, C, F and H

B E

A C G

F H
COMP 1500 Winter 2019 Data Structures and Recursion 15

Ordered Binary trees


• This is also an example of an ordered binary tree
• At each node that is not a leaf node:
• all the nodes in the left path are smaller than the node itself, AND
• all the nodes in the right path are larger than the node itself
• This data structure is called a Binary Search Tree

B E

A C G

F H
COMP 1500 Winter 2019 Data Structures and Recursion 16

Binary Search Tree (BST)


• With a BST, we can use binary search
• Given a BST called T and an item x:
search(T, x)
if T is null:
report x not found (EZ case 1)
else if T contains x:
report x found (EZ case 2)
else if x less than T:
search(left child of T, x) (recursive case 1)
if x still not found:
search(right child of T, x) (recursive case 2)
COMP 1500 Winter 2019 Data Structures and Recursion 17

Searching a BST
• Search for F
• Start at root D D

• go right to E
• go right to G B E

• go left to F
• Success A C G

F H
COMP 1500 Winter 2019 Data Structures and Recursion 18

Searching a BST
• Search for E
• Start at root D D
• go right to G
• go left B G

• found null
• E not found A C K

J L
COMP 1500 Winter 2019 Data Structures and Recursion 19

Binary Search
• At each step in this version of binary search, we are again
dividing the list in half
• By going either left or right
• As we saw before, binary search is a logarithmic algorithm
• But only if the tree is balanced
• That is, at each level, about half the remaining nodes are contained
in the left and right children
• Also note that the search algorithm calls itself
• This is called recursion
• More about recursion coming up
COMP 1500 Winter 2019 Data Structures and Recursion 20

Expression trees
• Mathematical expressions
can be represented using +

binary trees
• Operands are stored in leaf * 2
nodes
• Operators are stored in non- + 8
leaf nodes.
• Brackets are not required 3 5
• This tree represents
(3 + 5) * 8 + 2
COMP 1500 Winter 2019 Data Structures and Recursion 21

Game trees
• Not a binary tree, since a node can have more than 2
children
• Each level of the tree represents moves for one player
• Levels alternate between the player and the opponent
• For example, a game tree for tic-tac-toe

x
oo x
ox
xx x x x
oo x oo x oo x
ox ox ox x
xx o xx xo x x x xo x o
oo x oo x oo x oo x oo x oo x
ox oxo ox oxo ox x ox x
xx x xox xx x xo x
oo x oo x oo x oo x
oxo ox x oxo ox x
COMP 1500 Winter 2019 Data Structures and Recursion 22

Graphs
• Trees (of all sorts) and linked lists are two special
cases of a more general data structure known as a
graph
• Trees are generally used as a strict storage structure much
like a linked list or an array might be used
• Sometimes trees are used because the parent-child
relationship of the data is important
• Although a graph could be used as a strict storage
structure, it is generally used when complex
relationships between data must be represented
COMP 1500 Winter 2019 Data Structures and Recursion 23

Graphs
• Formally, a graph is a collection of entities called vertices,
and a collection of relationships between vertices, called
edges

Vertex
A
F B
X
E C
D
Edge
COMP 1500 Winter 2019 Data Structures and Recursion 24

Another graph example


• A graph of airline flight info
• In this example, the edges are directed (using an arrow)
• They also have a cost or weight (the price of a flight)

Saskatoon $299
$150
$95 Edmonton
$219
Winnipeg Moosejaw $209 Vancouver
$199
$175 Calgary

Regina $1099
$899 $349
COMP 1500 Winter 2019 Data Structures and Recursion 25

Graphs

• Dijkstra’s Shortest Path Algorithm


• A classic a la 1959
• A "greedy" algorithm always makes the locally optimal choice
under the assumption that this will lead to an optimal solution
overall
• A single source algorithm
• Generally easy to program
COMP 1500 Winter 2019 Data Structures and Recursion 26

Graphs
• Example B 60
• Cheapest path from A to all other 30 20
vertices
• For each vertex, record: A E
• Cost D 10
10
• Cheapest known cost to get to this
vertex from the source C
• Via
50 100
• The previous vertex on this path
• Done
• Is the route and cost guaranteed to
be optimum? A B C D E
• Iteration Cost 0 ∞ ∞ ∞ ∞
• When was the vertex marked done
Via - - - - -
• Initial table shown here
Done Yes No No No No
Iteration 0 - - - -
COMP 1500 Winter 2019 Data Structures and Recursion 27

Iteration 1
• Look at all outgoing B 60
edges from A that lead to 30 20
a vertex not yet done A E
• There are 3
• AE (cost 100) 10 D 10
• AB (cost 30)
C
• AC (cost 10) 50 100
• Fill in the cost and via entries
for B, C and E
• Choose the lowest cost for
any node not yet done A B C D E
• C (10) Cost 0 30 10 ∞ 100
• Mark C as done Via - A A - A
• Mark Iteration 1 for C Done Yes No Yes No No
Iteration 0 - 1 - -
Iteration 2
• Repeat, but now look at B 60
outgoing edges from C 30 20
• There is 1 A E
• CD (cost 50)
10 D 10
• Update the table entries for D
• The cost is the TOTAL cost from C
50 100
A to D (10 + 50)
• As before, choose the lowest
cost for any node not yet
done A B C D E
• B (30) Cost 0 30 10 60 100
• Mark B as done (iteration 2) Via - A A C A
Done Yes Yes Yes No No
Iteration 0 2 1 - -
Iteration 3
• Now look at outgoing B 60
edges from B 30 20
• There are 2
A E
• BD (cost 20)
• BE (cost 60) 10 D 10
• The cost to get to D via B is
30 + 20 = 50 C
50 100
• A lower cost, so update D
• We also have a lower cost to
get to E via B (30 + 60 = 90)
A B C D E
• As before, choose the lowest
cost for any node not yet Cost 0 30 10 50 90
done Via - A A B B
• D (50)
Done Yes Yes Yes Yes No
• Mark D as done (iteration 3)
Iteration 0 2 1 3 -
Iteration 4 (last iteration)
• Now look at outgoing B 60
edges from D 30 20
• There is one A E
• DE (cost 10)
10 D 10
• The cost to get to E via D is
50 + 10 = 60 C
50 100
• A lower cost, so update E
• As before, choose the lowest
cost for any node not yet
A B C D E
done (only one left)
• E (60) Cost 0 30 10 50 60
• Mark E as done (iteration 4) Via - A A B D
Done Yes Yes Yes Yes Yes
Iteration 0 2 1 3 4
COMP 1500 Winter 2019 Data Structures and Recursion 31

Activity
COMP 1500 Winter 2019 Data Structures and Recursion 32

Recursion
• Recursion is:
• An act of self-reference
• In Computer Science, self-reference is embodied in a function that
calls itself
• Magic
• Why magic?
• Defining an entity (problem) in terms of itself is not (at first) a
natural thing to do
• Results of recursion are sometimes surprising
• Lots of results from relatively little code

• Despite potential difficulties, some problems are “naturally


recursive”
COMP 1500 Winter 2019 Data Structures and Recursion 33

The Run Time Stack (RTS)


• In order for recursion to work, we need one of the Data
Structures we discussed earlier
• The Stack
• A Last-In, First-Out (LIFO) list
• When we look at examples of recursion, you won't actually
see a Stack being used though
• That is because the programming language itself provides
one
• The Run Time Stack (RTS), a special part of memory
• Each time a function is called, it gets an entry on the RTS
• To store variables, arguments, where to return to and a return value
• This means each call to a function has its own dedicated memory
memory layout
0xFFFFFFFF • when a program runs,
stack
(function calls) its instructions/globals
load into memory
• address space is like a
available memory huge array of bytes
address • as functions are called,
space data goes on a stack
heap • dynamic data is created
(dynamically allocated data) on a heap
global/static variables
("data segment")
code instructions
("text segment")
0x00000000
Stack frames
• stack frame or activation record: memory for a function call
• stores parameters, local variables, and return address to go
back to
int f(int p1, int p2) {
int x;
stack
int a[3];
...
return x + y; available
}

heap
global data
code
Stack frames
• stack frame or activation record: memory for a function call
• stores parameters, local variables, and return address to go
back to
int f(int p1, int p2) {
int x;
stack
int a[3];
contents
...
return x + y; p2 available
}
p1
return address heap
a[2] global data
a[1] code
a[0]
x
Tracing function calls
int main(void) { stack
int n1 = f(3, -5);
n1 = g(n1);
}

int f(int p1, int p2) {


int x;
int a[3];
...
x = g(a[2]); available
heap
return x + y;
global data
}
code
int g(int param) { main
return param * 2; f
} g
Tracing function calls
int main(void) { stack
int n1 = f(3, -5); main
n1 = g(n1); n1
}

int f(int p1, int p2) {


int x;
int a[3];
...
x = g(a[2]); available
heap
return x + y;
global data
}
code
int g(int param) { main
return param * 2; f
} g
Tracing function calls
int main(void) { stack
int n1 = f(3, -5); main
n1 = g(n1); n1
}

int f(int p1, int p2) {


int x;
int a[3];
...
x = g(a[2]); available
heap
return x + y;
global data
}
code
int g(int param) { main
return param * 2; f
} g
Tracing function calls
int main(void) { stack
int n1 = f(3, -5); main
n1 = g(n1); n1
}
f
p1, p2
int f(int p1, int p2) { x, a
int x;
int a[3];
...
x = g(a[2]); available
heap
return x + y;
global data
}
code
int g(int param) { main
return param * 2; f
} g
Tracing function calls
int main(void) { stack
int n1 = f(3, -5); main
n1 = g(n1); n1
}
f
p1, p2
int f(int p1, int p2) { x, a
int x;
int a[3];
...
x = g(a[2]); available
heap
return x + y;
global data
}
code
int g(int param) { main
return param * 2; f
} g
Tracing function calls
int main(void) { stack
int n1 = f(3, -5); main
n1 = g(n1); n1
}
f
p1, p2
int f(int p1, int p2) { x, a
int x;
int a[3]; g
p
...
x = g(a[2]); available
heap
return x + y;
global data
}
code
int g(int param) { main
return param * 2; f
} g
Tracing function calls
int main(void) { stack
int n1 = f(3, -5); main
n1 = g(n1); n1
}
f
p1, p2
int f(int p1, int p2) { x, a
int x;
int a[3]; g
p
...
x = g(a[2]); available
heap
return x + y;
global data
}
code
int g(int param) { main
return param * 2; f
} g
Tracing function calls
int main(void) { stack
int n1 = f(3, -5); main
n1 = g(n1); n1
}
f
p1, p2
int f(int p1, int p2) { x, a
int x;
int a[3];
...
x = g(a[2]); available
heap
return x + y;
global data
}
code
int g(int param) { main
return param * 2; f
} g
Tracing function calls
int main(void) { stack
int n1 = f(3, -5); main
n1 = g(n1); n1
}

int f(int p1, int p2) {


int x;
int a[3];
...
x = g(a[2]); available
heap
return x + y;
global data
}
code
int g(int param) { main
return param * 2; f
} g
Tracing function calls
int main(void) { stack
int n1 = f(3, -5); main
n1 = g(n1); n1
}
g
p
int f(int p1, int p2) {
int x;
int a[3];
...
x = g(a[2]); available
heap
return x + y;
global data
}
code
int g(int param) { main
return param * 2; f
} g
Tracing function calls
int main(void) { stack
int n1 = f(3, -5); main
n1 = g(n1); n1
}
g
p
int f(int p1, int p2) {
int x;
int a[3];
...
x = g(a[2]); available
heap
return x + y;
global data
}
code
int g(int param) { main
return param * 2; f
} g
Tracing function calls
int main(void) { stack
int n1 = f(3, -5); main
n1 = g(n1); n1
}

int f(int p1, int p2) {


int x;
int a[3];
...
x = g(a[2]); available
heap
return x + y;
global data
}
code
int g(int param) { main
return param * 2; f
} g
Tracing function calls
int main(void) { stack
int n1 = f(3, -5);
n1 = g(n1);
}

int f(int p1, int p2) {


int x;
int a[3];
...
x = g(a[2]); available
heap
return x + y;
global data
}
code
int g(int param) { main
return param * 2; f
} g
Tracing function calls
int main(void) { stack
int n1 = f(3, -5);
n1 = g(n1);
}

int f(int p1, int p2) {


int x;
int a[3];
...
x = g(a[2]); available
heap
return x + y;
global data
}
code
int g(int param) { main
return param * 2; f
} g
COMP 1500 Winter 2019 Data Structures and Recursion 51

An example: factorial
• Factorial 5! = 5 x 4!
5! = 5 x 4 x 3 x 2 x 1 4! = 4 x 3!
379! = 379 x 378 x…x 3 x 2 x 1 3! = 3 x 2!
1! = 1 2! = 2 x 1!
0! = 1 1! = 1
• The last result is called
• Factorial also has a
the EZ (non recursive)
recursive definition case
• n! = n x (n-1)! • All recursive definitions
• Factorial (!) uses itself in must have at least one
defining itself • It’s the key to successful
recursion
COMP 1500 Winter 2019 Data Structures and Recursion 52

Using Recursion
• Factorial is commonly the first example of recursion shown
to students because it's easy to understand
• A simple recursive definition
• Only one EZ case
• No one actually uses recursion to find factorial though
• It's easy to calculate using a loop
• So when do we use recursion?
• If the following two conditions hold:
• The problem is naturally recursive
• An iterative method would prove to be more difficult
• How do I know if a problem is naturally recursive?
• It's usually the data structure that determines this
• Such as Binary Trees
COMP 1500 Winter 2019 Data Structures and Recursion 53

Binary Trees
• We have already seen a recursive algorithm for searching
a binary search tree (BST)
• A BST has a simple recursive definition
• A BST is either:
• Empty (EZ case), OR
• A Node with left and right children that are BSTs
• As the search follows a path from the root, it calls itself repeatedly
• The RTS 'keeps track' of where the search has been previously
• If we didn't use recursion, we would have to have a Stack
of our own to keep track of where the search has been
• The algorithm would be much more complicated
COMP 1500 Winter 2019 Data Structures and Recursion 54

Another example of recursion


• A Snap! block to draw a tree
• The EZ case is the if-part
• It draws a line and returns to where
it began
• The recursion is the else-part
• It draws the trunk of the tree
• Then it turns left and draws a left
tree
• The level is reduced by 1
• It then turns right and draws a right
tree
• Finally, it returns to its starting point
COMP 1500 Winter 2019 Data Structures and Recursion 55

Drawing a Tree
• Here is a tree with 8 levels
• This would be very difficult to
draw without using recursion

You might also like