Equivalence Classes

Linked List Containers
Useful Linked List Add-Ons

Are there new variables/changes to the lists as they have
been defined that could make our jobs as programmers
easier?

Size member variable



Be able to determine current size of list, bound positions for
add, delete, etc.
Maintaining a variable is cheap, counting the nodes every time
size is called (the alternative) is not!
Tail pointer

Significantly simplifies addition at end of list
Tail Pointers



If doing a lot of adds to the end (which is
natural), a tail pointer provides instant access to
the last node
Does require some extra overhead in the
add/delete functions to keep tail updated
List Construction: first=tail=0; 1-node: first != 0
and first == tail
first
List1 Data1
tail
List1 Data2
List1 Data3
Circular Lists

Another improvement on LinkedLists:


Use link pointer of last node to point to the first
node.
Changes to implementation:

Check to see if at last node:
test “if (current -> link == first)”
instead of “if (current->link == 0)”

Insertion and deletion need to preserve property
that last nodes link is always set equal to first
Circular List Implementation
Retrofitting our LinkedList to make circular:
 Don’t want to have just a head pointer: Why?




If inserting at tail, have to traverse the whole list from head
to get to tail to update the tails link
If inserting at head, need to find last node to point its link to
new head - this also traverses the whole list
Circular linked lists are much more efficient if use tail
(last) pointer as the pointer for the list.
Two pointers (head and tail) are actually overkill .. why?
Circular Linked Lists
first
List1 Data1
tail
List1 Data2
List1 Data3
Can access head via tail->link
in one step so don’t need head
pointer
Circular Linked List
void insertAtFront(ListNode <Type> *x)
{
if (tail == 0) { // empty list,
tail = x; x-> link = x; // point to yourself
}
else
{
x->link = tail->link; // point new head link to old head
tail->link = x; // point tail to new head
}
}
insertAtRear() only requires adding tail = x in the else statement
(to update rear to new node)
Linked List Examples

Now that the basic structures of LinkedLists
have been defined, what are some potential
applications?
Target problems where can take advantage of
LinkedList memory usage model
 Target problems where can take advantage of
dynamic aspects of LinkedList

Linked List Example

Polynomials
Interested in representing and manipulating
polynomials
 Polynomial defined as:



Y = coef_n * xexp_n + coef_n-1 * xexp_n-1 + … coef_0 * x0
Examples:
3x14 + 2x8 + 1
8x14 – 3x10 + 10x6
Polynomials

Has a very nice linked representation

In particular, able to take advantage of sparse number of
coefficients with a linked representation

Compare against an array that holds the coefficients and
exponents for all possible indices from 0 to max degree


Horribly wasteful in terms of space
What if we had an array of Term objects, where a Term was
an exponent/coefficient

Only need enough array space to hold true terms, so amount of
space required ok.. However, we’ll see this is still inflexible.
Polynomials

For each component of the polynomial, need to store
coefficient and exponent
struct Term
{
int coef;
int exp;
void Init(int c, int e) { coef = c; exp = e;}
};
Polynomials

Polynomial itself implemented by a templated
LinkedList of Terms
class Polynomial
{
private:
LinkedList<Term> poly;
};
Polynomials

Adding Polynomials:
3x3 + 2x + 1
+
2x3 + 3x2 + 5
=================
5x3 + 3x2 + 2x + 6
Polynomials

Adding Polynomials:
Iterate through both lists
 If exponent1 == exponent2,

CoefficientSum = Coefficient1 + Coefficient2
 If CoefficentSum != 0, add term to new polynomial
representing answer


Else,
Find higher exponent
 Add term to new polynomial representing answer

Polynomials
5
3
3
2
1
1
0
2
3
3
2
5
0
3
3
2
2
1
6
0
Polynomials

The “Answer” polynomial is where LinkedLists
have another win. There is now way beforehand
to know how may terms there will be in the
answer polynomial
Number of terms is anywhere from (the size of the
largest list) to the size of the largest list plus the size
of the smallest list)
 With an array representation, would have to
overallocate to handle large answers
 With LinkedLists, just grab a new Node when need
it.

Polynomials


Overload + operator to perform polynomial
addition
Implementation in page 193
Example: Equivalence Classes

Another Example of Linked List Usage:
Finding Equivalence Classes
Definition:
Given a relation (<, >, ==, …),
the relation is an equivalence relation over a set S
if the relation is reflexive, symmetric, and
transitive over S

Equivalence Relations
Reflexive
A?A
Is < Reflexive?
Is = Reflexive?
 Symmetric:
If A ? B, then B ? A
Is < Symmetric?
Is = Symmetric?

A<A
A == A
No
Yes
A < B, then B < A No
A = B, then B = A Yes
Equivalence Relations
Transitive
If A ? B and B ? C, then A ? C
Is < transitive?
A < B, B < C, A < C Yes
Is = transitive?
A = B, B = C, A = C Yes

= is reflexive, symmetric, and transitive, so it is an
equivalence relation
< fails on reflexive and symmetric, so it is not an
equivalence relation
Equivalence Classes

Effect of equivalence relations is the ability to
partition a set S into classes such that any two
members of S, x and y, are in the same class if x
equiv y.

Classes are called equivalence classes
Equivalence Classes
Equivalence Class Example:
Let our relationship be = mod 3
Reflexive
5 mod 3 = 5 mod 3 => 2 = 2 Yes
Symmetric
5 mod 3 = 8 mod 3, then 8 mod 3 = 5 mod 3
2 = 2, then 2 = 2 Yes
Transitive
5 mod 3 = 8 mod 3, 8 mod 3 = 14 mod 3, then 5 mod 3 = 14
mod 3
2 = 2, 2 = 2, then 2 = 2, Yes
Equivalence Classes

Equivalence Classes:

All numbers that are = mod 3 are in the same
equivalence class
Mod 3 = 0
{0,3,6,9,…}
Mod 3 = 1
{1,4,7,10,…}
Mod 3 = 2
{2,5,8,11,…}
Equivalence Classes

Goal:

Given a list of equivalence relations between items x
and y, construct the equivalence classes
Relations: 0 = 4, 3 = 1, 6 = 10, 8 = 9, 7 = 4, 6 =
8, 3 = 5, 2= 11, 11 = 0
Classes: {0,2,4,7,11}, {1,3,5,}, {6,8,9,10}
Equivalence Classes



Could build N by N (N = number of total items)
matrix indicating relationship, but most of entries are
likely to be zero
Instead, use an array of pointers for 1-dimensional list
of all items, where for each item, the pointer points to a
list of all items that are equivalent in the input
For each item I = J input,
Add J to I’s list
Add I to J’s list
Equivalence Classes
0
1
2
3
4
5
6
7
8
9
10
11
11
3
11
5
7
3
8
4
6
8
6
0
1
0
4
10
9
2
Equivalence Classes

To find equivalence classes,
Start at front of array
 Print X, Mark as printed
 Print all things equivalent to X (follow its list), mark
as printed
 For each thing equivalent to X, print the appropriate
list

Equivalence Classes

Include an array to indicate whether that data
has already been written:
boolean out[n] initially all set to false

Code on pages 205, 206 of book
Equivalence Classes

First Steps of Test Run on Previous Data
Output
Stack
Out Array
FFFFFFFFFFFF
NC: 0
TFFFFFFFFFFF
NC: 0,11
Top->11
TFFFFFFFFFFT
NC: 0,11,4
Top->4,11
TFFFTFFFFFFT
NC: 0,11,4,7
Top->7,11
TFFFTFFTFFFT
Complexity of
Equivalence Class Operations
Initialization
Initializing sequence and output arrays with n possible values: O(n)
Processing each pair of input – 2 steps * m inputs: O(m)
So, pre-processing requires: O(n+m)

Traversing list:
n possible lists to look at, 2m entries total on the lists
Only process list if haven’t already written
So only looks at each entry in the array once (upper bound of n)
Since only looks at each array once, only looks at nodes underneath
the array entry once (upper bound of 2m)
So traverse time is O(n+m)

Doubly Linked Lists

Biggest problem with linked lists as we’ve used
so far:
Can only navigate in one direction
 Requires traversals from front to a position, even if
currently located right behind where want to be
 Requires “trailing pointer” if want to easily get
access to previous node for current node


When update current to current->next, update prev to
current
Doubly Linked Lists

Work around these issues by storing both the
left and right side neighbors of a linked list

Makes add, delete, and other array manipulation
operators more complicated as have to preserve
doubly linked property
Doubly Linked Lists

Definition:
class DblList; // forward declaration
class DblListNode
{
friend class DblList;
private:
int data;
DblListNode *right, *left;
};
Doubly Linked Lists
class DblList
{
public:
// list manipulation
private:
DblListNode* first;
};
Doubly Linked Lists
Example Node
LEFT
DATA
RIGHT
3 Node Circular
Doubly Linked List
LEFT
LEFT
10
RIGHT
15
LEFT
RIGHT
29
RIGHT
Doubly Linked List

Note that, given a pointer p,
p = p->left->right = p->right->left

Going back and forth is equally easy.
Doubly Linked List
void DblList::Insert(DblListNode *new,
DblListNode *current)
{
// insert new after x
new->left = current;
new->right = current->right;
current->right->left = new;
new
current->right = new;
}
L
11
current
L
10
R
L
12
R
R
Doubly Linked List
void DblList::Delete(DblListNode
*toDelete)
{
// delete node pointed to by toDelete
toDelete->left->right = toDelete->right;
toDelete->right->left = toDelete->left;
delete toDelete;
toDelete
}
L
10
L
11
R
L
12
R