Sie sind auf Seite 1von 72

Class No.

32

Data Structures

http://ecomputernotes.com



Tables and Dictionaries
http://ecomputernotes.com
Tables: rows & columns of information
A table has several fields (types of information)
A telephone book may have fields name, address,
phone number
A user account table may have fields user id,
password, home folder
Name Address Phone
Sohail Aslam 50 Zahoor Elahi Rd, Gulberg-4, Lahore 576-3205
Imran Ahmad 30-T Phase-IV, LCCHS, Lahore 572-4409
Salman Akhtar 131-D Model Town, Lahore 784-3753
http://ecomputernotes.com
Tables: rows & columns of information
To find an entry in the table, you only need
know the contents of one of the fields (not
all of them).

This field is the key
In a telephone book, the key is usually name
In a user account table, the key is usually user
id
http://ecomputernotes.com
Tables: rows & columns of information
Ideally, a key uniquely identifies an entry
If the key is name and no two entries in the
telephone book have the same name, the key
uniquely identifies the entries
Name Address Phone
Sohail Aslam 50 Zahoor Elahi Rd, Gulberg-4, Lahore 576-3205
Imran Ahmad 30-T Phase-IV, LCCHS, Lahore 572-4409
Salman Akhtar 131-D Model Town, Lahore 784-3753
http://ecomputernotes.com
The Table ADT: operations
insert: given a key and an entry, inserts the entry
into the table

find: given a key, finds the entry associated with
the key

remove: given a key, finds the entry associated
with the key, and removes it


http://ecomputernotes.com
How should we implement a table?
How often are entries inserted and removed?
How many of the possible key values are likely to
be used?
What is the likely pattern of searching for keys?
E.g. Will most of the accesses be to just one or
two key values?
Is the table small enough to fit into memory?
How long will the table exist?
Our choice of representation for the Table ADT
depends on the answers to the following
http://ecomputernotes.com
TableNode: a key and its entry
For searching purposes, it is best to store
the key and the entry separately (even
though the keys value may be inside the
entry)
Saleem Saleem, 124 Hawkers Lane, 9675846
Yunus Yunus, 1 Apple Crescent, 0044 1970 622455
key entry
TableNode
http://ecomputernotes.com
Implementation 1: unsorted sequential array
An array in which TableNodes
are stored consecutively in
any order
insert: add to back of array;
(1)
find: search through the keys
one at a time, potentially all of
the keys; (n)
remove: find + replace
removed node with last node;
(n)

0


key entry
1
2
3
and so on
http://ecomputernotes.com
Implementation 2:sorted sequential array
An array in which TableNodes
are stored consecutively,
sorted by key
insert: add in sorted order; (n)
find: binary search; (log n)
remove: find, remove node
and shuffle down; (n)
0


key entry
1
2
3
We can use binary search because the
array elements are sorted
and so on
http://ecomputernotes.com
Searching an Array: Binary Search
Binary search is like looking up a phone number
or a word in the dictionary
Start in middle of book
If name you're looking for comes before names on
page, look in first half
Otherwise, look in second half
http://ecomputernotes.com
Binary Search



If ( value == middle element )
value is found
else if ( value < middle element )
search left-half of list with the same method
else
search right-half of list with the same method
http://ecomputernotes.com
Case 1: val == a[mid]
val = 10
low = 0, high = 8
5 7 9 10 13 17 19 1 27
1 2 3 4 5 6 7 0 8
a:
low high
Binary Search
mid
mid = (0 + 8) / 2 = 4
10
http://ecomputernotes.com
Case 2: val > a[mid]
val = 19
low = 0, high = 8
mid = (0 + 8) / 2 = 4
Binary Search -- Example 2
5 7 9 10 1 13 17 19 27
1 2 3 4 5 6 7 0 8
a:
mid
low high
new
low
new low = mid+1 = 5
13 17 19 27
http://ecomputernotes.com
Case 3: val < a[mid]
val = 7
low = 0, high = 8
mid = (0 + 8) / 2 = 4
Binary Search -- Example 3
10 13 17 19 5 7 9 1 27
1 2 3 4 5 6 7 0 8
a:
mid
low high new
high
new high = mid-1 = 3
5 7 9 1
http://ecomputernotes.com
val = 7
Binary Search -- Example 3 (cont)
5 7 9 10 13 17 19 1 27
1 2 3 4 5 6 7 0 8
a:
5 7 9 10 13 17 19 1 27
1 2 3 4 5 6 7 0 8
a:
5 7 9 10 13 17 19 1 27
1 2 3 4 5 6 7 0 8
a:
Binary Search C++ Code
int isPresent(int *arr, int val, int N)
{
int low = 0;
int high = N - 1;
int mid;
while ( low <= high ){
mid = ( low + high )/2;
if (arr[mid]== val)
return 1; // found!
else if (arr[mid] < val)
low = mid + 1;
else
high = mid - 1;
}
return 0; // not found
}

http://ecomputernotes.com
Binary Search: binary tree
The search divides a list into two small sub-
lists till a sub-list is no more divisible.
First half
First half
An entire sorted list
First half Second half
Second half
http://ecomputernotes.com
Binary Search Efficiency
After 1 bisection N/2 items
After 2 bisections N/4 = N/2
2
items


. . .
After i bisections N/2
i
=1 item

i = log
2
N
http://ecomputernotes.com
Implementation 3: linked list
TableNodes are again stored
consecutively (unsorted or
sorted)
insert: add to front; (1or n for
a sorted list)
find: search through
potentially all the keys, one at
a time; (n for unsorted or for
a sorted list
remove: find, remove using
pointer alterations; (n)
key entry
and so on
http://ecomputernotes.com
Implementation 4: Skip List
Overcome basic limitations of previous lists
Search and update require linear time
Fast Searching of Sorted Chain
Provide alternative to BST (binary search
trees) and related tree structures. Balancing
can be expensive.
Relatively recent data structure: Bill Pugh
proposed it in 1990.
http://ecomputernotes.com
Skip List Representation
Can do better than n comparisons to find
element in chain of length n
20 30 40 50 60
head tail
http://ecomputernotes.com
Skip List Representation
Example: n/2 + 1 if we keep pointer to
middle element
20 30 40 50 60
head tail
http://ecomputernotes.com
Higher Level Chains
For general n, level 0 chain includes all elements
level 1 every other element, level 2 chain every
fourth, etc.
level i, every 2
i

th element
40 50 60
head
tail
20 30 26 57
level 1&2 chains
http://ecomputernotes.com
Higher Level Chains
Skip list contains a hierarchy of chains
In general level i contains a subset of
elements in level i-1

40 50 60
head
tail
20 30 26 57
level 1&2 chains
Skip List: formally
A skip list for a set S of distinct (key, element)
items is a series of lists S
0
, S
1
, , S
h
such
that
Each list S
i
contains the special keys +
and
List S
0
contains the keys of S in
nondecreasing order

Each list is a subsequence of the
previous one, i.e.,
S
0
_

S
1
_

_ S
h
List S
h
contains only the two special keys
Lecture No.38
Data Structure

Dr. Sohail Aslam
Skip List: formally
56 64 78 + 31 34 44

12 23 26 S
0
64
+
31 34

23 S
1
+
31
S
2
+ S
3
Skip List: Search
We search for a key x as follows:
We start at the first position of the top list
At the current position p, we compare x
with y key(after(p))
x = y: we return element(after(p))
x > y: we scan forward
x < y: we drop down
If we try to drop down past the bottom list,
we return NO_SUCH_KEY
Skip List: Search
Example: search for 78
+
S
0
S
1
S
2
S
3
+
31

64
+
31 34

23
56 64 78 + 31 34 44

12 23 26
To insert an item (x, o) into a skip list, we
use a randomized algorithm:

We repeatedly toss a coin until we get tails,
and we denote with i the number of times the
coin came up heads
If i > h, we add to the skip list new lists S
h+1
,
, S
i +1
, each containing only the two special
keys
Skip List: Insertion
To insert an item (x, o) into a skip list, we
use a randomized algorithm: (cont)

We search for x in the skip list and find the
positions p
0
,

p
1
, , p
i
of the items with largest
key less than x in each list S
0
, S
1
, , S
i

For j 0, , i, we insert item (x, o) into list S
j

after position p
j
Skip List: Insertion
Example: insert key 15, with i = 2
Skip List: Insertion
+
10 36
+
23
23 +
S
0
S
1
S
2
+
S
0
S
1
S
2
S
3
+
10 36 23 15
+
15
+
23 15
p
0
p
1
p
2
Randomized Algorithms
A randomized algorithm performs coin tosses
(i.e., uses random bits) to control its execution
It contains statements of the type
b random()
if b <= 0.5 // head
do A
else // tail
do B
Its running time depends on the outcomes of the
coin tosses, i.e, head or tail

Skip List: Deletion
To remove an item with key x from a skip list,
we proceed as follows:
We search for x in the skip list and find the
positions p
0
,

p
1
, , p
i
of the items with key x,
where position p
j
is in list S
j
We remove positions p
0
,

p
1
, , p
i
from the lists
S
0
, S
1
, , S
i

We remove all but one list containing only the
two special keys
Skip List: Deletion
Example: remove key 34
+
45 12
+
23
23 +
S
0
S
1
S
2
+
S
0
S
1
S
2
S
3
+
45 12 23 34
+
34
+
23 34
p
0
p
1
p
2
Skip List: Implementation
+
S
0
S
1
S
2
S
3
+
45 12 23 34
+
34
+
23 34
Implementation: TowerNode
TowerNode will have array of next pointers.
Actual number of next pointers will be
decided by the random procedure.
Define MAXLEVEL as an upper limit on
number of levels in a node.
40 50 60
head
tail
20 30 26 57
Tower Node
Implementation: QuadNode
A quad-node stores:
item
link to the node before
link to the node after
link to the node below
link to the node above
This will require copying the
key (jitem) at different levels
x
quad-node

Skip Lists with Quad Nodes
56 64 78 + 31 34 44

12 23 26
+
+
31

64
+
31 34

23
S
0
S
1
S
2
S
3
Performance of Skip Lists
In a skip list with n items
The expected space used is proportional
to n.
The expected search, insertion and
deletion time is proportional to log n.
Skip lists are fast and simple to implement
in practice
Implementation 5: AVL tree
An AVL tree, ordered by key
insert: a standard insert; (log n)
find: a standard find (without
removing, of course); (log n)
remove: a standard remove;
(log n)
key entry
key entry key entry
key entry
and so on
Anything better?
So far we have find, remove and insert
where time varies between constant logn.

It would be nice to have all three as
constant time operations!

An array in which
TableNodes are not stored
consecutively
Their place of storage is
calculated using the key and
a hash function



Keys and entries are
scattered throughout the
array.
Implementation 6: Hashing
key entry
Key
hash
function
array
index
4
10
123
insert: calculate place of
storage, insert
TableNode; (1)
find: calculate place of
storage, retrieve entry;
(1)
remove: calculate place
of storage, set it to null;
(1)
Hashing
key entry
4
10
123
All are constant time (1) !
Hashing
We use an array of some fixed size T to
hold the data. T is typically prime.

Each key is mapped into some number
in the range 0 to T-1 using a hash
function, which ideally should be
efficient to compute.
Example: fruits
Suppose our hash function
gave us the following
values:
hashCode("apple") = 5
hashCode("watermelon") = 3
hashCode("grapes") = 8
hashCode("cantaloupe") = 7
hashCode("kiwi") = 0
hashCode("strawberry") = 9
hashCode("mango") = 6
hashCode("banana") = 2
kiwi
banana
watermelon
apple
mango
cantaloupe
grapes
strawberry
0
1
2
3
4
5
6
7
8
9
Example
Store data in a table
array:
table[5] = "apple"
table[3] = "watermelon"
table[8] = "grapes"
table[7] = "cantaloupe"
table[0] = "kiwi"
table[9] = "strawberry"
table[6] = "mango"
table[2] = "banana"
kiwi
banana
watermelon
apple
mango
cantaloupe
grapes
strawberry
0
1
2
3
4
5
6
7
8
9
Example
Associative array:
table["apple"]
table["watermelon"]
table["grapes"]
table["cantaloupe"]
table["kiwi"]
table["strawberry"]
table["mango"]
table["banana"]
kiwi
banana
watermelon
apple
mango
cantaloupe
grapes
strawberry
0
1
2
3
4
5
6
7
8
9
Example Hash Functions
If the keys are strings the hash function is
some function of the characters in the
strings.
One possibility is to simply add the ASCII
values of the characters:
TableSize ABC h Example
TableSize i str str h
length
i
)% 67 66 65 ( ) ( :
% ] [ ) (
1
0
+ + =
|
|
.
|


\
|
=


=
Finding the hash function
int hashCode( char* s )
{
int i, sum;
sum = 0;
for(i=0; i < strlen(s); i++ )
sum = sum + s[i]; // ascii value
return sum % TABLESIZE;
}
Example Hash Functions
Another possibility is to convert the string
into some number in some arbitrary base b
(b also might be a prime number):
T b b b ABC h Example
T b i str str h
length
i
i
)% 67 66 65 ( ) ( :
% ] [ ) (
2 1 0
1
0
+ + =
|
|
.
|


\
|
=


=
Example Hash Functions
If the keys are integers then key%T is
generally a good hash function, unless the
data has some undesirable features.
For example, if T = 10 and all keys end in
zeros, then key%T = 0 for all keys.
In general, to avoid situations like this, T
should be a prime number.
Collision
Suppose our hash function gave us
the following values:
hash("apple") = 5
hash("watermelon") = 3
hash("grapes") = 8
hash("cantaloupe") = 7
hash("kiwi") = 0
hash("strawberry") = 9
hash("mango") = 6
hash("banana") = 2

kiwi
banana
watermelon
apple
mango
cantaloupe
grapes
strawberry
0
1
2
3
4
5
6
7
8
9
Now what?
hash("honeydew") = 6
Collision
When two values hash to the same array
location, this is called a collision
Collisions are normally treated as first
come, first servedthe first value that
hashes to the location gets it
We have to find something to do with the
second and subsequent values that hash to
this same location.
Solution for Handling collisions
Solution #1: Search from there for an empty
location
Can stop searching when we find the
value or an empty location.
Search must be wrap-around at the end.
Solution for Handling collisions
Solution #2: Use a second hash function
...and a third, and a fourth, and a fifth, ...
Solution for Handling collisions
Solution #3: Use the array location as the
header of a linked list of values that hash to
this location
Solution 1: Open Addressing
This approach of handling collisions is
called open addressing; it is also known
as closed hashing.
More formally, cells at h
0
(x), h
1
(x), h
2
(x),
are tried in succession where

h
i
(x) = (hash(x) + f(i)) mod TableSize,
with f(0) = 0.
The function, f, is the collision resolution
strategy.
Linear Probing
We use f(i) = i, i.e., f is a linear function
of i. Thus

location(x) = (hash(x) + i) mod TableSize

The collision resolution strategy is called
linear probing because it scans the array
sequentially (with wrap around) in search
of an empty cell.
Linear Probing: insert
Suppose we want to add
seagull to this hash table
Also suppose:
hashCode(seagull) = 143
table[143] is not empty
table[143] != seagull
table[144] is not empty
table[144] != seagull
table[145] is empty
Therefore, put seagull at
location 145
robin
sparrow
hawk
bluejay
owl
. . .
141
142
143
144
145
146
147
148
. . .
seagull
Linear Probing: insert
Suppose you want to add
hawk to this hash table
Also suppose
hashCode(hawk) = 143
table[143] is not empty
table[143] != hawk
table[144] is not empty
table[144] == hawk
hawk is already in the
table, so do nothing.
robin
sparrow
hawk
seagull
bluejay
owl
. . .
141
142
143
144
145
146
147
148
. . .
Linear Probing: insert
Suppose:
You want to add cardinal to
this hash table
hashCode(cardinal) = 147
The last location is 148
147 and 148 are occupied
Solution:
Treat the table as circular;
after 148 comes 0
Hence, cardinal goes in
location 0 (or 1, or 2, or ...)
robin
sparrow
hawk
seagull
bluejay
owl
. . .
141
142
143
144
145
146
147
148

Linear Probing: find
Suppose we want to find
hawk in this hash table
We proceed as follows:
hashCode(hawk) = 143
table[143] is not empty
table[143] != hawk
table[144] is not empty
table[144] == hawk (found!)
We use the same
procedure for looking
things up in the table as
we do for inserting them
robin
sparrow
hawk
seagull
bluejay
owl
. . .
141
142
143
144
145
146
147
148
. . .
Linear Probing and Deletion
If an item is placed in array[hash(key)+4],
then the item just before it is deleted
How will probe determine that the hole does not
indicate the item is not in the array?
Have three states for each location
Occupied
Empty (never used)
Deleted (previously used)
Clustering
One problem with linear probing
technique is the tendency to form
clusters.
A cluster is a group of items not
containing any open slots
The bigger a cluster gets, the more likely
it is that new values will hash into the
cluster, and make it ever bigger.
Clusters cause efficiency to degrade.
Quadratic Probing
Quadratic probing uses different formula:
Use F(i) = i
2
to resolve collisions
If hash function resolves to H and a search in cell
H is inconclusive, try H + 1
2
, H + 2
2
, H + 3
2
,
Probe
array[hash(key)+1
2
], then
array[hash(key)+2
2
], then
array[hash(key)+3
2
], and so on
Virtually eliminates primary clusters
Collision resolution: chaining
Each table position is a
linked list
Add the keys and
entries anywhere in the
list (front easiest)
4
10
123
key entry key entry
key entry key entry
key entry
No need to change position!
Collision resolution: chaining
Advantages over open
addressing:
Simpler insertion and
removal
Array size is not a
limitation
Disadvantage
Memory overhead is
large if entries are small.
4
10
123
key entry key entry
key entry key entry
key entry
Applications of Hashing
Compilers use hash tables to keep track of
declared variables (symbol table).

A hash table can be used for on-line
spelling checkers if misspelling detection
(rather than correction) is important, an
entire dictionary can be hashed and words
checked in constant time.
Applications of Hashing
Game playing programs use hash tables to
store seen positions, thereby saving
computation time if the position is
encountered again.

Hash functions can be used to quickly
check for inequality if two elements hash
to different values they must be different.

When is hashing suitable?
Hash tables are very good if there is a need for
many searches in a reasonably stable table.
Hash tables are not so good if there are many
insertions and deletions, or if table traversals are
needed in this case, AVL trees are better.
Also, hashing is very slow for any operations
which require the entries to be sorted
e.g. Find the minimum key

Das könnte Ihnen auch gefallen