Vtu 3RD Sem Cse Data Structures With C Notes 10CS35
Vtu 3RD Sem Cse Data Structures With C Notes 10CS35
Vtu 3RD Sem Cse Data Structures With C Notes 10CS35
I.A. Marks : 25
Exam Hours: 03
Exam Marks: 100
PART A
UNIT - 1
8 Hours
BASIC CONCEPTS: Pointers and Dynamic Memory Allocation, Algorithm Specification, Data Abstraction,
Performance Analysis, Performance Measurement
UNIT - 2
6 Hours
ARRAYS and STRUCTURES: Arrays, Dynamically Allocated Arrays, Structures and Unions, Polynomials,
Sparse Matrices, Representation of Multidimensional Arrays
UNIT - 3
6 Hours
STACKS AND QUEUES: Stacks, Stacks Using Dynamic Arrays, Queues, Circular Queues Using Dynamic
Arrays, Evaluation of Expressions, Multiple Stacks and Queues.
UNIT - 4
6 Hours
LINKED LISTS: Singly Linked lists and Chains, Representing Chains in C, Linked Stacks and Queues,
Polynomials, Additional List operations, Sparse Matrices, Doubly Linked Lists
PART - B
UNIT - 5
TREES 1: Introduction, Binary Trees, Binary Tree Traversals, Threaded Binary Trees, Heaps.
6 Hours
UNIT - 6
6 Hours
TREES 2, GRAPHS: Binary Search Trees, Selection Trees, Forests, Representation of Disjoint Sets, Counting
Binary Trees, The Graph Abstract Data Type.
UNIT - 7
6 Hours
PRIORITY QUEUES Single- and Double-Ended Priority Queues, Leftist Trees, Binomial Heaps, Fibonacci
Heaps, Pairing Heaps.
UNIT - 8
8 Hours
EFFICIENT BINARY SEARCH TREES: Optimal Binary Search Trees, AVL Trees, Red-Black Trees, Splay
Trees.
Text Book:
1. Horowitz, Sahni, Anderson-Freed: Fundamentals of Data Structures in C, 2nd Edition, University Press,
2007.
(Chapters 1, 2.1 to 2.6, 3, 4, 5.1 to 5.3, 5.5 to 5.11, 6.1, 9.1 to 9.5, 10)
TABLE OF CONTENTS
UNIT 1: BASIC CONCETPS
1-12
13-26
27-36
UNIT 5: TREES
37-50
51-62
NULL POINTER
The null pointer points to no object or function.
i.e. it does not point to any part of the memory.
if(p==NULL)
printf("p does not point to any memory");
else
printf("access the value of p");
Each one of us views the world through the lens of our personal context, which has been shaped by the unique
experiences of our lives.
1
If we frequently allocate the memory space, then it is better to define a macro as shown below:
#define MALLOC(p,s)
if(!((p)==malloc(s)))
{
printf("insufficient memory");
exit(0);
}
\
\
\
\
\
3) Pointers have same size as data type 'int'. Since int is the default type specifier, some programmers
omit return type when defining a function. The return type defaults to int which can later be
interpreted as a pointer. Therefore, programmer has to define explicit return types for functions.
There is a giant asleep within everyone. When that giant awakens, miracles happen.
2
Happiness isn't a place you get to, it's an inner state you create. Anyone can be happy, it's available to everyone &
is available right now.
3
2) Indirect recursion: A function which contains a call to another function which in turn calls
another function and so on and eventually calls the first function.
For Ex,
void f1()
{
....
f2();
}
void f2()
{
....
f3();
}
void f3()
{
....
f1();
}
The level of thinking that got you to where you now are, will not get you to where you dream of being.
5
// Sabc(I) = 0
In above program, there is no variable space requirement. This has only fixed space requirement ie
Ssum(I) = 0 . However, if same program is expressed recursively, then it is as shown below.
float rsum(int list[], int n)
{
if(n) return rsum(list,n-1)+list[n-1];
return 0;
}
Program 1.10: Recursive function for summing a list of numbers
Space needed for one recursive call for above program is given below
Type
parameter:float
parameter:integer
return address:
TOTAL
Name
list[]
n
per recursive call
Number of bytes
2
2
2
6
Ssum(I)= Ssum(n)=6n
For your life to be great, your faith must be bigger than your fears.
6
Program 1.11: Program for summing a list of numbers with count statements
Since we are interested in only the final count, we can eliminate the computations of sum in above
program as shown below:
The best case step count is the minimum number of steps that can be executed for the given
parameters.
The worst-case step count is the maximum number of steps that can be executed for the given
parameters.
The average step count is the average number of steps executed on instances with the
given parameters.
The man who succeeds above his fellows is the one who early in life clearly discerns his object and towards that object
habitually directs his powers.
8
When you align your outer world with your inner world, the universe throws its winds beneath your wings & sends you
more of its treasures.
9
The sad times improves us & lead us to the good, while the good times show us the fullness of our possibilities and
offer us the blessings to be appreciated.
10
To live your highest life, the trick is to have the heart & the mind working together in harmony.
11
If we keep doing things the same way, we're only going to see the same results.
13
//array of 5 integers
//array of 5 pointers to integers
Compiler allocates 5 consecutive memory-locations for each of the variables 'list' and 'plist'.
Address of first element list[0] is called base-address.
Memory-address of list[i] can be computed by compiler as
+i*sizeof(int)
where =base address
void print1(int *ptr, int rows)
{
/* print out a one-dimensional array using a pointer */
int i;
printf(Address Contents\n);
for (i=0; i < rows; i++)
printf(%8u%5d\n, ptr+i, *(ptr+i));
printf(\n);
}
void main()
{
int one[] = {0, 1, 2, 3, 4};
print1(&one[0], 5)
}
Program 2.2:Program to print both address of ith element of given array & the value found at that address(Fig 2.1)
For every finger we point at another, we have three pointing back at us.
14
The above code would allocate an array of exactly the required size and hence would not result in
any wastage.
TWO DIMENSIONAL ARRAYS
These are created by using the concept of array of arrays.
A 2-dimensional array is represented as a 1-dimensional array in which each element has a pointer
to a 1-dimensional array as shown below
int x[5][7];
The people who endure more pain & suffering are meant for greater things on the planet.
15
To create clean and readable programs, a CALLOC macro can be created as shown below:
#define CALLOC(p,n,s)
if((p=calloc(n,s))==NULL)
{
printf("insufficient memory");
exit(1);
}
\
\
\
\
REALLOC
These functions resize memory previously allocated by either malloc or calloc.
For example,
realloc(p,s); //this changes the size of memory-block pointed at by p to s.
When s>oldSize, the additional s-oldSize have an unspecified value and
when s<oldSize, the rightmost oldSize-s bytes of old block are freed.
On successful resizing, it returns a pointer to the start of the new block.
On failure, it returns the value NULL.
To create clean and readable programs, the REALLOC macro can be created as shown below
#define REALLOC(p,s)
if((p=realloc(p,s))==NULL)
{
printf("insufficient memory");
exit(0);
}
\
\
\
\
\
One of the organizing principles that any great life is built around is this one: 'Simplify, simplify, simplify.'
16
Dot operator(.) is used to access a particular member of the structure. For ex,
person.age=10;
person.salary=35000;
strcpy(person.name,"james");
Structures cannot be directly checked for equality or inequality. So, we can write a function to do
this.
if(humansEqual(person1,person2))
printf("two human beings are same");
else
printf("two human beings are different");
---------------------------------------------------------------------int humansEqual(humanBeing person1,humanBeing person2)
{
if(strcmp(person1.name,person2.name))
return 0;
if(person1.age!=person2.salary)
return 0;
if(person.salary!=person2.salary)
return 0;
return 1;
}
struct
month;
day;
year;
typedef struct
{
char name[10];
int age;
float salary;
data dob;
}humanBeing;
We have forty million reasons for failure, but not a single excuse.
17
UNION
This is similar to a structure, but the fields of a union must share their memory space. This means
that only one field of the union is "active" at any given time. For ex,
typedef struct
{
enum tagField{female,male} sex;
typedef union
{
int children;
int beard;
}u;
}sexType;
typedef struct
{
char name[10];
int age;
float salary;
date dob;
sexType sexInfo;
}humanBeing;
humanBeing person1,person2;
When you shift from a compulsion to survive toward a commitment to serve others, your life cannot help but explode
into success.
18
Life will bring you pain all by itself. Your responsibility is to create joy.
19
startA & startB give the index of first term of A and B respectively (Figure 2.3b).
finishA & finishB give the index of the last term of A & B respectively
avail gives the index of next free location in the array.
Any polynomial A that has n non-zero terms has startA & finishA such that finishA=startA+n-1
Advantage: This representation solves the problem of many 0 terms since A(x)-2x1000+1 uses only 6
units of storage (one for startA, one for finishA, 2 for the coefficients and 2 for the exponents)
Disadvantage: However, when all the terms are non-zero, the current representation requires about
twice as much space as the first one.
When you know what's most important in your life, then you can be more selective about what you do & what you don't do.
21
ANALYSIS
Let m and n be the number of non-zero terms in A and B respectively.
If m>0 and n>0, the while loop is entered.
At each iteration, we increment the value of startA or startB or both.
Since the iteration terminates when either startA or startB exceeds finishA or finishB respectively, the
number of iterations is bounded by m+n-1.
This worst case occurs when
A(x)= x2i and B(x)=x2i+1
The asymptotic computing time of this algorithm is O(n+m)
Free flow is a state of living where you have present moment awareness. Every cell within you is engaged in the
moment you're living.
23
The world will be either better or worse depending on whether we become better or worse.
24
}
}
/* end of for j <= totalb+1 */
for (; a[i].row == row; i++);
row_begin = i; row = a[i].row;
}
/* end of for i <=totala */
d[0].row = rows_a;
d[0].col = cols_b; d[0].value = totald;
Praogram 2.9: Sparse matrix multiplication
Life's all about striking a balance: Get the head & the heart working as team-mates, as life partners.
25
The easiest things to do are also the things that are easiest not to do.
26
SYSTEM STACK
A stack used by a program at run-time to process function-calls is called system-stack (Figure 3.2).
When functions are invoked, programs
create a stack-frame(or activation-record) &
place the stack-frame on top of system-stack
Initially, stack-frame for invoked-function contains only
pointer to previous stack-frame &
return-address
The previous stack-frame pointer points to the stack-frame of the invoking-function
while return-address contains the location of the statement to be executed after the
function terminates.
If one function invokes another function, local variables and parameters of the invoking-function are
added to its stack-frame.
A new stack-frame is then
created for the invoked-function &
placed on top of the system-stack
When this function terminates, its stack-frame is removed (and processing of the invoking-function,
which is again on top of the stack, continues).
Frame-pointer(fp) is a pointer to the current stack-frame.
The most pathetic person in the world is someone who has sight but has no vision.
27
Function push() checks to see if the stack is full. If it is, it calls stackFull, which prints an error
message and terminates execution.
When the stack is not full, we increment top and assign item to stack[top].
Most people die with the great song of their lives remaining unsung.
28
Once the stack is full, realloc() function is used to increase the size of array.
In array-doubling, we double array-capacity whenever it becomes necessary to increase the capacity
of an array.
ANALYSIS
In worst case, the realloc function needs to
allocate 2*capacity*sizeof(*stack) bytes of memory and
copy capacity*sizeof(*stack) bytes of memory from the old array into the new one.
The total time spent over all array doublings = O(2k) where capacity=2k
Since the total number of pushes is more than 2k-1, the total time spend in array doubling is O(n)
where n=total number of pushes.
Without personal accountability, it's easy to escape from your commitment to making the dream happen.
29
structure Queue is
objects: a finite ordered list with zero or more elements.
functions:
for all queue Queue, item element, max_ queue_ size positive integer
Queue CreateQ(max_queue_size) ::=
create an empty queue whose maximum size is max_queue_size
Boolean IsFullQ(queue, max_queue_size) ::=
if(number of elements in queue == max_queue_size)
return TRUE
else
return FALSE
Queue AddQ(queue, item) ::=
if (IsFullQ(queue))
queue_full
else
insert item at rear of queue and return queue
Boolean IsEmptyQ(queue) ::=
if (queue ==CreateQ(max_queue_size))
return TRUE
else
return FALSE
Element DeleteQ(queue) ::=
if (IsEmptyQ(queue))
return
else
remove and return the item at front of queue
ADT 3.2: Abstract data type Queue
element queue[MAX_QUEUE_SIZE];
int rear=-1;
int front=-1;
Boolean IsEmptyQ(queue)::= front==rear;
Boolean IsFullQ(queue)::= rear=MAX_QUEUE_SIZE-1;
void addq(int rear, element item)
{
if (rear == MAX_QUEUE_SIZE_1)
{
queue_full( );
return;
}
queue [++rear] = item;
}
Program 3.5: Add to a queue
The place where your greatest fears live is also the place where your greatest growth lies.
30
CIRCULAR QUEUE
In a circular-queue, the elements are arranged implicitly in a circle (Figure 3.7).
When the array is viewed as a circle, each array position has a next and a previous position.
The law of attraction says that we attract into our life that which we focus on.
31
To get a proper circular queue configuration, we must slide the elements in the right segment to the
right end of the array (Figure 3.8d).
The array doubling and the slide to the right together copy at most 2*capacity-2 elements.
The number of elements copied can be limited to capacity-1 by customizing the array doubling code
so as to obtain the configuration of Figure3.8e. This configuration may be obtained as follows:
1) Create a new array newQueue of twice the capacity.
2) Copy the second segment to positions in newQueue begining at 0.
3) Copy the first segment to positions in newQueue begining at capacity-front-1.
Too many people are leaving the quality of their futures to chance rather than to choice.
32
INFIX TO POSTFIX
Algorithm for producing a postfix expression from an infix is as follows (Figure 3.14):
1) Fully parenthesize the expression
2) Move all binary operators so that they replace their corresponding right parentheses
3) Delete all parentheses
For ex, a/b-c+d*e-a*c when fully parenthesized becomes:
((((a/b)-c)+(d*e))-a*c))
Performing steps 2 and 3 gives
ab/c-de*+ac* This algorithm is inefficient on a computer because it requires 2 passes.
The first pass reads the expression and parenthesizes it
while the second pass moves the operators.
Since the order of operands is same in infix and postfix, we can form the postfix equivalent by
scanning the infix expression left-to-right.
During this scan, operands are passed to the output expression as they are encountered.
However, the order in which the operators are output depends on their precedence (Figure 3.12).
Since we must output the higher precedence operators first, we save operators until we know their
correct placement. A stack is one way of doing this.
Example 3.3[Simple expression]: Consider simple expression a+b*c which yields abc*+ in postfix.
The operands are output immediately, but the two operators need to be reversed (Figure 3.15):
In general, operators with higher precedence must be output before those with lower precedence.
Therefore, we stack operators as long as the precedence of the operator at the top of the stack is less
than the precedence of the incoming operator.
You are the creator of the moods you experience, moods that you can change in a single instant.
33
/* size of memory */
/* max number of stacks plus 1 */
/* number of stacks entered by the user */
To divide the array into equal segment, we use the following code:
top[0]=boundary[0]=-1;
for(j=1;j<n;j++)
top[j]=boundary[j]=(MEMORY_SIZE/n)*j;
boundary[n]=MEMORY_SIZE-1;
Figure 3.19:Configuration when stack i meets stack i+1,but the memory is not full
In push function, top[i]==boundary[i+1] condition implies only that a particular stack ran out of
memory, not that the entire memory is full.(In fact, there may be a lot of unused space between other
stacks in array memory).
Therefore, we create an error recovery function, stackFull, which determines if there is any free
space in memory.
If there is space available, it should shift the stacks so that space is allocated to the full stack.
We can guarantee that stackFull adds elements as long as there is free space in array memory if we:
1) Determine the least j, i<j<n such that there is free space between stacks j and j+1 i.e.
top[j]<boundary[j+1]. If there is such a j, then move stacks i+1,i+2 . . . . . .j one position to
the right. This creates a space between stacks i and i+1.
1) If there is no j as in (i),then look to the left of stack i. Find the largest j such that 0<=j<i
and there is space between stacks j and j+1 i.e. top[j]<boundary[j+1]. If there is such a j, then
move stacks j+1,j+2. . . . . .i one space to the left. This also creates a space between stacks i
and i+1.
3) If there is no j satisfying either (i) or (ii),then all MEMORY_SIZE spaces of memory are
utilized and there is no free space. In this case, stackFull terminates with an error message.
To maintain a health level of optimism & passion for life, you must keep on setting higher & higher goals.
36
UNIT 5: TREES
TREE
This is a finite set of one or more nodes such that
1) There is a specially designated node called root.
2) Remaining nodes are partitioned into disjoint sets T 1, T2. . . . . Tn where each of these are
called subtrees of root(Figure 5.2).
Consider the tree shown below
To get the best from life, you must be completely present & mindful in every minute of every hour of every day.
37
For a tree of degree k, we can use the node-structure as shown below (Figure 5.4).
As you live your days, so you will live your life. You change your life the second you make a decision from the depths of
your heart to be a better, more dedicated human being.
38
Our lives can change when we become more aware of all the positives we live with.
40
Advantage: For complete binary tree, array representation is ideal, as no space is wasted.
Disadvantage: For skewed tree, less than half the array is utilized. In the worst case, a skewed
tree of depth k will require 2k-1 spaces. Of these, only k will be used.
Your strategy is really your action plan for closing the gap between vision & results.
41
Root of tree is stored in the data member 'root' of Tree. This data member serves as access-pointer
to the tree.
Elite performers never leave the site of a new idea without taking some action to advance it.
42
(Let L=
INORDER TRAVERSAL
Inorder traversal calls for moving down tree toward left until you can go no farther (Program 5.1).
Then, you "visit" the node, move one node to the right and continue.
If you cannot move to the right, go back one more node.
Each step of the trace shows the call of inorder, the value in the root, and whether or not the printf
function is invoked (Figure 5.17).
Since there are 19 nodes in the tree, inorder() is invoked 19 times for the complete traversal. The
nodes of figure 5.16 would be output in an inorder as
A/B*C*D+E
To have more in the world, you must give more to others.
43
POSTORDER TRAVERSAL
Visit a node, traverse right, and continue (Program 5.3).
When you cannot continue, move left and begin again or move back until you can move left and
resume.
The nodes of figure 5.16 would be output in postorder as
AB/C*D*E+
void preorder(tree_pointer ptr)
{
/* preorder tree traversal */
if (ptr)
{
printf(%d, ptr->data);
preorder(ptr->left_child);
predorder(ptr->right_child);
}
}
Program 5.3: Postorder traversal of binary tress
There's a place deep inside every single one of our hearts that knows all the answers to our biggest questions.
44
LEVEL-ORDER TRAVERSAL
This traversal uses a queue (Program 5.7).
We visit the root first, then the root's left child followed by the root's right child.
We continue in this manner, visiting the nodes at each new level from the leftmost node to the
rightmost node.
void level_order(tree_pointer ptr)
{
/* level order tree traversal */
int front = rear = 0;
tree_pointer queue[MAX_QUEUE_SIZE];
if (!ptr) return;
/* empty queue */
addq(front, &rear, ptr);
for (;;)
{
ptr = deleteq(&front, rear);
if (ptr)
{
printf(%d, ptr->data);
if (ptr->left_child)
addq(front, &rear, ptr->left_child);
if (ptr->right_child)
addq(front, &rear, ptr->right_child);
}
else
break;
}
}
Program 5.7: Level order traversal of binary tress
Failure reawakens us to who we really are & to what we truly want, & it shakes us out of our complacency.
45
When we represent the tree in memory, we must be able to distinguish between threads and normal
pointers. This is done by adding two additional fields to the node structure, leftThread and rightThread
(Figure 5.21).
Assume that ptr is an arbitrary node in a threaded tree. If ptr->leftThread=TRUE, then ptr->leftChild
contains a thread; otherwise it contains a pointer to the left child (Fig 5.23).
Similarly, if ptr->rightThread=TRUE, then ptr->rightChild contains a thread; otherwise it
contains a pointer to the right child (Figure 5.22).
We handle the problem of the loose threads by having them point to the header node, root.
The variable 'root' points to the header.
We need to sharpen our focus & live to the point-- just like a pencil.
46
Life is all about choices. To have a better life, we must less choosing how we are living.
47
MAX(MIN) HEAP
A max tree is a tree in which key value in each node is larger than key values in its children (if any).
A min tree is a tree in which key value in each node is smaller than key values in its
children (if any).
A max heap is a complete binary tree that is also a max tree (Figure 5.24).
A min heap is a complete binary tree that is also a min tree (Figure 5.25).
The key in the root of a max tree is the largest key in the tree,
whereas key in the root of a min tree is the smallest key in the tree.
Everything that happens to you is simply an opportunity to grow & heal a part of you that's in need of healing.
48
Everyone who enters your life comes to you at precisely the time that you most need to learn the lesson they've come
to teach.
41
The wise man always does at once what the fool does at finally.
51
The construction of the winner tree may be compared to the playing of a tournament.
The winner is a record with the smaller key.
Each non-leaf node represents winner of a tournament, and root node represents overall winner (or
smaller key).
LOSER TREES
This is a selection tree in which each non-leaf node retains a pointer to the loser (Fig 5.33).
FOREST TRAVERSALS
There are 3 forest traversal techniques namely: preorder, inorder and postorder traversal.
Preorder traversal of forest F can be recursively defined as follows
1) If F is empty then return.
2) Visit the root of the first tree of F.
3) Traverse the subtrees of the first tree in forest preorder.
4) Traverse the remaining trees of F in forest preorder.
Inorder traversal of forest F can be recursively defined as follows
1) If F is empty then return.
2) Traverse the subtrees of the first tree in forest inorder.
3) Visit the root of the first tree of F.
4) Traverse the remaining trees of F in forest inorder.
Postorder traversal of forest F can be recursively defined as follows
1) If F is empty then return.
2) Traverse the subtrees of the first tree in forest postorder.
3) Traverse the remaining trees of F in forest postorder.
4) Visit the root of the first tree of F.
The same 3 sets S1, S2 and S3 can be represented in the form of array as shown below
Giving with the intention of receiving really isn't giving, it's trading.
55
2) Find(i):Find the set containing the element i. For example, 3 is in set S3 and 8 in set S1.
int find1(int i)
{
for (; parent[i]>=0; i=parent[i]);
return i;
}
void union1(int i, int j)
{
parent[i]= j;
}
Program 5.16: iterative searching a Binary Search Tree
Reading is one of the best disciplines to stay on your game & at your highest.
56
The number of distinct binary trees is equal to the number of distinct inorder permutations obtainable
from binary trees having the preorder permutation 1, 2 . . . . . n. (Figure 5.49).
If we start with the numbers 1,2 and 3,then the possible permutations obtainable by a stack are
(1,2,3) (1,3,2) (2,1,3) (2,3,1) (3,2,1)
Small choices lead to giant consequences-- over time. There's no such thing as an unimportant day.
57
Greatness arrives for those who are never satisfied with what is, no matter how nice it looks.
58
Life is a skill. And like any other skill, once you know the ground rules & make the time to practice, you can get better.
59
A path from vertex u to vertex v in graph G is a sequence of vertices u,i 1,i2 . . . ik, v such that
(u,i1),(i1,i2) . . . . . . .(ik, v) are edges in E(G).
A simple path is a path in which all vertices except possibly the first and last are distinct.
A cycle is a simple path in which the first and last vertices are the same.
A undirected graph is said to be connected iff
for every pair of distinct vertices u & v in V(G) there is a path from u to v in G.
A connected component H of an undirected graph is a maximal connected subgraph (Figure 6.5).
The degree of a vertex is the number of edges incident to that vertex. (Degree of vertex 0 is 3)
In a directed graph G, in-degree of a vertex v defined as the number of edges for which v is the
head. The out-degree is defined as the number of edges for which v is the tail. (Vertex 1 of G3 has
in-degree 1, out-degree 2 and degree 3).
GRAPH ABSTRACT DATA TYPE
structure Graph is
objects: a nonempty set of vertices and a set of undirected edges, where each edge is a
pair of vertices
functions: for all graph Graph, v, v1 and v2 Vertices
Graph Create()::=return an empty graph
Graph InsertVertex(graph, v)::= return a graph with v inserted. v has no incident edge.
Graph InsertEdge(graph, v1,v2)::= return a graph with new edge between v1 and v2
Graph DeleteVertex(graph, v)::= return a graph in which v and all edges incident to it
are removed
Graph DeleteEdge(graph, v1, v2)::=return a graph in which the edge (v1, v2) is
removed
Boolean IsEmpty(graph)::= if (graph==empty graph)
return TRUE
else
return FALSE
List Adjacent(graph,v)::= return a list of all vertices that are adjacent to v
ADT 6.1:Abstract data type Graph
Adjacency Lists
The n rows of the adjacency matrix are represented as n chains.
There is one chain for each vertex in G.
The data field of a chain node stores the index of an adjacent vertex (Figure 6.8).
For an undirected graph with n vertices and e edges, this representation requires an array of size n
and 2e chain nodes.
Failure reawakens us to who we really are & to what we truly want, & it shakes us out of our complacency.
62