52.219 LISTS, DLISTS, STACKS, QUEUES
A look at lists, how we can implement them and the operations
that act upon them. Same for doubly linked lists, stacks and queues.
LIST
Sequence of elements, two special cases are stacks and queues
Lists are flexible, grow and shrink on demand, elements can be accessed,
inserted, deleted. Lists can be joined together (concatenated),
split into sub-lists.
Applications, almost too many to list (!): simulations, memory
management, programming language translation, ...
Mathematically: a list is a sequence of zero or more elements of a
given type, and is often shown as
a1, a2, ..., an or as
[a1,a2,...,an] or as
(a1, a2,...,an)
LLIST (Linked Lists)
Also known as (aka) "pointer" implementation, or "linked-list"
(most frequently linked list).
source: /home/s7/pat/scheme/pub/llist.scm
1. show diagram of linked list for x -> [a c e]
end of list, and empty list
2. Print list (simple traversal), find an element in the list,
and get me the nth item in a list
3. show how we insert, and how we insert! (where insert! is destructive
or mutative, and insert is constructive (building a new structure))
Notes on insert((2)!)
(a) insert(!) takes a predicate that imposes some "order" on the list
and this allows POLYMORPHISM (a big win)
(b) insert constructs a new list, but retains the contents of the
list, doing this via (make-llist (head l) (insert e (tail l) p))
(c) insert! alters existing list, but must keep "ahead" of the
element that will be changed. That is insert(2)! looks one
cell into the future when forced to traverse/recurse
Notes on delete(!)
(a) delete constructs new list, delete! modifies list
COSTS v BENEFITS of LLIST (linked list)
COSTS: space overhead per element due to pointers, computational
effort in maintaining pointers, garbage when we use a constructive
technique for insert and delete, garbage on delete!
BENEFITS: do not need contiguous storage (don't need to pack/shift
elements), can allow lists to grow/shrink dynamically
VLIST (vector list)
Vector implementation of lists. We have a contiguous block of
storage area, a one dimensional array, a vector.
source: /home/s7/pat/scheme/pub/vlist.scm
Notes on insert
There is no such thing as insert (as far as I am concerned for
vlist). So to insert! something we make space for it by shifting
everything right from the position that we wish to occupy
(via function right-shift). Beware of careless right shift!!
Notes on lazy list
How about we mark a deleted element as deleted and do not do a left shift
until we are about to run out of space? Would there be any advantage?
For example, when inserting we do a right shift. Could we again
use deletions to our advantage (ie stopping right shift early?)
COSTS v BENEFITS for VLIST (vector list)
COSTS: hard work doing insertions and deletions (whole load of shifting
going on), have to set a limit on the size of the list, have that
space even if we don't use it.
BENEFITS: compact, no space overheads in additional pointers etc, can
get quickly to the nth element (don't have to traverse list).
ALIST (a cursor implementation using arrays)
Cursor base implementation of lists. We are using a two dimensional
array, so I called it an alist. Think of a row of the array,
the first column corresponds to the contents of the cell, and the
second column corresponds to a pointer to the next element in the
list (or empty).
Within the structure we have a pointer to the first element in the
list, and the first free element in the list. Initially element i
points to element i+1, the last element point to ground (0 in this
case), and free points to the first element. So, its all free
initially.
When we delete an element we remove it from the "active" list and
"push" it onto the free list (see free-element). Consequently, when
we insert! a new item/element into the list we must "pop" off a new
element from the free list, upd8 the free list, find the appropriate
insertion position, and upd8 the active list. Behaviour of insert!
is essentially the same as for llist.
COSTS v BENEFITS for ALIST (cursor based implementation)
COSTS: we have to maintain free space, essentially doing garbage
collection as we go, we put a limit on the size of the list,
BENEFITS: dont do all the shifting about that we have in vlist,
and probably have smaller space overhead than the llist
implementation. Generally, very similar to llist.
source: /home/s7/pat/scheme/pub/alist.scm
DLIST
Doubly linked list, same set of functions
Each element of the dlist points to previous and next memeber in the
list. We can then traverse left or right along the list. One might
expect that insertions and deletions will be easier.
Why have a DLIST? Again, using structure with pointers, and arrays
STACK
Stack is one of the most ubiquitous data structures in computing.
Earlier generation machines (such as PDP11) used a stack based architecture.
Function/procedure calls, when allowing local variables, will use a
stack to save off state information. Stacks are used as a mechanism
for evaluating expressions, deferring actions to be performed, and so on.
aka First In Last Out (FILO)
What is a stack, why do we need such a structure? (recursive functions)
(top s), (pop s), (push s), (empty? s). Again, structures versus arrays
Would we want to implement a stack in a similar way to vlist?
Think of a stack "growing down", such that top of stack (t.o.s) is last
element in the vector that is occupied, and element 1 is the bottom of stack
(b.o.s). Again, consider costs v benefits.
source: /home/s7/pat/scheme/pub/stack.scm
We will later look at removing recursion by implementing explicit stacks.
However, recursion is supported by stacks!!
QUEUE
Queue is very often used in simulations. That is we have a queue
of events that must happen in a given order. There will be a model
that takes events off the queue (dequeue) and executes that event.
Similarly there will be a process that generates events and adds them
to the queue (enqueue), maybe using some stochastic process.
An easy example to consider would be simulating a super market,
where events are customers (with certain demands on the check outs)
and a number of checkout desks. We might then wish to run a simulation
to determine what would be "optimal" number of desks, and shift
pattern for check outers (what would you call a person that works ata
a check out desk?). Might also consider design of airport, placement
of machines (or any other resource) in a factory, investment of funds,
and so on.
aka First In First Out (FIFO)
Why have queues? Structure versus array.
Queues implemented as circular arrays. How do we recognise over full, and
empty situations. What is the best implementation for a queue?
(front q), (enqueue e q), (dequeue q), (empty? q)
source: /home/s7/pat/scheme/pub/lqueue.scm