Вы находитесь на странице: 1из 7

Linked Lists, Arrays, Stacks and Queues

A linked list is a linear list data structure where the objects (of identical
type) point to the next and previous objects in the linear order (in doubly
linked lists) and to only the next object (in singly linked lists).
An array is a similar type of data structure for storing linear lists of
identical type of data.
Array locations can be instantly accessed, whereas in a linked list one
must move step by step through different nodes to reach a particular node.
However if an insertion must be made somewhere in the middle of an
array then all subsequent elements have to be pushed farther down the
array thus entailing a lot of data movement. This operation can be easily
performed in a linked list by changing the pointer fields of only the three
nodes involved. Likewise, when one deletes an element from the middle of
an array the subsequent data needs to be shifted.
A linked list has one (or two) global variables called Head (or head and
tail). It also specifies (implicitly) the type of data of its elements.
The following is the routine for inserting an element into a linked list at
the tail.
(doubly)-linked list insertion(L, x)
1.if (head[L] = nil)
2. then head[l] ← x
3. prev[x] ← next[x] ← nil
4. else next[tail[L]] ← x
5. prev[x] ← tail[l]
6. next[x] ← nil
7. tail[L] ← x

The running time of this procedure is θ(1) because there is a constant


number of boolean conditional checks and assignments independent of the
length of the linked list at the time of insertion.
The following is the routine for inserting an element into a linked list at
the head.

1
(doubly)-linked list insertion(L, x)
1.if (head[L] = nil)
2. then head[L] ← x
3. prev[x] ← next[x] ← nil
4. else next[x] ← head[L]
5. prev[x] ← nil
6. prev[head[L]] ← x
7. head[L] ← x

The running time of this procedure is θ(1) because there is a constant


number of boolean conditional checks and assignments independent of the
length of the linked list at the time of insertion.
The following is the routine for deleting an arbitrary node x from a linked
list.

(doubly)-linked list deletion(L, x)


1.if (prev[x] = nil)
2. then if (next[x] = nil)
3. then head[L] ← tail[L] ← nil
4. return x
5. prev[next[x]] ← nil
6. head[L] ← next[x]
7. return x
8.if (next[x] = nil)
9. then next[prev[x]] ← nil
10. tail[L] ← prev[x]
11. return x
12.next[prev[x]] ← next[x]
13.prev[next[x]] ← prev[x]
14.return x

The running time of this procedure, given a pointer to x is θ(1). If,


however, the list must be traversed in order to locate the element to be
deleted based on some criterion, then the running time could be as high as
θ(n).

2
List-Search (L, k)

1.x ← head[L]
2.while (x 6= nil&key[x] 6= k)
3. x ← next[x]
4.return x
The running time is proportional to the number of elements in the list upto
and including the element which matches the search key. It is in the worst-
case θ(n), where n is the number of elements in the list.

Multi-Search(L, k)

1.create T emp List


2.x ← head[L]
3.while (x 6= nil)
4. if (key[x] = k)
5. then x0 ← duplicate(x)
6. Insert(T empL ist, x0 )
7.x ← next[x]
8.return T emp List

Here the running time is always θ(n).


A stack is a linear list data structure which has the logical specification
that elements can only be added to the top (called push) and removed
also from the top (called pop). Thus stacks follow the Last-In-First-Out
(lifo) protocol for accessing elements or modifying. No element other than
the top-most may be accessed and access to even the top element happens
along with it being popped.
A queue is a linear list data structure which has the logical specification
that elements can only be at the rear (called enqueue) and removed also
from the rear (called dequeue). Thus stacks follow the First-In-First-Out
(fifo) protocol for accessing elements or modifying. No element other than
the top-most may be accessed and access to even the top element happens
along with it being dequeued.
When a function calls another function in a procedural programming
language, the calling function is suspended and resumes exactly when the
called process returns. It resumes execution at the instruction following
the point at which it transferred control. This information is stored in
the state configuration. The order in which various functions call or are
called by other functions is maintained in a system data structure called the

3
program stack and this ensures that the various functions execute in the
correct intended order.
Recursion is the scenario when a function calls itself (as opposed to
calling another function) with, possibly, different arguments. Recursion is
a powerful method to design algorithms for a problem which solves smaller
subproblems and uses their solutions to solve the initial bigger instance.
When the problem size becomes adequately small the problem is solved
directly and the recursion is said to bottom-out.
Iteration is the process of solving a problem by applying a similar idea
multiple times in a loop.
Recursion has the advantage of simplifying the algorithm design ideas in
comparison to the difficulty of visualisation associated with iteration. How-
ever iteration has the advantage of not needing the overhead of a program
stack which acrues with the use of recursion.
It is often a good idea to design an algorithm using recursion and then
systemmatically translating it to an iterative version.

Array-Search(A, k)

1.for (i = 1 ← n)
2. if (key[i] = k)
3. then return i

This procedure takes time θ(n) in the worst case.


Reversing a singly linked list can be done in θ(n) time using only three
extra memory locations of size the node of the list. There is a complicated
solution using only two temporary variables.

Reverse-Singly-Linked-List (L)

1.create prev, curr, next


2.prev ← nil
3.curr ← head[L]
4.next ← next[curr]
5.while (curr 6= nil)
6. next[curr] ← prev
7. prev ← curr
8. curr ← next
9. next ← next[next]
10.head[L] ← prev

4
Stacks and Queues implemented using linked lists.

PUSH(S, x)

1.Insert(L, x) (at tail)

POP(S)

1.return Delete(L, T ail[L])

ENQUEUE(Q, x)

1.Insert(L, x) (at tail)

DEQUEUE(Q)

1.return Delete(L, Head[L])

All of these operations take time θ(1), independent of the size of the
stack or queue.
Simulation a stack using two queues.
A stack can be simulated using two queues. Here in order to perform
the push operation we simply enqueue in queue Q1 . In order to pop,
we transfer all but the last element from queue Q1 to queue Q2 using
ENQUEUE(Q2 ,DEQUEUE(Q1 )). Further push operations take place
in Q2 until the next pop operation. Thus the queue changes everytime a
pop operation is performed. Thus the cost of a push is θ(1) while the cost
of a pop is θ(n).
Simulation of a queue using two stacks.
A queue can likewise be simulated using two stacks. Here, we use the
first stack to perform ENQUEUE and the second stack to perform DE-
QUEUE. In order to enqueue, we just push onto the first stack. When we
need to dequeue, we transfer all elements from stack S1 to stack S2 except
for the bottom most element and return the bottom most element. For
subsequent enqueues, we leave the elements in S2 untouched and continue
pushing onto S1 . Thus the front elements of the queue are in stack S2 with
head at the top and going down in the correct order. The rear portion of
the queue lies in stack S2 with the rear of the queue at the top and elements

5
in reverse. If at some stage we want to dequeue and stack S2 is empty,
then we transfer all elements from S1 to S2 . Thus enqueues take time θ(1).
dequeues take an average time of θ(1) but when the stack S2 is empty then
the dequeue takes time proportional to the number of elements on stack S1 .
Thus the dequeue operations are occasionally expensive, but on an average
are θ(1).
Stacks and queues implemented using arrays. Here we assign fixed
size arrays for these data structures and when the array becomes full it is
not possible to push/enqueue. When the array is empty, it is not possible
to pop/dequeue. This latter error condition must be checked even for the
linked list implementations. For the stack the top variable points to one
position above the top. Thus if the stack currently has k elements then the
variable top = k + 1. In the case of a queue we use modulo arithmetic to
keep track of the head and tail. This is because as the queue evolves both
the head and the tail move. The head moves during a dequeue while the
tail moves during an enqueue. On the other hand, in the case of the stock
the top keeps moving left and right while the other endpoint is invariant.
Error condition of stack is that top = 1 means dequeue is not possible
since stack is empty and top = n + 1 means push is not possible since stack
is full. Ina queue there are two ways of doing these error checks. Either we
keep a last operation variable and also check head and tail positions or keep
only head and tail positions and add a condition that in the array of size n
the queue may contain at most n − 1 elements.
We can perform manipulations of the fixed order of the elements in a
stack using two extra stacks. If we use only one extra stack it is impossible
to change the order. Transferring a set of elements from one stack to another
reverses their order and transferring again restores the original order. Thus
an odd number of transfers reverses while an even numb er restores. Using
this we can design algorithms to reverse a subportion of a stack using two
extra stacks. We can also similarly design routines to exchange two elements’
positions in a stack keeping the other elements in their original position,
using two extra stacks. We can also reach any permutation of the input
order using similar routines.
The above operations can also be performed for a queue of elements
using two extra queues. The operation of reversing a subqueue is much
more expensive than the corresponding operations for stacks.
A computational tree is a tree with nodes showing the possible paths
of computation of a program or a data structure’s evolution. Each node
represents a configuration. The root represents the initial configuration.
The children of any node in the tree represent all configurations reachable

6
in one unit step of computation from the current configuration.
Stacks can slso be used to transform expressions between the post-
fix, in-fix and pre-fix forms.

Вам также может понравиться