02 - APS - Basic Data Structures
02 - APS - Basic Data Structures
Data structure
●
a data structure is a way of storing and organizing a
dataset in a computer in order to facilitate its access
and modification
●
operations on data structures include searching,
inserting and deleting data
●
operation efficiency depends on the type of data
structure (sequential, hierarchical, …)
●
individual data structure element can contain a data
field, called a key, and associated satellite data, such
as pointers to other elements
3
Array
●
a one-dimensional array is a sequence of n keys of the
same data type, which are stored in consecutive memory
locations and accessible through their index
●
each array element is accessible in constant time, i.e., in
O(1)
●
arrays can be used for implementation of other data
structures and strings
5
Stack
●
is a data structure that uses strategy LIFO (last-in, first-
out) in which we can directly access only the last
inserted element (real examples are stack of plates or
towels)
●
for inserting an element onto a stack we use operation
push; for removing the last inserted element from a
stack we use operation pop*
PUSH POP
6
Stack
●
is a data structure that uses strategy LIFO (last-in, first-
out) in which we can directly access only the last
inserted element (real examples are stack of plates or
towels)
●
for inserting an element onto a stack we use operation
push; for removing the last inserted element from a
stack we use operation pop
12
11 11 11
9 9 9 9 9
8 8 8 8 8 8
1 1 1 1 1 1
Stack
●
stack is used for hardware implementation STACK-EMPTY(S)
of interrupts, method calls, recursion and if top = 0 then
temporary data storage (e.g., undo/redo) return TRUE
else
●
we will use array S for implementation of return FALSE
stack; the array length determines
maximum number MAX of elements PUSH(S,x)
that can be pushed onto the stack if top = MAX then
return overflow
●
top is the index of the next free location else
on the stack; S[0] is the bottom stack S[top] ← x
element and S[top-1] is the top stack top ← top + 1
element
●
stack is empty if top=0 POP(S)
if STACK-EMPTY(S) then
●
top is incremented by PUSH and return error
decremented by POP else
top ← top - 1
●
each operation has time complexity return S[top]
O(1)
8
Stack
0 1 2 3 4 5
●
example: S 12 7 2
– stack currently contains
three elements 12, 7 and 2 top=3
(top=3)
0 1 2 3 4 5
– after executing PUSH(S,9) S 12 7 2 9 6
and PUSH(S,6) we get
top=5
top=5
– executing POP(S) returns
0 1 2 3 4 5
element 6, which remains
S 12 7 2 9 6
in the array but will be
overwritten by next PUSH
top=4
9
Queue
●
is a data structure that uses strategy FIFO (first-in, first-
out) in which we can directly access only the element
that has been in the queue for the longest time (a real
example is checkout line)
●
for inserting an element into the queue we use operation
enqueue; for removing an element from the queue we
use operation dequeue
●
a queue is used, for instance, as a buffer for data
streaming
●
we will use array Q to implement a queue; an array of
length MAX can store MAX–1 queue elements
●
head is the index of first queue element and tail is the
index of first free array cell into which we will write next
element
10
Circular queue
●
a circular queue reuses previously occupied and
released locations in a linear array ⇒ after location
Q[MAX-1] fills, we write the next element to location Q[0]
(if it has been released in the meantime)
●
queue is empty if head=tail (initial state is head=tail=0)
QUEUE-EMPTY(Q)
if head = tail then
return TRUE
else
return FALSE
11
Circular queue
●
when inserting elements we write to location Q[tail] and
circularly increase tail; time complexity is O(1)
ENQUEUE(Q,x)
if (head=tail+1) or (head=0) and (tail=MAX-1) then
return overflow % queue is full
else
Q[tail] ← x % insert element
if tail = MAX - 1 then
tail ← 0 % circularly increase tail
else
tail ← tail + 1
12
Circular queue
●
when removing elements we read from location Q[head]
and circularly increase head; time complexity is O(1)
DEQUEUE(Q)
if QUEUE-EMPTY(Q) then
return error % queue is empty
else
x ← Q[head] % read element
if head = MAX - 1 then
head ← 0 % circularly increase head
else
head ← head + 1
return x
13
Circular queue
●
example:
– the queue currently contains elements 3, 1, 12, 9 and 7,
head=4 and tail=9
head
0 1 2 3 4 5 6 7 8 9
Q 3 1 12 9 7 3
1
head=4 tail=9 tail 7
9 12
14
Circular queue
●
example:
– the queue currently contains elements 3, 1, 12, 9 and 7,
head=4 and tail=9
– if we execute operations ENQUEUE(Q,4) and
ENQUEUE(Q,8), tail circularly increases to 1
tail
head
0 1 2 3 4 5 6 7 8 9 8
Q 8 3 1 12 9 7 4 3
4
1
tail=1 head=4 7
9 12
15
Circular queue
●
example:
– the queue currently contains elements 3, 1, 12, 9 and 7,
head=4 and tail=9
– if we execute operations ENQUEUE(Q,4) and
ENQUEUE(Q,8), tail circularly increases to 1
– after we execute DEQUEUE(Q), head increments to 5
tail
0 1 2 3 4 5 6 7 8 9 8
Q 8 3 1 12 9 7 4 3
4 head
1
tail=1 head=5 7
9 12
16
Linked list
●
a linked list is a linear dynamic data structure in which
consecutive elements are connected through pointers
●
each element contains a key (data field) and pointers
prev and next to previous and next element in the list
– if some element has no predecessor (successor) the
corresponding pointer is NIL
●
head is the pointer to first and tail is the pointer to last
element of the list
– list is empty if head=NIL
Linked list
●
types of linked list:
key next
– singly linked list (each
element only has pointer 8 17 15 NIL
next) head tail
Linked list
●
advantages:
●
the length of linked list is not limited except by the
amount of available memory
●
elements can be inserted anywhere
●
disadvantages:
●
access to elements is sequential
●
poor use of cache
●
extra space consumption for pointers
19
LIST-INSERT(L,x)
next[x] ← head
if tail = NIL then
tail ← x
head ← x
21
8 NIL 8
x head=x
17 next[head] 17 next[head]
head head
22
LIST-INSERT-AFTER(L,y,x)
next[x] ← next[y]
next[y] ← x
if tail = y then
tail ← x
23
8 NIL
x
20 next[y] 17 next[next[y]]
y next[y]
24
8 next[y]
x
20 x 17 next[next[y]]
y next[y]
25
●
when removing an element from singly linked list we
need a pointer to its predecessor, if one exists
● procedure LIST-DELETE(L) removes the first element
of list L*
LIST-DELETE(L)
x ← head
head ← next[head]
if tail = x then
tail ← NIL
destroy x
26
●
when removing an element from singly linked list we
need a pointer to its predecessor, if one exists
● procedure LIST-DELETE(L) removes the first element
of list L*
20 next[head] 17 next[next[head]]
head next[head]
20 next[head] 17 next[head]
head head
27
LIST-INSERT(L,x)
next[x] ← head
if head ≠ NIL then
prev[head] ← x
head ← x
prev[x] ← NIL
if tail = NIL then
tail ← x
30
●
procedure for searching is identical to the one for a singly
linked list
● procedure LIST-INSERT(L,x) inserts element x in the
head of list L*
y 8 next[y]
x
prev[y] 20 x x 17 next[next[y]]
y next[y]
33