Data Structures With C Notes
Data Structures With C Notes
Data Structures With C Notes
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
TABLE OF CONTENTS
TABLE OF CONTENTS.............................................................................................................................. 1
UNIT 1: BASIC CONCEPTS.................................................................................................................... 4
1.1-Pointers and Dynamic Memory Allocation: ....................................................................................... 4
1.2. Algorithm Specification ..................................................................................................................... 8
1.3. Data Abstraction ................................................................................................................................ 9
1.4. Performance Analysis ...................................................................................................................... 10
1.5. Performance Measurement: ............................................................................................................. 11
1.6 RECOMMENDED QUESTIONS .................................................................................................... 13
UNIT -2 : ARRAYS and STRUCTURES .................................................................................................. 14
2.1 ARRAY............................................................................................................................................. 14
2.2. Dynamically Allocating Multidimensional Arrays .......................................................................... 18
2.3. Structures and Unions ...................................................................................................................... 20
2.4 .Polynomials...................................................................................................................................... 23
2.5. Sparse Matrices ................................................................................................................................ 26
2.6. Representation of Multidimensional arrays ..................................................................................... 29
2.7. RECOMMENDED QUESTIONS ................................................................................................... 31
UNIT 3 : STACKS AND QUEUES ........................................................................................................ 32
3.1.Stacks: ............................................................................................................................................... 32
3.2. Stacks Using Dynamic Arrays ......................................................................................................... 34
3.3. Queues.............................................................................................................................................. 34
3.4. Circular Queues Using Dynamic Arrays.......................................................................................... 37
3.5. Evaluation of Expressions: Evaluating a postfix expression ........................................................... 39
3.6. Multiple Stacks and Queues............................................................................................................. 43
3.7. RECOMMENDED QUESTIONS ................................................................................................... 44
UNIT 4 : LINKED LISTS ...................................................................................................................... 45
4.1. Singly Linked lists and Chains......................................................................................................... 45
4.2. Representing Chains in C: ............................................................................................................... 46
4.3. Linked Stacks and Queues ............................................................................................................... 47
4.4. Polynomials: .................................................................................................................................... 49
4.5. Additional List operations: .............................................................................................................. 52
4.6. Sparse Matrices ................................................................................................................................ 54
CITSTUDENTS.IN
Page 2
10CS35
CITSTUDENTS.IN
Page 3
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
CITSTUDENTS.IN
Page 4
10CS35
(*p)++:
While "pointer" has been used to refer to references in general, it more properly applies to data structures
whose interface explicitly allows the pointer to be manipulated (arithmetically via pointer arithmetic) as a
memory address, as opposed to a magiccookie or capability where this is not possible.
Fig 1: Pointer a pointing to the memory address associated with variable b. Note that in this particular
diagram, the computing architecture uses the same address space and data primitive for both pointers and
non-pointers; this need not be the case.
Pointers and Dynamic Memory Allocation:
Although arrays are good things, we cannot adjust the size of them in the middle of the program. If our
array is too small - our program will fail for large data. If our array is too big - we waste a lot of space,
again restricting what we can do. The right solution is to build the data structure from small pieces, and
add a new piece whenever we need to make it larger. Pointers are the connections which hold these
pieces together!
Pointers in Real Life
In many ways, telephone numbers serve as pointers in today's society. To contact someone, you do not
have to carry them with you at all times. All you need is their number. Many different people can all have
your number simultaneously. All you need do is copy the pointer. More complicated structures can be
built by combining pointers. For example, phone trees or directory information. Addresses are a more
physically correct analogy for pointers, since they really are memory addresses.
Linked Data Structures
All the dynamic data structures we will build have certain shared properties. We need a pointer to the
entire object so we can find it. Note that this is a pointer, not a cell. Each cell contains one or more data
fields, which is what we want to store. Each cell contains a pointer field to at least one ``next'' cell. Thus
much of the space used in linked data structures is not data! We must be able to detect the end of the data
structure. This is why we need the NIL pointers.
There are four functions defined in c standard for dynamic memmory allocation - calloc, free,
malloc and realloc. But in the heart of DMA there are only 2 of them malloc and free. Malloc stands for
memmory allocations and is used to allocate memmory from the heap while free is used to return
allocated memmory from malloc back to heap. Both these functions uses a standard library header
CITSTUDENTS.IN
Page 5
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
<stdlib.h> .Warning !!! - free ( ) function should be used to free memmory only allocated previously from
malloc, realloc or calloc. Freeing a random or undefined or compiler allocated memmory can lead to
severe damage to the O.S., Compiler and Computer Hardware Itself, in form of nasty system crashes.
The prototype of malloc ( ) function is void *malloc (size_t number_of_bytes)
Important thing to nore is malloc return a void pointer which can be converted to any pointer type as
explained in previous points. Also size_t is a special type of unsigned integer defined in <stdlib.h>
capable of storing largest memmory size that can be allocated using DMA, number_of_bytes is a value of
type size_t generally a integer indicating the amount of memmory to be allocated. Function malloc ( ) will
be returning a null pointer if memmory allocation fails and will return a pointer to first region of
memmory allocated when succsefull. It is also recommended you check the pointer returned for failure in
allocation before using the returned memmory for increasing stability of your program, generally
programmers provide some error handling code in case of failures. Also this returned pointer never needs
a typecast in C since it is a void pointer, it is a good practice to do one since it is required by C++ and will
produce a error if you used C++ compiler for compilation.Another commonly used operator used with
malloc is sizeof operator which is used to calculate the value of number_of_bytes by determing the size of
the compiler as well as user defined types and variables.
The prototype of free ( ) function is void free (void *p)
Function free ( ) is opposite of malloc and is used to return memmory previously allocated by other DMA
functions. Also only memmory allocated using DMA should be free using free () otherwise you may
corrupt your memmory allocation system at minimum.
C Source code shown below shows simple method of using dynamic memmory allocation elegantly
#include <stdio.h>
#include <stdlib.h>
int main ()
{
int *p;
p = (int *) malloc ( sizeof (int) ); //Dynamic Memmory Allocation
if (p == NULL) //Incase of memmory allocation failure execute the error handling code block
{
printf ("\nOut of Memmory");
exit (1);
}
*p = 100;
CITSTUDENTS.IN
Page 6
10CS35
Page 7
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
Garbage Collection
The Modula-3 system is constantly keeping watch on the dynamic memory which it has allocated, making
sure that something is still pointing to it. If not, there is no way for you to get access to it, so the space
might as well be recycled. The garbage collector automatically frees up the memory which has nothing
pointing to it. It frees you from having to worry about explicitly freeing memory, at the cost of leaving
certain structures which it can't figure out are really garbage, such as a circular list.
Explicit Deallocation
Although certain languages like Modula-3 and Java support garbage collection, others like C++ require
you to explicitly deallocate memory when you don't need it.
Dispose(p) is the opposite of New - it takes the object which is pointed to by p and makes it available for
reuse. Note that each dispose takes care of only one cell in a list. To dispose of an entire linked structure
we must do it one cell as a time. Note we can get into trouble with dispose:
Of course, it is too late to dispose of music, so it will endure forever without garbage collection. Suppose
we dispose(p), and later allocation more dynamic memory with new. The cell we disposed of might be
reused. Now what does q point to?
Answer - the same location, but it means something else! So called dangling references are a horrible
error, and are the main reason why Modula-3 supports garbage collection. A dangling reference is like a
friend left with your old phone number after you move. Reach out and touch someone - eliminate
dangling references!
Security in Java
It is possible to explicitly dispose of memory in Modula-3 when it is really necessary, but it is strongly
discouraged. Java does not allow one to do such operations on pointers at all. The reason is
security.Pointers allow you access to raw memory locations. In the hands of skilled but evil people,
unchecked access to pointers permits you to modify the operating system's or other people's memory
contents.
Page 8
10CS35
Page 9
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
Interviewing a sponsor
Reading the annual report
Chatting at lunch with a group of customer service representatives
Reading the organization's policy on customer service, focusing particularly on the recognition
and incentive aspects
Listening to audiotapes associates with customer service complaints
Leading a focus group with supervisors
Interviewing some randomly drawn representatives
Reviewing the call log
Reading an article in a professional journal on the subject of customer service performance
improvement
Chatting at the supermarket with somebody who is a customer, who wants to tell you about her
experience with customer service
data collection,
data transformation, and
data visualization.
Data collection is the process by which data about program performance are obtained from an
executing program. Data are normally collected in a file, either during or after execution,
although in some situations it may be presented to the user in real time.
Three basic data collection techniques can be distinguished:
Profiles record the amount of time spent in different parts of a program. This information, though
minimal, is often invaluable for highlighting performance problems. Profiles typically are gathered
automatically.
Counters record either frequencies of events or cumulative times. The insertion of counters may require
some programmer intervention.
Event traces record each occurrence of various specified events, thus typically producing a large amount
of data. Traces can be produced either automatically or with programmer intervention.
CITSTUDENTS.IN
Page 10
10CS35
The raw data produced by profiles, counters, or traces are rarely in the form required to answer
performance questions. Hence, data transformations are applied, often with the goal of reducing
total data volume. Transformations can be used to determine mean values or other higher-order statistics
or to extract profile and counter data from traces. For example, a profile recording the time spent in each
subroutine on each processor might be transformed to determine the mean time spent in each subroutine
on each processor, and the standard deviation from this mean. Similarly, a trace can be processed to
produce a histogram giving the distribution of message sizes. Each of the various performance tools
described in subsequent sections incorporates some set of built-in transformations; more specialized
transformation can also be coded by the programmer.
Parallel performance data are inherently multidimensional, consisting of execution times, communication
costs, and so on, for multiple program components, on different processors, and for different problem
sizes. Although data reduction techniques can be used in some situations to compress performance data to
scalar values, it is often necessary to be able to explore the raw multidimensional data. As is well known
in computational science and engineering, this process can benefit enormously from the use of data
visualization techniques. Both conventional and more specialized display techniques can be applied to
performance data.
As we shall see, a wide variety of data collection, transformation, and visualization tools are available.
When selecting a tool for a particular task, the following issues should be considered:
Accuracy. In general, performance data obtained using sampling techniques are less accurate than data
obtained by using counters or timers. In the case of timers, the accuracy of the clock must be taken into
account.
Simplicity. The best tools in many circumstances are those that collect data automatically, with little or no
programmer intervention, and that provide convenient analysis capabilities.
Flexibility. A flexible tool can be extended easily to collect additional performance data or to provide
different views of the same data. Flexibility and simplicity are often opposing requirements.
Intrusiveness. Unless a computer provides hardware support, performance data collection inevitably
introduces some overhead. We need to be aware of this overhead and account for it when analyzing data.
Abstraction. A good performance tool allows data to be examined at a level of abstraction appropriate for
the programming model of the parallel program. For example, when analyzing an execution trace from a
message-passing program, we probably wish to see individual messages, particularly if they can be
related to send and receive statements in the source program. However, this presentation is probably not
appropriate when studying a data-parallel program, even if compilation generates a message-passing
program. Instead, we would like to see communication costs related to data-parallel program statements.
CITSTUDENTS.IN
Page 11
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
CITSTUDENTS.IN
Page 12
10CS35
A simple performance measurement framework is outlined, which includes more than just measuring, but
also defining and understanding metrics, collecting and analysing data, then prioritising and taking
improvement actions. A description of the balanced scorecard approach is also covered.
Why measure performance?
When you can measure what you are speaking about and express it in numbers, you know something
about it.
You cannot manage what you cannot measure.
These are two often-quoted statements that demonstrate why measurement is important. Yet it is
surprising that organisations find the area of measurement so difficult to manage.
In the cycle of never-ending improvement, performance measurement plays an important role in:
Identifying and tracking progress against organisational goals
Identifying opportunities for improvement
Comparing performance against both internal and external standards
Reviewing the performance of an organisation is also an important step when formulating the direction of
the strategic activities. It is important to know where the strengths and weaknesses of the organisation lie,
and as part of the Plan Do Check Act cycle, measurement plays a key role in quality and
productivity improvement activities. The main reasons it is needed are:
To ensure customer requirements have been met
To be able to set sensible objectives and comply with them
To provide standards for establishing comparisons
To provide visibility and a scoreboard for people to monitor their own performance level
To highlight quality problems and determine areas for priority attention
To provide feedback for driving the improvement effort
It is also important to understand the impact of TQM on improvements in business performance, on
sustaining current performance and reducing any possible decline in performance.
CITSTUDENTS.IN
Page 13
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
2.1 ARRAY:
Definition :Array by definition is a variable that hold multiple elements which has the same data type.
Declaring Arrays :
We can declare an array by specify its data type, name and the number of elements the array holds
between square brackets immediately following the array name. Here is the syntax:
data_type array_name[size];
For example, to declare an integer array which contains 100 elements we can do as follows:
int a[100];
There are some rules on array declaration. The data type can be any valid C data types including structure
and union. The array name has to follow the rule of variable and the size of array has to be a positive
constant integer.We can access array elements via indexes array_name[index]. Indexes of array starts
from 0 not 1 so the highest elements of an array is array_name[size-1]
Initializing Arrays :
It is like a variable, an array can be initialized. To initialize an array, you provide initializing values which
are enclosed within curly braces in the declaration and placed following an equals sign after the array
name. Here is an example of initializing an integer array.
Array Representation
x
x
x
It is appropriate that we begin our study of data structures with the array. The array is often the only
means for structuring data which is provided in a programming language. Therefore it deserves a
significant amount of attention. If one asks a group of programmers to define an array, the most often
quoted saying is: a consecutive set of memory locations. This is unfortunate because it clearly reveals a
common point of confusion, namely the distinction between a data structure and its representation. It is
true that arrays are almost always implemented by using consecutive memory, but not always.
Intuitively, an array is a set of pairs, index and value. For each index which is defined, there is a value
CITSTUDENTS.IN
Page 14
10CS35
associated with that index. In mathematical terms we call this a correspondence or a mapping. However,
as computer scientists we want to provide a more functional definition by giving the operations which
are permitted on this data structure. For arrays this means we are concerned with only two operations
which retrieve and store values. Using our notation this object can be defined as:
structure ARRAY(value, index)
declare CREATE( ) array
RETRIEVE(array,index) value
STORE(array,index,value) array;
for all A array, i,j index, x value let
RETRIEVE(CREATE,i) :: = error
RETRIEVE(STORE(A,i,x),j) :: =
if EQUAL(i,j) then x else RETRIEVE(A,j)
end
end ARRAY
The function CREATE produces a new, empty array. RETRIEVE takes as input an array and an index,
and either returns the appropriate value or an error. STORE is used to enter new index-value pairs. The
second axiom is read as "to retrieve the j-th item where x has already been stored at index i in A is
equivalent to checking if i and j are equal and if so, x, or search for the j-th value in the remaining array,
A." This axiom was originally given by J. McCarthy. Notice how the axioms are independent of any
representation scheme. Also, i and j need not necessarily be integers, but we assume only that an
EQUAL function can be devised.
If we restrict the index values to be integers, then assuming a conventional random access memory we
can implement STORE and RETRIEVE so that they operate in a constant amount of time. If we interpret
the indices to be n-dimensional, (i1,i2, ...,in), then the previous axioms apply immediately and define ndimensional arrays. In section 2.4 we will examine how to implement RETRIEVE and STORE for multidimensional arrays using consecutive memory locations.
Array and Pointer:
Each array element occupies consecutive memory locations and array name is a pointer that points to the
first element. Beside accessing array via index we can use pointer to manipulate array. This program
helps you visualize the memory address each array elements and how to access array element using
pointer.
01 #include <stdio.h>
02
03 void main()
04 {
05
06
CITSTUDENTS.IN
Page 15
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
07
08
09
10
11
12
13
14
15
printf("list[%d] is in %d\n",i,&list[i]);
16
17
18
19
20
21
22
printf("list[%d] = %d\n",i,*plist);
23
24
25
26
27
plist++;
}
28
29 }
Here is the output
CITSTUDENTS.IN
Page 16
10CS35
is
is
is
is
is
in
in
in
in
in
=
=
=
=
1310568
1310572
1310576
1310580
1310584
2
1
3
7
You can store pointers in an array and in this case we have an array of pointers. This code snippet uses an
array to store integer pointer.
1 int *ap[10];
Multidimensional Arrays:
An array with more than one index value is called a multidimensional array. The entire array above is
called single-dimensional array. To declare a multidimensional array you can do follow syntax
1 data_type array_name[][][];
The number of square brackets specifies the dimension of the array. For example to declare two
dimensions integer array we can do as follows:
1 int matrix[3][3];
Initializing Multidimensional Arrays :
You can initialize an array as a single-dimension array. Here is an example of initialize an two
dimensions integer array:
1 int matrix[3][3] =
2{
3 {11,12,13},
4 {21,22,23},
5 {32,31,33},
6 };
Page 17
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
To allocate a one-dimensional array of length N of some particular type, simply use malloc to allocate
enough memory to hold N elements of the particular type, and then use the resulting pointer as if it were
an array. For example, the following code snippet allocates a block of N ints, and then, using array
notation, fills it with the values 0 through N-1:
int *A = malloc (sizeof (int) * N);
int i;
Page 18
10CS35
if(array == NULL)
{
fprintf(stderr, "out of memory\n");
exit or return
}
for(i = 0; i < nrows; i++)
{
array[i] = malloc(ncolumns * sizeof(int));
if(array[i] == NULL)
{
fprintf(stderr, "out of memory\n");
exit or return
}
}
array is a pointer-to-pointer-to-int : at the first level, it points to a block of pointers, one for each row.
That first-level pointer is the first one we allocate; it has n rows elements, with each element big enough
to hold a pointer-to-int, or int *. If we successfully allocate it, we then fill in the pointers (all n rows of
them) with a pointer (also obtained from malloc) to n columns number of ints, the storage for that row
of the array. If this isn't quite making sense, a picture should make everything clear:
Page 19
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
becomes possible to write ``heterogeneous'' functions which don't have to know (at compile time) how
big the ``arrays'' are. In other words, one function can operate on ``arrays'' of various sizes and shapes.
The function will look something like
func2(int **array, int nrows, int ncolumns)
{
}
This function does accept a pointer-to-pointer-to-int, on the assumption that we'll only be calling it with
simulated, dynamically allocated multidimensional arrays. (We must not call this function on arrays like
the ``true'' multidimensional array a2 of the previous sections). The function also accepts the dimensions
of the arrays as parameters, so that it will know how many ``rows'' and ``columns'' there are, so that it can
iterate over them correctly. Here is a function which zeros out a pointer-to-pointer, two-dimensional
``array'':
void zeroit(int **array, int nrows, int ncolumns)
{
int i, j;
for(i = 0; i < nrows; i++)
{
for(j = 0; j < ncolumns; j++)
array[i][j] = 0;
}
}
Finally, when it comes time to free one of these dynamically allocated multidimensional ``arrays,'' we
must remember to free each of the chunks of memory that we've allocated. (Just freeing the top-level
pointer, array, wouldn't cut it; if we did, all the second-level pointers would be lost but not freed, and
would waste memory.) Here's what the code might look like:
for(i = 0; i < nrows; i++)
free(array[i]);
free(array);
Page 20
10CS35
particularly in large programs, because they permit a group of related variables to be treated as a unit
instead of as separate entities.
Todays application requires complex data structures to support them. A structure is a collection
of related elements where element belongs to a different type. Another way to look at a structure is a
template a pattern. For example graphical user interface found in a window requires structures typical
example for the use of structures could be the file table which holds the key data like logical file name,
location of the file on disc and so on. The file table in C is a type defined structure - FILE. Each element
of a structure is also called as field. A field has a many characteristic similar to that of a normal variable.
An array and a structure are slightly different. The former has a collection of homogeneous elements but
the latter has a collection of heterogeneous elements. The general format for a structure in C is shown
struct {field_list} variable_identifier;
struct struct_name
{
type1 fieldname1;
type2 fieldname2;
.
.
.
typeN fieldnameN;
};
struct struct_name variables;
The above format shown is not concrete and can vary, so different `
flavours of structure declaration is as shown.
struct
{
.
} variable_identifer;
Example
struct mob_equip;
{
long int IMEI;
char rel_date[10];
CITSTUDENTS.IN
Page 21
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
char model[10];
char brand[15];
};
The above example can be upgraded with typedef. A program to illustrate the
working of the structure is shown in the previous section.
typedef struct mob_equip;
{
long int IMEI;
char rel_date[10];
char model[10];
char brand[15];
int count;
} MOB; MOB m1;
struct tag
{
.
};
struct tag variable_identifers;
typedef struct
{
..
} TYPE_IDENITIFIER;
TYPE_IDENTIFIER variable_identifers;
Accessing a structure
A structure variable or a tag name of a structure can be used to access the members of a structure with the
help of a special operator . also called as member operator . In our previous example To access the idea
of the IMEI of the mobile equipment in the structure mob_equip is done like this Since the structure
variable can be treated as a normal variable All the IO functions for a normal variable holds good for the
structure variable also with slight. The scanf statement to read the input to the IMEI is given below
scanf (%d,&m1.IMEI);
CITSTUDENTS.IN
Page 22
10CS35
Increment and decrement operation are same as the normal variables this includes postfix and prefix also.
Member operator has more precedence than the increment or decrement. Say suppose in example quoted
earlier we want count of student then
m1.count++; ++m1.count
Unions
Unions are very similar to structures, whatever discussed so far holds good for unions also then why do
we need unions? Size of unions depends on the size of its member of largest type or member with largest
size, but this is not son in case of structures.
Example union abc1
{
int a;
float b;
char c;
};
The size of the union abc1 is 4bytes as float is largest type. Therefore at any point of time we can access
only one member of the union and this needs to remembered by programmer.
2.4 .Polynomials:
In mathematics, a polynomial (from Greek poly, "many" and medieval Latin binomium, "binomial"[1] [2]
[3]
) is an expression of finite length constructed from variables (also known as indeterminates) and
constants, using only the operations of addition, subtraction, multiplication, and non-negative integer
exponents. For example, x2 4x + 7 is a polynomial, but x2 4/x + 7x3/2 is not, because its second term
involves division by the variable x (4/x) and because its third term contains an exponent that is not a
whole number (3/2). The term "polynomial" can also be used as an adjective, for quantities that can be
expressed as a polynomial of some parameter, as in "polynomial time" which is used in computational
complexitytheory.
Polynomials appear in a wide variety of areas of mathematics and science. For example, they are used to
form polynomial equations, which encode a wide range of problems, from elementary wordproblems to
complicated problems in the sciences; they are used to define polynomial functions, which appear in
settings ranging from basic chemistry and physics to economics and social science; they are used in
calculus and numerical analysis to approximate other functions. In advanced mathematics, polynomials
are used to construct polynomialrings, a central concept in abstractalgebra and algebraicgeometry.
CITSTUDENTS.IN
Page 23
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
example of a polynomial
this one has 3 terms
Fig 2:
Polynomial comes form poly- (meaning "many") and -nomial (in this case meaning "term") ... so it says
"many terms"
A polynomial can have:
constants (like 3, -20, or )
variables (like x and y)
exponents (like the 2 in y2) but only 0, 1, 2, 3, ... etc
That can be combined using:
+
addition,
subtraction, and
Multiplication
... but not division!
Those rules keeps polynomials simple, so they are easy to work with!
Polynomial or Not?
CITSTUDENTS.IN
Page 24
10CS35
Fig 3:
These are polynomials:
3x
x-2
-6y2 - (7/9)x
3xyz + 3xy2z - 0.1xz - 200y + 0.5
512v5+ 99w5
1
(Yes, even "1" is a polynomial, it has one term which just happens to be a constant).
And these are not polynomials
2/(x+2) is not, because dividing is not allowed
1/x is not
3xy-2 is not, because the exponent is "-2" (exponents can only be 0,1,2,...)
x is not, because the exponent is "" (see fractionalexponents)
But these are allowed:
x/2 is allowed, because it is also ()x (the constant is , or 0.5)
also 3x/8 for the same reason (the constant is 3/8, or 0.375)
2 is allowed, because it is a constant (= 1.4142...etc)
Monomial, Binomial, Trinomial
There are special names for polynomials with 1, 2 or 3 terms:
CITSTUDENTS.IN
Page 25
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
Fig
4:(There
is
also
quadrinomial
but those names are not often used)
(4
terms)
and
quintinomial
(5
terms),
0 -3
>> S = sparse(A)
S=
(2,1)
(3,2)
-3
CITSTUDENTS.IN
Page 26
(2,3)
10CS35
>> whos
Name
Size
Bytes Class
3x3
72 double array
3x3
64 sparse array
(1,2)
(3,2)
-1
(Of course, for this to be truly useful, the nonzeros would be added in a loop.)
Another version of the sparse command is S = sparse(I,J,S,m,n,maxnz). This creates an
sparse
Page 27
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
-16
0 -16
64 -16
0 -16 64
-16
0 -16
0 -16
0
0 -16
0 64 -16
0 -16
0 -16
0 -16
0 -16 64
0 -16
0 -16
64 -16
10CS35
0
0
0
0 -16
0
0 -16
0 -16
0 64 -16
0 -16
64 -16
0 -16 64
This is a
matrix with 5 nonzero diagonals. In Matlab's indexing scheme, the nonzero diagonals of A
are numbers -3, -1, 0, 1, and 3 (the main diagonal is number 0, the first subdiagonal is number -1, the first
superdiagonal is number 1, and so forth). To create the same matrix in sparse format, it is first necessary
to create a
matrix containing the nonzero diagonals of A. Of course, the diagonals, regarded as
column vectors, have different lengths; only the main diagonal has length 9. In order to gather the various
diagonals in a single matrix, the shorter diagonals must be padded with zeros. The rule is that the extra
zeros go at the bottom for subdiagonals and at the top for superdiagonals. Thus we create the following
matrix:
>> B = [
-16 -16 64
-16 -16
-16
64 -16
0 64 -16
-16 -16 64
-16 -16
-16
0
0
0
0 -16
64 -16 -16
0 64 -16 -16
0 -16 64
0 -16
0 64 -16 -16
];
(notice the technique for entering the rows of a large matrix on several lines). The spdiags command also
needs the indices of the diagonals:
>> d = [-3,-1,0,1,3];
CITSTUDENTS.IN
Page 28
10CS35
Page 29
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
Compact layouts
Often the coefficients are chosen so that the elements occupy a contiguous area of memory. However, that
is not necessary. Even if arrays are always created with contiguous elements, some array slicing
operations may create non-contiguous sub-arrays from them.
There are two systematic compact layouts for a two-dimensional array. For example, consider the matrix
In the row-major order layout (adopted by C for statically declared arrays), the elements of each row are
stored in consecutive positions:
In Column-major order (traditionally used by Fortran), the elements of each column are consecutive in
memory:
For arrays with three or more indices, "row major order" puts in consecutive positions any two elements
whose index tuples differ only by one in the last index. "Column major order" is analogous with respect
to the first index. In systems which use processor cache or virtual memory, scanning an array is much
faster if successive elements are stored in consecutive positions in memory, rather than sparsely scattered.
Many algorithms that use multidimensional arrays will scan them in a predictable order. A programmer
(or a sophisticated compiler) may use this information to choose between row- or column-major layout
for each array. For example, when computing the product AB of two matrices, it would be best to have A
stored in row-major order, and B in column-major order.
The Representation of Multidimensional Arrays:
N-dimension, A[M0][M2]. . .[Mn-1]
Address of any entry A[i0][i1]...[in-1]
base + i0 M 1 M 2
+ i1 M 2 M 3
+ i2 M 3 M 4
M n -1
M n -1
M n -1
+ in - 2 M n -1
+ in -1
n -1
base +
i
j= 0
a j,
a
where{ j
a n -1
3 nk -1j +1 ,0 d j
n -1
Array resizing:
Static arrays have a size that is fixed at allocation time and consequently do not allow elements to be
inserted or removed. However, by allocating a new array and copying the contents of the old array to it, it
CITSTUDENTS.IN
Page 30
10CS35
is possible to effectively implement a dynamic or growable version of an array; see dynamic array. If this
operation is done infrequently, insertions at the end of the array require only amortized constant time.
Some array data structures do not reallocate storage, but do store a count of the number of elements of the
array in use, called the count or size. This effectively makes the array a dynamic array with a fixed
maximum size or capacity; Pascal strings are examples of this.
Non-linear formulas
More complicated ("non-linear") formulas are occasionally used. For a compact two-dimensional
triangular array, for instance, the addressing formula is a polynomial of degree 2.
Efficiency
Both store and select take (deterministic worst case) constant time. Arrays take linear (O(n)) space in the
number of elements n that they hold. In an array with element size k and on a machine with a cache line
size of B bytes, iterating through an array of n elements requires the minimum of ceiling(nk/B) cache
misses, because its elements occupy contiguous memory locations. This is roughly a factor of B/k better
than the number of cache misses needed to access n elements at random memory locations. As a
consequence, sequential iteration over an array is noticeably faster in practice than iteration over many
other data structures, a property called locality of reference (this does not mean however, that using a
perfect hash or trivial hash within the same (local) array, will not be even faster - and achievable in
constant time).
Memory-wise, arrays are compact data structures with no per-element overhead. There may be a per-array
overhead, e.g. to store index bounds, but this is language-dependent. It can also happen that elements
stored in an array require less memory than the same elements stored in individual variables, because
several array elements can be stored in a single word; such arrays are often called packed arrays. An
extreme (but commonly used) case is the bit array, where every bit represents a single element.
Linked lists allow constant time removal and insertion in the middle but take linear time for indexed
access. Their memory use is typically worse than arrays, but is still linear. An alternative to a
multidimensional array structure is to use a one-dimensional array of references to arrays of one
dimension less.
For two dimensions, in particular, this alternative structure would be a vector of pointers to vectors, one
for each row. Thus an element in row i and column j of an array A would be accessed by double indexing
(A[i][j] in typical notation). This alternative structure allows ragged or jagged arrays, where each row
may have a different size or, in general, where the valid range of each index depends on the values of
all preceding indices. It also saves one multiplication (by the column address increment) replacing it by a
bit shift (to index the vector of row pointers) and one extra memory access (fetching the row address),
which may be worthwhile in some architectures.
CITSTUDENTS.IN
Page 31
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
3.1.Stacks:
A stack is an ordered collection of items into which new items may be inserted and from which items
may be deleted at one end, called the top of the stack. A stack is a dynamic, constantly changing object as
the definition of the stack provides for the insertion and deletion of items. It has single end of the stack as
top of the stack, where both insertion and deletion of the elements takes place. The last element inserted
into the stack is the first element deleted-last in first out list (LIFO). After several insertions and
deletions, it is possible to have the same frame again.
Primitive Operations
When an item is added to a stack, it is pushed onto the stack. When an item is removed, it is popped from
the stack.
Given a stack s, and an item i, performing the operation push(s,i) adds an item i to the top of stack s.
push(s, H);
push(s, I);
push(s, J);
Operation pop(s) removes the top element. That is, if i=pop(s), then the removed element is assigned to i.
pop(s);
Because of the push operation which adds elements to a stack, a stack is sometimes called a pushdown
list. Conceptually, there is no upper limit on the number of items that may be kept in a stack. If a stack
contains a single item and the stack is popped, the resulting stack contains no items and is called the
empty stack. Push operation is applicable to any stack. Pop operation cannot be applied to the empty
stack. If so, underflow happens. A Boolean operation empty(s), returns TRUE if stack is empty.
Otherwise FALSE, if stack is not empty.
Representing stacks in C
Before programming a problem solution that uses a stack, we must decide how to represent the stack in a
programming language. It is an ordered collection of items. In C, we have ARRAY as an ordered
collection of items. But a stack and an array are two different things. The number of elements in an array
is fixed. A stack is a dynamic object whose size is constantly changing. So, an array can be declared large
enough for the maximum size of the stack. A stack in C is declared as a structure containing two objects:
An array to hold the elements of the stack.
An integer to indicate the position of the current stack top within the array.
#define STACKSIZE 100
struct stack {
int top;
CITSTUDENTS.IN
Page 32
10CS35
int items[STACKSIZE];
};
The stack s may be declared by
struct stack s;
The stack items may be int, float, char, etc. The empty stack contains no elements and can therefore be
indicated by top= -1. To initialize a stack S to the empty state, we may initially execute
s.top= -1.
To determine stack empty condition,
if (s.top=-1)
stack empty;
else
stack is not empty;
The empty(s) may be considered as follows:
int empty(struct stack *ps)
{
if(ps->top== -1)
return(TRUE);
else
return(FALSE);
}
Aggregating the set of implementation-dependent trouble spots into small, easily identifiable units is an
important method of making a program more understandable and modifiable. This concept is known as
modularization, in which individual functions are isolated into low-level modules whose properties are
easily verifiable. These low-level modules can then be used by more complex routines, which do not have
to concern themselves with the details of the low-level modules but only with their function. The complex
routines may themselves then be viewed as modules by still higher-level routines that use them
independently of their internal details.
Implementing pop operation
If the stack is empty, print a warning message and halt execution. Remove the top element from the stack.
Return this element to the calling program
int pop(struct stack *ps)
{
CITSTUDENTS.IN
Page 33
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
if(empty(ps)){
printf(%,stack underflow);
exit(1);
}
return(ps->items[ps->top--]);
}
*str;
words;
main()
{
words x[100]; // I do not want to use this, I want to dynamic increase the size of the array as data
comesin.
}
For example here is the following array in which i read individual words from a .txt file and save them
word by word in the array:
Code:
char words[1000][15];
Here 1000 defines the number of words the array can save and each word may comprise of not more than
15 characters. Now I want that that program should dynamically allocate the memory for the number of
words it counts. For example, a .txt file may contain words greater that 1000. Now I want that the
program should count the number of words and allocate the memory accordingly. Since we cannot use a
variable in place of [1000], I am completely blank at how to implement my logic. Please help me in this
regard.
3.3. Queues:
A queue is like a line of people waiting for a bank teller. The queue has a front and a rear.
When we talk of queues we talk about two distinct ends: the front and the rear. Additions to the queue
take place at the rear. Deletions are made from the front. So, if a job is submitted for execution, it joins at
the rear of the job queue. The job at the front of the queue is the next one to be executed
New people must enter the queue at the rear. push, although it is usually called an enqueue operation.
CITSTUDENTS.IN
Page 34
10CS35
When an item is taken from the queue, it always comes from the front. pop, although it is usually called
a dequeue operation.
What is Queue?
Ordered collection of elements that has two ends as front and rear.
Delete from front end
Insert from rear end
A queue can be implemented with an array, as shown here. For example, this queue contains the integers
4 (at the front), 8 and 6 (at the rear).
Queue Operations
Queue Overflow
Insertion of the element into the queue
Queue underflow
Deletion of the element from the queue
Display of the queue
struct Queue {
int que [size];
int front;
int rear;
}Q;
Example:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#define size 5
struct queue {
int que[size];
int front, rear;
} Q;
CITSTUDENTS.IN
Page 35
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
Example:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#define size 5
struct queue {
int que[size];
int front, rear;
} Q;
int Qfull ( ){
if (Q.rear >= size-1)
return 1;
else
return 0;
}
int Qempty( ){
if ((Q.front == -1)||(Q.front > Q.rear))
return 1;
else
return 0;
}
int insert (int item) {
if (Q.front == -1)
Q.front++;
Q.que[++Q.rear] = item;
return Q.rear;
}
Int delete () {
Int item;
CITSTUDENTS.IN
Page 36
10CS35
Item = Q.que[Q.front];
Q.front++;
Return Q.front;
}
Void display () {
Int I;
For (i=Q.front;i<=Q.rear;i++)
Printf( %d,Q.que[i]);
}
Void main (void) {
Int choice, item;
Q.front = -1; Q.Rear = -1;
do {
Printf(Enter your choice : 1:I, 2:D, 3:Display);
Scanf(%d, &choice);
Switch(choice){
Case 1: if(Qfull()) printf(Cannt Insert);
else scanf(%d,item); insert(item); break;
Case 2: if(Qempty()) printf(Underflow);
else delete(); break;
}
}
}
A more efficient queue representation is obtained by regarding the array Q(1:n) as circular. It
now becomes more convenient to declare the array as Q(0:n - 1). When rear = n - 1, the next
element is entered at Q(0) in case that spot is free. Using the same conventions as before, front
CITSTUDENTS.IN
Page 37
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
will always point one position counterclockwise from the first element in the queue. Again, front
= rear if and only if the queue is empty. Initially we have front = rear = 1. Figure 3.4 illustrates
some of the possible configurations for a circular queue containing the four elements J1-J4 with
n > 4. The assumption of circularity changes the ADD and DELETE algorithms slightly. In order
to add an element, it will be necessary to move rear one position clockwise, i.e.,
if rear = n - 1 then rear 0
else rear rear + 1.
Using the modulo operator which computes remainders, this is just rear (rear + 1)mod n.
Similarly, it will be necessary to move front one position clockwise each time a deletion is made.
Again, using the modulo operation, this can be accomplished by front (front + l)mod n. An
examination of the algorithms indicates that addition and deletion can now be carried out in a
fixed amount of time or O(1).
e.g.
OOOOO7963 _ 4OOOO7963 (after Enqueue(4))
After Enqueue(4), rear index moves from 3 to 4.
Queue Full Condition:
if(front == (rear+1)%size) Queue is Full
Where do we insert:
rear = (rear + 1)%size; queue[rear]=item;
After deletion : front = (front+1)%size;
Example of a Circular Queue
A Circular Q, the size of which is 5 has three elements 20, 40, and 60 where front is 0
and rear is 2. What are the values of after each of these operations:
Q = 20, 40, 60, - , - front20[0], rear60[2]
Insert item 50:
CITSTUDENTS.IN
Page 38
10CS35
(3.1)
might have several meanings; and even if it were uniquely defined, say by a full use of parentheses, it still
seemed a formidable task to generate a correct and reasonable instruction sequence. Fortunately the
solution we have today is both elegant and simple. Moreover, it is so simple that this aspect of compiler
writing is really one of the more minor issues.
An expression is made up of operands, operators and delimiters. The expression above has five operands:
A,B,C,D, and E. Though these are all one letter variables, operands can be any legal variable name or
constant in our programming language. In any expression the values that variables take must be consistent
with the operations performed on them. These operations are described by the operators. In most
programming languages there are several kinds of operators which correspond to the different kinds of
data a variable can hold. First, there are the basic arithmetic operators: plus, minus, times, divide, and
exponentiation (+,-,*,/,**). Other arithmetic operators include unary plus, unary minus and mod, ceil, and
floor. The latter three may sometimes be library subroutines rather than predefined operators. A second
class are the relational operators: . These are usually defined to work for arithmetic operands, but they can
just as easily work for character string data. ('CAT' is less than 'DOG' since it precedes 'DOG' in
alphabetical order.) The result of an expression which contains relational operators is one of the two
CITSTUDENTS.IN
Page 39
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
constants: true or false. Such all expression is called Boolean, named after the mathematician George
Boole, the father of symbolic logic.
The first problem with understanding the meaning of an expression is to decide in what order the
operations are carried out. This means that every language must uniquely define such an order. For
instance, if A = 4, B = C = 2, D = E = 3, then in eq. 3.1 we might want X to be assigned the value
4/(2 ** 2) + (3 * 3) - (4 * 2)
= (4/4) + 9 - 8
= 2.
Let us now consider an example. Suppose that we are asked to evaluate the following postfix expression:
623+-382/+*2$3+
Symb Opnd1 Opnd2 Value opndstk
66
2 6,2
3 6,2,3
+ 2 3 5 6,5
-6511
3 6 5 1 1,3
8 6 5 1 1,3,8
2 6 5 1 1,3,8,2
/ 8 2 4 1,3,4
8
+ 3 4 7 1,7
*1777
2 1 7 7 7,2
$ 7 2 49 49
3 7 2 49 49,3
+ 49 3 52 52
Each time we read an operand, we push it onto a stack. When we reach an operator, its operands will be
the top two elements on the stack. We can then pop these two elements, perform the indicated operation
on them, and push the result on the stack so that it will be available for use as an operand of the next
operator. The maximum size of the stack is the number of operands that appear in the input expression.
But usually, the actual size of the stack needed is less than maximum, as operator pops the top two
operands.
CITSTUDENTS.IN
Page 40
10CS35
Page 41
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
switch (symb){
CITSTUDENTS.IN
Page 42
10CS35
6 * AB+ *
7 C AB+C *
8 AB+C*
Program to convert an expression from infix to postfix
Along with pop, push, empty, popandtest, we also make use of additional functions such as, isoperand,
prcd, postfix.
isoperand returns TRUE if its argument is an operand and FALSE otherwise
prcd accepts two operator symbols as arguments and returns TRUE if the first has precedence over the
second when it appears to the left of the second in an infix string and FALSE otherwise
postfix prints the postfix string
(3.2)
as the initial values of B(i) and T(i), (see figure 3.9). Stack i, 1 i n can grow from B(i) + 1 up to B(i + 1)
before it catches up with the i + 1'st stack. It is convenient both for the discussion and the algorithms to
define B(n + 1) = m. Using this scheme the add and delete algorithms become:
procedure ADD(i,X)
//add element X to the i'th stack, 1 i n//
if T(i) = B(i + 1) then call STACK-FULL (i)
T(i) T(i) + 1
V(T(i)) X
//add X to the i'th stack//
end ADD
procedure DELETE(i,X)
CITSTUDENTS.IN
Page 43
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
Page 44
10CS35
A linked list whose nodes contain two fields: an integer value and a link to the next node
Linked lists are among the simplest and most common data structures. They can be used to implement
several other common abstract data structures, including stacks, queues, associative arrays, and symbolic
expressions, though it is not uncommon to implement the other data structures directly without using a list
as the basis of implementation.
The principal benefit of a linked list over a conventional array is that the list elements can easily be added
or removed without reallocation or reorganization of the entire structure because the data items need not
be stored contiguously in memory or on disk. Linked lists allow insertion and removal of nodes at any
point in the list, and can do so with a constant number of operations if the link previous to the link being
added or removed is maintained during list traversal.
On the other hand, simple linked lists by themselves do not allow randomaccess to the data other than the
first node's data, or any form of efficient indexing.
CITSTUDENTS.IN
Page 45
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
An array and a sequential mapping is used to represent simple data structures in the previous chapters
This representation has the property that successive nodes of the data object are stored a fixed
distance apart
(1) If the element aijis stored at location Lij, then aij+1is at the location Lij+1
CITSTUDENTS.IN
Page 46
10CS35
(2) If the i-thelement in a queue is at location Li, then the (i+1)-th element is at location Li+1% n for the
circular representation
(3) If the topmost node of a stack is at location LT , then the node beneath it is at location LT-1, and so on
When a sequential mapping is used for ordered lists, operations such as insertion and deletion of
arbitrary elements become expensive.
In a linked representationTo access list elements in the correct order, with each element we store the
address or location of the next element in the listA linked list is comprised of nodes; each node has zero
or more data fields and one or more link or pointer fields.
Page 47
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
return success;
}
A queue is a particular kind of collection in which the entities in the collection are kept in order and the
principal (or only) operations on the collection are the addition of entities to the rear terminal position and
removal of entities from the front terminal position. This makes the queue a First-In-First-Out (FIFO)
data structure. In a FIFO data structure, the first element added to the queue will be the first one to be
removed. This is equivalent to the requirement that once an element is added, all elements that were
added before have to be removed before the new element can be invoked. A queue is an example of a
lineardatastructure.
Queues provide services in computer science, transport, and operations research where various entities
such as data, objects, persons, or events are stored and held to be processed later. In these contexts, the
queue performs the function of a buffer.
#include<malloc.h>
#include<stdio.h>
structnode{
intvalue;
structnode*next;
};
voidInit(structnode*n){
n->next=NULL;
} voidEnqueue(structnode*root,intvalue){
structnode*j=(structnode*)malloc(sizeof(structnode));
j->value=value;
j->next=NULL;
structnode*temp
temp=root;
while(temp->next!=NULL)
{
temp=temp->next;
}
temp->next=j;
printf(Value Enqueued is : %d\n,value);
}
voidDequeue(structnode*root)
{
if(root->next==NULL)
{
printf(NoElementtoDequeue\n);
}
CITSTUDENTS.IN
Page 48
10CS35
else
{
structnode*temp;
temp=root->next;
root->next=temp->next;
printf(ValueDequeuedis%d\n,temp->value);
free(temp);
}
}
voidmain()
{ structnodesample_queue;
Init(&sample_queue);
Enqueue(&sample_queue,10);
Enqueue(&sample_queue,50);
Enqueue(&sample_queue,570);
Enqueue(&sample_queue,5710);
Dequeue(&sample_queue);
Dequeue(&sample_queue);
Dequeue(&sample_queue);
}
4.4. Polynomials:
In mathematics, a polynomial (from Greek poly, "many" and medieval Latin binomium, "binomial"[1] [2]
[3]
) is an expression of finite length constructed from variables (also known as indeterminates) and
constants, using only the operations of addition, subtraction, multiplication, and non-negative integer
exponents. For example, x2 4x + 7 is a polynomial, but x2 4/x + 7x3/2 is not, because its second term
involves division by the variable x (4/x) and because its third term contains an exponent that is not a
whole number (3/2). The term "polynomial" can also be used as an adjective, for quantities that can be
expressed as a polynomial of some parameter, as in "polynomialtime" which is used in computational
complexitytheory.
Polynomials appear in a wide variety of areas of mathematics and science. For example, they are used to
form polynomial equations, which encode a wide range of problems, from elementary wordproblems to
complicated problems in the sciences.
A polynomial is a mathematical expression involving a sum of powers in one or more variables
multiplied by coefficients. A polynomial in one variable (i.e., a univariate polynomial) with constant
coefficients is given by
(1)
CITSTUDENTS.IN
Page 49
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
The individual summands with the coefficients (usually) included are called monomials (Becker and
Weispfenning 1993, p. 191), whereas the products of the form
in the multivariate case, i.e., with
the coefficients omitted, are called terms (Becker and Weispfenning 1993, p. 188). However, the term
"monomial" is sometimes also used to mean polynomial summands without their coefficients, and in
some older works, the definitions of monomial and term are reversed. Care is therefore needed in
attempting to distinguish these conflicting usages.
The highest power in a univariate polynomial is called its order, or sometimes its degree.
Any polynomial
with
can be expressed as
(2)
A polynomial in two variables (i.e., a bivariate polynomial) with constant coefficients is given by
(3)
The sum of two polynomials is obtained by adding together the coefficients sharing the same powers of
variables (i.e., the same terms) so, for example,
(4)
and has order less than (in the case of cancellation of leading terms) or equal to the maximum order of the
original two polynomials. Similarly, the product of two polynomials is obtained by multiplying term by
term and combining the results, for example
5
and has order equal to the sum of the orders of the two original polynomials.
A polynomial quotient
(7)
of two polynomials
and
is known as a rational function. The process of performing such a
division is called long division, with synthetic division being a simplified method of recording the
division. For any polynomial
,
divides
, meaning that the polynomial quotient is
a rational polynomial or, in the case of an integer polynomial, another integer polynomial (N. Sato, pers.
comm., Nov. 23, 2004).
CITSTUDENTS.IN
Page 50
10CS35
Horner's rule provides a computationally efficient method of forming a polynomial from a list of its
coefficients, and can be implemented in Mathematica as follows.
Polynomial[l_List, x_] := Fold[x #1 + #2&, 0, l]
The following table gives special names given to polynomials of low orders.
polynomial order polynomial name
2
quadratic polynomial
cubic polynomial
quartic
quintic
sextic
Polynomials of fourth degree may be computed using three multiplications and five additions if a few
quantities are calculated first (Press et al. 1989):
(9)
(10)
where
(11)
(12)
(13)
(14)
Similarly, a polynomial of fifth degree may be computed with four multiplications and five additions, and
a polynomial of sixth degree may be computed with four multiplications and seven additions.The use of
linked lists is well suited to polynomial operations. We can easily imagine writing a collection of
procedures for input, output addition, subtraction and multiplication of polynomials using linked lists as
CITSTUDENTS.IN
Page 51
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
the means of representation. A hypothetical user wishing to read in polynomials A(x), B(x) and C(x) and
then compute D(x) = A(x) * B(x) + C(x) would write in his main program:
call READ(A)
call READ(B)
call READ(C)
T PMUL(A, B)
D PADD(T, C)
call PRINT(D)
Now our user may wish to continue computing more polynomials. At this point it would be useful to
reclaim the nodes which are being used to represent T(x). This polynomial was created only as a partial
result towards the answer D(x). By returning the nodes of T(x), they may be used to hold other
polynomials.
procedure ERASE(T)
//return all the nodes of T to the available space list avoiding repeated
calls to procedure RET//
if T = 0 then return
p T
while LINK (p) 0 do //find the end of T//
p LINK (p)
end
LlNK (p) AV
// p points to the last node of T//
AV T //available list now includes T//
end ERASE
Study this algorithm carefully. It cleverly avoids using the RET procedure to return the nodes of T one
node at a time, but makes use of the fact that the nodes of T are already linked. The time required to erase
T(x) is still proportional to the number of nodes in T. This erasing of entire polynomials can be carried out
even more efficiently by modifying the list structure so that the LINK field of the last node points back to
the first node as in figure 4.8. A list in which the last node points back to the first will be termed a
circular list. A chain is a singly linked list in which the last node has a zero link field.
Circular lists may be erased in a fixed amount of time independent of the number of nodes in the list. The
algorithm below does this.
procedure CERASE(T)
//return the circular list T to the available pool//
if T = 0 then return;
X LINK (T)
LINK(T) AV
AV X
end CERASE
Page 52
10CS35
procedure INVERT(X)
//a chain pointed at by X is inverted so that if X = (a1, ...,am)
then after execution X = (am, ...,a1)//
p X;q 0
while p 0 do
r q;q p //r follows q; q follows p//
p LINK(p)
//p moves to next node//
LINK(q) r
//link q to previous node//
end
X q
end INVERT
The reader should try this algorithm out on at least 3 examples: the empty list, and lists of length 1 and 2
to convince himself that he understands the mechanism. For a list of m 1 nodes, the while loop is
executed m times and so the computing time is linear or O(m).
Another useful subroutine is one which concatenates two chains X and Y.
procedure CONCATENATE(X, Y, Z)
//X = (a1, ...,am), Y = (b1, ...,bn), m,n 0, produces a new chain
Z = (a1, ...,am,b1 , ...,bn)//
Z X
if X = 0 then [Z Y; return]
if Y = 0 then return
p X
while LINK(p) 0 do //find last node of X//
p LINK(p)
end
LINK(p) Y
//link last node of X to Y//
end CONCATENATE
This algorithm is also linear in the length of the first list. From an aesthetic point of view it is nicer to
write this procedure using the case statement in SPARKS. This would look like:
procedure CONCATENATE(X, Y, Z)
case
: X = 0 :Z Y
:Y=0:Z X
: else : p X; Z X
while LINK(p) 0 do
p LINK (p)
end
LINK(p) Y
end
end CONCATENATE
Suppose we want to insert a new node at the front of this list. We have to change the LINK field of the
node containing x3. This requires that we move down the entire length of A until we find the last node. It
is more convenient if the name of a circular list points to the last node rather than the first.
CITSTUDENTS.IN
Page 53
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
Now we can write procedures which insert a node at the front or at the rear of a circular list and take a
fixed amount of time.
procedure INSERT FRONT(A, X)
//insert the node pointed at by X to the front of the circular list
A, where A points to the last node//
if A = 0 then [A X
LINK (X) A]
else [LINK(X) LINK (A)
LINK(A) X]
end INSERT--FRONT
To insert X at the rear, one only needs to add the additional statement A X to the else clause of INSERT__FRONT.
As a last example of a simple procedure for circular lists, we write a function which determines the length
of such a list.
procedure LENGTH(A)
//find the length of the circular list A//
i 0
if A 0 then [ptr A
repeat
i i + 1; ptr LINK(ptr)
until ptr = A ]
return (i)
end LENGTH
CITSTUDENTS.IN
Page 54
10CS35
If a matrix A is stored in ordinary (dense) format, then the command S = sparse(A) creates a copy of the
matrix stored in sparse format. For example:
>> A = [0 0 1;1 0 2;0 -3 0]
A=
0
0 -3
>> S = sparse(A)
S=
(2,1)
(3,2)
-3
(1,3)
(2,3)
>> whos
Name
Size
Bytes Class
3x3
72 double array
3x3
64 sparse array
CITSTUDENTS.IN
Page 55
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
>> A(3,2)=-1;
>> A
A=
(3,1)
(1,2)
(3,2)
-1
(Of course, for this to be truly useful, the nonzeros would be added in a loop.)
Another version of the sparse command is S = sparse(I,J,S,m,n,maxnz). This creates an
sparse
0 -16
64 -16
0 -16
0 -16
0 -16 64
-16
0 -16
0 64 -16
0 -16
0
0
0 -16
64 -16
0 -16
0 -16 64
0 -16
0 -16
0
0
0
0 -16
0
0 -16
0 -16
0 64 -16
0 -16
64 -16
0 -16 64
This is a
matrix with 5 nonzero diagonals. In Matlab's indexing scheme, the nonzero diagonals of A
are numbers -3, -1, 0, 1, and 3 (the main diagonal is number 0, the first subdiagonal is number -1, the first
superdiagonal is number 1, and so forth). To create the same matrix in sparse format, it is first necessary
to create a
matrix containing the nonzero diagonals of A. Of course, the diagonals, regarded as
column vectors, have different lengths; only the main diagonal has length 9. In order to gather the various
CITSTUDENTS.IN
Page 56
10CS35
diagonals in a single matrix, the shorter diagonals must be padded with zeros. The rule is that the extra
zeros go at the bottom for subdiagonals and at the top for superdiagonals. Thus we create the following
matrix:
>> B = [
-16 -16 64
-16 -16
-16
64 -16
0 64 -16
-16 -16 64
-16 -16
-16
0
0
0
0 -16
64 -16 -16
0 64 -16 -16
0 -16 64
0 -16
0 64 -16 -16
];
(notice the technique for entering the rows of a large matrix on several lines). The spdiags command also
needs the indices of the diagonals:
>> d = [-3,-1,0,1,3];
The matrix is then created as follows:
S = spdiags(B,d,9,9);
The last two arguments give the size of S.
Perhaps the most common sparse matrix is the identity. Recall that an identity matrix can be created, in
dense format, using the command eye. To create the
identity matrix in sparse format, use I =
speye(n). Another useful command is spy, which creates a graphic displaying the sparsity pattern of a
matrix. For example, the above penta-diagonal matrix A can be displayed by the following command; see
Figure 6:
>> spy(A)
CITSTUDENTS.IN
Page 57
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
CITSTUDENTS.IN
Page 58
10CS35
return;
}
*px = p -> info;
q = p -> left;
r = p -> right;
q -> right = r;
r -> left = q;
freenode( p );
return;
}
A node can be inserted on the right or on the left of a given node. Let us consider insertion at right side of
a given node. The routine insert right inserts a node with information field x to right of node(p) in a
doubly linked list.
insertright( p, x) {
NODEPTR p, q, r;
int x;
if ( p = = NULL ) {
printf( Void Insertion \n);
return;
}
q = getnode();
q -> info = x;
r = p -> right;
r -> left = q;
q -> right = r;
q -> left = p;
p -> left = q;
CITSTUDENTS.IN
Page 59
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
return;
}
CITSTUDENTS.IN
Page 60
10CS35
UNIT 5 : TREES 1
5.1 Introduction:
A tree is a finite set of one or more nodes such that: (i) there is a specially designated node called the
root; (ii) the remaining nodes are partitioned into n 0 disjoint sets T1, ...,Tn where each of these sets is a
tree. T1, ...,Tn are called the subtrees of the root. A tree structure means that the data is organized so that
items of information are related by branches. One very common place where such a structure arises is in
the investigation of genealogies.
AbstractDataType tree{
instances
A set of elements:
(1) empty or having a distinguished root element
(2) each non-root element having exactly one parent element operations
root()
degree()
child(k)
}
Some basic terminology for trees:
x
x
x
x
x
x
x
Trees are formed from nodes and edges. Nodes are sometimes called vertices. Edges are
sometimes called branches.
Nodes may have a number of properties including value and label.
Edges are used to relate nodes to each other. In a tree, this relation is called "parenthood."
An edge {a,b} between nodes a and b establishes a as the parent of b. Also, b is called a child of
a.
Although edges are usually drawn as simple lines, they are really directed from parent to child. In
tree drawings, this is top-to-bottom.
Informal Definition: a tree is a collection of nodes, one of which is distinguished as "root,"
along with a relation ("parenthood") that is shown by edges.
Formal Definition: This definition is "recursive" in that it defines tree in terms of itself. The
definition is also "constructive" in that it describes how to construct a tree.
1. A single node is a tree. It is "root."
2. Suppose N is a node and T1, T2, ..., Tk are trees with roots n1, n2, ...,nk, respectively. We
can construct a new tree T by making N the parent of the nodes n1, n2, ..., nk. Then, N is
the root of T and T1, T2, ..., Tk are subtrees.
CITSTUDENTS.IN
Page 61
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
More terminology
x
x
x
x
x
Definition: A path is a sequence of nodes n1, n2, ..., nk such that node ni is the parent of node ni+1
for all 1 <= i <= k.
Definition: The length of a path is the number of edges on the path (one less than the number of
nodes).
Definition: The descendents of a node are all the nodes that are on some path from the node to
any leaf.
Definition: The ancestors of a node are all the nodes that are on the path from the node to the
root.
Definition: The depth of a node is the length of the path from root to the node. The depth of a
node is sometimes called its level.
Definition: The height of a node is the length of the longest path from the node to a leaf.
Definition: the height of a tree is the height of its root.
Representation of trees
CITSTUDENTS.IN
Page 62
10CS35
1. List representation
2. left child right sibling representation
3. Representation as a degree two tree
x
x
Definition: A binary tree is a tree in which each node has degree of exactly 2 and the children of
each node are distinguished as "left" and "right." Some of the children of a node may be empty.
Formal Definition: A binary tree is:
1. either empty, or
2. it is a node that has a left and a right subtree, each of which is a binary tree.
Definition: A full binary tree (FBT) is a binary tree in which each node has exactly 2 non-empty
children or exactly two empty children, and all the leaves are on the same level. (Note that this
definition differs from the text definition).
Definition: A complete binary tree (CBT) is a FBT except, perhaps, that the deepest level may
not be completely filled. If not completely filled, it is filled from left-to-right.
A FBT is a CBT, but not vice-versa.
A Binary Tree. As usual, the empty children are not explicitly shown.
CITSTUDENTS.IN
Page 63
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
Not a Complete Binary Tree. The tree above is not a CBT because the
deepest level is not filled from left-to-right.
Properties
Full tree A tree with all the leaves at the same level, and all the non-leaves having the same degree.
Complete Tree A full tree in which the last elements are deleted.
CITSTUDENTS.IN
Page 64
10CS35
Representations
1. Nodes consisting of a data field and k pointers
2. Nodes consisting of w data field and two pointers: a pointer to the first child, and a
Level order
CITSTUDENTS.IN
Page 65
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
x := root()
if( x ) queue (x)
while( queue not empty ){
x := dequeue()
visit()
i=1; while( i <= degree() ){
queue( child(i) )
}
}
Preorder
procedure preorder(x){
visit(x)
i=1; while( i <= degree() ){
preorder( child(i) )
}
}
Postorder
procedure postorder(x){
i=1; while( i <= degree() ){
postorder( child(i) )
}
visit(x)
}
Inorder
Meaningful just for binary trees.
procedure inorder(x){
if( left_child_for(x) ) { inorder( left_child(x) ) }
visit(x)
if( right_child_for(x) ) { inorder( right_child(x) ) }
}
CITSTUDENTS.IN
Page 66
10CS35
Usages for visit: determine the height, count the number of elements .
5.5. Heaps
A heap is a complete tree with an ordering-relation R holding between each node and its descendant.
Examples for R: smaller-than, bigger-than
Assumption: In what follows, R is the relation bigger-than, and the trees have degree 2.
Heap
Not a heap
Adding an Element
1. Add a node to the tree
CITSTUDENTS.IN
Page 67
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
2. Move the elements in the path from the root to the new node one position down, if they are
smaller than the new element
new element
modified tree
4. A complete tree of n nodes has depth log n , hence the time complexity is O(log n)
Deleting an Element
1. Delete the value from the root node, and delete the last node while saving its value.
before
CITSTUDENTS.IN
after
Page 68
10CS35
2. As long as the saved value is smaller than a child of the vacant node, move up into the vacant
node the largest value of the children.
Given a sequence of n values e1, ..., en, repeatedly use the insertion module on the n given values.
x
x
x
Efficient
x
For each node, starting from the last one and ending at the root, reorganize into a heap the subtree
whose root node is given. The reorganization is performed by interchanging the new element with
the child of greater value, until the new element is greater than its children.
CITSTUDENTS.IN
Page 69
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
k=1
(k - 1)2-
Applications:
Priority Queue A dynamic set in which elements are deleted according to a given ordering-relation.
Heap Sort Build a heap from the given set (O(n)) time, then repeatedly remove the elements from the
heap (O(n log n)).
Page 70
10CS35
Figure 6.1 Section of the river Pregal in Koenigsberg and Euler's graph.
Definitions and Terminology
A graph, G, consists of two sets V and E. V is a finite non-empty set of vertices. E is a set of pairs of
vertices, these pairs are called edges. V(G) and E(G) will represent the sets of vertices and edges of graph
G.
We will also write G = (V,E) to represent a graph.
In an undirected graph the pair of vertices representing any edge is unordered . Thus, the pairs (v1, v2)
and (v2, v1) represent the same edge.
In a directed graph each edge is represented by a directed pair (v1, v2). v1 is the tail and v2 the head of the
edge. Therefore <v2, v1> and <v1, v2> represent two different edges. Figure 6.2 shows three graphs G1, G2
and G3.
CITSTUDENTS.IN
Page 71
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
A path from vertex vp to vertex vq in graph G is a sequence of vertices vp,vi1,vi2, ...,vin,vq such that
(vp,vi1),(vi1,vi2), ...,(vin,vq) are edges in E(G). If G' is directed then the path consists of <vp,vi1>,<vi,vi2>,
...,<vin,vq>, edges in E(G').
CITSTUDENTS.IN
Page 72
10CS35
CITSTUDENTS.IN
Page 73
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
insert 6
CITSTUDENTS.IN
insert 10
Page 74
10CS35
Deletion
The way the deletion is made depends on the type of node holding the key.
Node of degree 0
Delete the node
Node of degree 1
Delete the node, while connecting its predecessor to the successor.
Node of degree 2
Replace the node containing the deleted key with the node having the largest key in the left
subtree, or with the node having the smallest key in the right subtree.
CITSTUDENTS.IN
Page 75
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
It takes O(log n) time to modify a selection tree in response to a change of a key in a leaf.
Initialization
The construction of a selection tree from scratch takes O(n) time by traversing it level-wise from bottom
up.
16 9 10 8 6 11 12 1 4 7 14 13 2 15 5 3
16 9 10 8
6 11 12 1
4 7 14 13
2 15 5 3
8 9 10 16
1 6 11 12
4 7 13 14
2 3 5 15
CITSTUDENTS.IN
M
=4
Page 76
10CS35
CITSTUDENTS.IN
Page 77
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
6.3 Forests
The default interdomain trust relationships are created by the system during domain controller creation.
The number of trust relationships that are required to connect n domains is n 1, whether the domains are
linked in a single, contiguous parent-child hierarchy or they constitute two or more separate contiguous
parent-child hierarchies.
When it is necessary for domains in the same organization to have different namespaces, create a separate
tree for each namespace. In Windows 2000, the roots of trees are linked automatically by two-way,
transitive trust relationships. Trees linked by trust relationships form a forest A single tree that is related
to no other trees constitutes a forest of one tree.
The tree structures for the entire Windows 2000 forest are stored in Active Directory in the form of
parent-child and tree-root relationships. These relationships are stored as trust account objects (class
trustedDomain ) in the System container within a specific domain directory partition. For each domain in
a forest, information about its connection to a parent domain (or, in the case of a tree root, to another tree
root domain) is added to the configuration data that is replicated to every domain in the forest. Therefore,
every domain controller in the forest has knowledge of the tree structure for the entire forest, including
knowledge of the links between trees. You can view the tree structure in Active Directory Domain Tree
Manager.
Page 78
10CS35
Page 79
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
CITSTUDENTS.IN
Page 80
10CS35
Representations
Different data structures for the representation of graphs are used in practice:
x
x
Adjacency list Vertices are stored as records or objects, and every vertex stores a list of
adjacent vertices. This data structure allows the storage of additional data on the vertices.
Incidence list Vertices and edges are stored as records or objects. Each vertex stores its
incident edges, and each edge stores its incident vertices. This data structure allows the storage of
additional data on vertices and edges.
Adjacency matrix A two-dimensional matrix, in which the rows represent source vertices and
columns represent destination vertices. Data on edges and vertices must be stored externally.
Only the cost for one edge can be stored between each pair of vertices.
Incidence matrix A two-dimensional Boolean matrix, in which the rows represent the vertices
and columns represent the edges. The entries indicate whether the vertex at a row is incident to
the edge at a column.
CITSTUDENTS.IN
Page 81
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
Deltemin(H)
Priority Queue H
Insert(H)
Implementation:
There are three ways for implementing priority queue. They are:
1. Linked list
2. Binary Search tree
3. Binary Heap
CITSTUDENTS.IN
Page 82
10CS35
Page 83
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
Although the figure shows each element stored in both the min and the max heap, it is necessary to store
each
element
in
only
one
of
the
two
heaps.
The isEmpty and size operations are implemented by using a variable size that keeps track of the number
of elements in the DEPQ. The minimum element is at the root of the min heap and the maximum element
is at the root of the max heap. To insert an element x, we insert x into both the min and the max heaps and
then set up correspondence pointers between the locations of x in the min and max heaps. To remove the
minimum element, we do a removeMin from the min heap and a remove(theNode), where theNode is the
corresponding node for the removed element, from the max heap. The maximum element is removed in
an
analogous
way.
Total and leaf correspondence are more sophisticated correspondence methods. In both of these, half
the elements are in the min PQ and the other half in the max PQ. When the number of elements is odd,
one element is retained in a buffer. This buffered element is not in either PQ. In total correspondence,
each element a in the min PQ is paired with a distinct element b of the max PQ. (a,b) is a corresponding
pair of elements such that priority(a) <= priority(b). Figure 2 shows a total correspondence heap for the
11 elements 2, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10. The element 9 is in the buffer. Corresponding pairs are shown
by red arrows.
In leaf correspondence, each leaf element of the min and max PQ is required to be part of a corresponding
pair. Nonleaf elements need not be in any corresponding pair. Figure 3 shows a leaf correspondence heap.
CITSTUDENTS.IN
Page 84
10CS35
Total and leaf correspondence structures require less space than do dual structures. However, the DEPQ
algorithms for total and leaf correspondence structures are more complex than those for dual structures.
Of the three correspondence types, leaf correspondence generally results in the fastest DEPQ
correspondence
structures.
Using any of the described correspondence methods, we can arrive at DEPQ structures from heaps, height
biased leftist trees, and pairing heaps. In these DEQP structures, the operations put(x), removeMin(), and
removeMax() take O(log n) time (n is the number of elements in the DEPQ, for pairing heaps, this is an
amortized
complexity),
and
the
remaining
DEPQ
operations
take
O(1)
time.
Notation. Let the s-value of a node be the shortest distance from the node to an external node.
x
x
CITSTUDENTS.IN
Page 85
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
Consider two nonempty height-biased leftist trees A and B, and a relation (e.g., smaller than) on
the values of the keys.
Assume the key-value of A is not bigger than the key-value of B
Let the root of the merged tree have the same left subtree as the root of A
Let the root of the merged tree have the right subtree obtained by merging B with the right
subtree of A.
If in the merged tree the s-value of the left subtree is smaller than the s-value of the right subtree,
interchange the subtrees.
For the following example, assume the key-value of each node equals its s-value.
CITSTUDENTS.IN
Page 86
10CS35
Time complexity
x
x
x
Binomial trees of order 0 to 3: Each tree has a root node with subtrees of all lower ordered binomial trees,
which have been highlighted. For example, the order 3 binomial tree is connected to an order 2, 1, and 0
(highlighted as blue, green and red respectively) binomial tree.
A binomial tree of order k has 2k nodes, height k.
Because of its unique structure, a binomial tree of order k can be constructed from two trees of order k1
trivially by attaching one of them as the leftmost child of root of the other one. This feature is central to
the merge operation of a binomial heap, which is its major advantage over other conventional heaps.The
name comes from the shape: a binomial tree of order
coefficient.)
CITSTUDENTS.IN
has
nodes at depth
. (See Binomial
Page 87
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
Each binomial tree in a heap obeys the minimum-heapproperty: the key of a node is greater than
or equal to the key of its parent.
There can only be either one or zero binomial trees for each order, including zero order.
The first property ensures that the root of each binomial tree contains the smallest key in the tree, which
applies to the entire heap.
The second property implies that a binomial heap with n nodes consists of at most log n + 1 binomial
trees. In fact, the number and orders of these trees are uniquely determined by the number of nodes n:
each binomial tree corresponds to one digit in the binary representation of number n. For example number
13 is 1101 in binary,
, and thus a binomial heap with 13 nodes will consist of three
binomial trees of orders 3, 2, and 0 (see figure below).
Example
of
a
binomial
heap
containing
13
The heap consists of three binomial trees with orders 0, 2, and 3.
nodes
with
distinct
keys.
Implementation
Because no operation requires random access to the root nodes of the binomial trees, the roots of the
binomial trees can be stored in a linkedlist, ordered by increasing order of the tree.
Merge
As mentioned above, the simplest and most important operation is the merging of two binomial trees of
the same order within two binomial heaps. Due to the structure of binomial trees, they can be merged
trivially. As their root node is the smallest element within the tree, by comparing the two keys, the smaller
of them is the minimum key, and becomes the new root node. Then the other tree become a subtree of the
combined tree. This operation is basic to the complete merging of two binomial heaps.
function mergeTree(p, q)
if p.root.key <= q.root.key
return p.addSubTree(q)
else
return q.addSubTree(p)
CITSTUDENTS.IN
Page 88
10CS35
To merge two binomial trees of the same order, first compare the root key. Since 7>3, the black tree on
the left(with root node 7) is attached to the grey tree on the right(with root node 3) as a subtree. The result
is a tree of order 3.
The operation of merging two heaps is perhaps the most interesting and can be used as a subroutine in
most other operations. The lists of roots of both heaps are traversed simultaneously, similarly as in the
mergealgorithm.
If only one of the heaps contains a tree of order j, this tree is moved to the merged heap. If both heaps
contain a tree of order j, the two trees are merged to one tree of order j+1 so that the minimum-heap
property is satisfied. Note that it may later be necessary to merge this tree with some other tree of order
j+1 present in one of the heaps. In the course of the algorithm, we need to examine at most three trees of
any order (two from the two heaps we merge and one composed of two smaller trees).
Because each binomial tree in a binomial heap corresponds to a bit in the binary representation of its size,
there is an analogy between the merging of two heaps and the binary addition of the sizes of the two
heaps, from right-to-left. Whenever a carry occurs during addition, this corresponds to a merging of two
binomial trees during the merge.
Each tree has order at most log n and therefore the running time is O(log n).
function merge(p, q)
while not( p.end() and q.end() )
tree = mergeTree(p.currentTree(), q.currentTree())
if not heap.currentTree().empty()
tree = mergeTree(tree, heap.currentTree())
heap.addTree(tree)
else
heap.addTree(tree)
heap.next() p.next() q.next()
CITSTUDENTS.IN
Page 89
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
This shows the merger of two binomial heaps. This is accomplished by merging two binomial trees of the
same order one by one. If the resulting merged tree has the same order as one binomial tree in one of the
two heaps, then those two are merged again.
Insert
Inserting a new element to a heap can be done by simply creating a new heap containing only this
element and then merging it with the original heap. Due to the merge, insert takes O(log n) time,however
it has an amortized time of O(1) (i.e. constant).
Find minimum
To find the minimum element of the heap, find the minimum among the roots of the binomial trees. This
can again be done easily in O(log n) time, as there are just O(log n) trees and hence roots to examine.By
using a pointer to the binomial tree that contains the minimum element, the time for this operation can be
reduced to O(1). The pointer must be updated when performing any operation other than Find minimum.
This can be done in O(log n) without raising the running time of any operation.
Delete minimum
To delete the minimum element from the heap, first find this element, remove it from its binomial tree,
and obtain a list of its subtrees. Then transform this list of subtrees into a separate binomial heap by
reordering them from smallest to largest order. Then merge this heap with the original heap. Since each
tree has at most log n children, creating this new heap is O(log n). Merging heaps is O(log n), so the entire
delete minimum operation is O(log n).
function deleteMin(heap)
min = heap.trees().first()
for each current in heap.trees()
if current.root < min then min = current
for each tree in min.subTrees()
tmp.addTree(tree)
heap.removeTree(min)
merge(heap, tmp)
Decrease key
CITSTUDENTS.IN
Page 90
10CS35
After decreasing the key of an element, it may become smaller than the key of its parent, violating the
minimum-heap property. If this is the case, exchange the element with its parent, and possibly also with
its grandparent, and so on, until the minimum-heap property is no longer violated. Each binomial tree has
height at most log n, so this takes O(log n) time.
Delete
To delete an element from the heap, decrease its key to negative infinity (that is, some value lower than
any element in the heap) and then delete the minimum in the heap.
Performance
All of the following operations work in O(log n) time on a binomial heap with n elements:
x
x
x
x
x
x
Finding the element with minimum key can also be done in O(1) by using an additional pointer to the
minimum.
Applications Discrete event simulation, Priority queues
CITSTUDENTS.IN
Page 91
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
Figure 1. Example of a Fibonacci heap. It has three trees of degrees 0, 1 and 3. Three vertices are marked
(shown in blue). Therefore the potential of the heap is 9.
A Fibonacci heap is a collection of trees satisfying the minimum-heap property, that is, the key of a child
is always greater than or equal to the key of the parent. This implies that the minimum key is always at
the root of one of the trees. Compared with binomial heaps, the structure of a Fibonacci heap is more
flexible. The trees do not have a prescribed shape and in the extreme case the heap can have every
element in a separate tree. This flexibility allows some operations to be executed in a "lazy" manner,
postponing the work for later operations. For example merging heaps is done simply by concatenating the
two lists of trees, and operation decrease key sometimes cuts a node from its parent and forms a new tree.
However at some point some order needs to be introduced to the heap to achieve the desired running time.
In particular, degrees of nodes (here degree means the number of children) are kept quite low: every node
has degree at most O(log n) and the size of a subtree rooted in a node of degree k is at least Fk + 2, where
Fk is the kth Fibonaccinumber. This is achieved by the rule that we can cut at most one child of each nonroot node. When a second child is cut, the node itself needs to be cut from its parent and becomes the root
of a new tree (see Proof of degree bounds, below). The number of trees is decreased in the operation
delete minimum, where trees are linked together.
As a result of a relaxed structure, some operations can take a long time while others are done very
quickly. In the amortized running time analysis we pretend that very fast operations take a little bit longer
than they actually do. This additional time is then later subtracted from the actual running time of slow
operations. The amount of time saved for later use is measured at any given moment by a potential
function. The potential of a Fibonacci heap is given by
Potential = t + 2m
where t is the number of trees in the Fibonacci heap, and m is the number of marked nodes. A node is
marked if at least one of its children was cut since this node was made a child of another node (all roots
are unmarked).
Thus, the root of each tree in a heap has one unit of time stored. This unit of time can be used later to link
this tree with another tree at amortized time 0. Also, each marked node has two units of time stored. One
can be used to cut the node from its parent. If this happens, the node becomes a root and the second unit
of time will remain stored in it as in any other root.
Implementation of operations
To allow fast deletion and concatenation, the roots of all trees are linked using a circular, doublylinked
list. The children of each node are also linked using such a list. For each node, we maintain its number of
CITSTUDENTS.IN
Page 92
10CS35
children and whether the node is marked. Moreover we maintain a pointer to the root containing the
minimum key.
Operation find minimum is now trivial because we keep the pointer to the node containing it. It does not
change the potential of the heap, therefore both actual and amortized cost is constant. As mentioned
above, merge is implemented simply by concatenating the lists of tree roots of the two heaps. This can be
done in constant time and the potential does not change, leading again to constant amortized time.
Operation insert works by creating a new heap with one element and doing merge. This takes constant
time, and the potential increases by one, because the number of trees increases. The amortized cost is thus
still constant.
Fibonacci heap from Figure 1 after first phase of extract minimum. Node with key 1 (the minimum) was
deleted and its children were added as separate trees.
Operation extract minimum (same as delete minimum) operates in three phases. First we take the root
containing the minimum element and remove it. Its children will become roots of new trees. If the number
of children was d, it takes time O(d) to process all new roots and the potential increases by d1. Therefore
the amortized running time of this phase is O(d) = O(log n).
Fibonacci heap from Figure 1 after extract minimum is completed. First, nodes 3 and 6 are linked
together. Then the result is linked with tree rooted at node 2. Finally, the new minimum is found.
However to complete the extract minimum operation, we need to update the pointer to the root with
minimum key. Unfortunately there may be up to n roots we need to check. In the second phase we
therefore decrease the number of roots by successively linking together roots of the same degree. When
two roots u and v have the same degree, we make one of them a child of the other so that the one with the
smaller key remains the root. Its degree will increase by one. This is repeated until every root has a
different degree. To find trees of the same degree efficiently we use an array of length O(log n) in which
we keep a pointer to one root of each degree. When a second root is found of the same degree, the two are
linked and the array is updated. The actual running time is O(log n + m) where m is the number of roots at
the beginning of the second phase. At the end we will have at most O(log n) roots (because each has a
different degree). Therefore the difference in the potential function from before this phase to after it is:
O(log n) m, and the amortized running time is then at most O(log n + m) + O(log n) m = O(log n).
Since we can scale up the units of potential stored at insertion in each node by the constant factor in the
O(m) part of the actual cost for this phase.
CITSTUDENTS.IN
Page 93
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
In the third phase we check each of the remaining roots and find the minimum. This takes O(log n) time
and the potential does not change. The overall amortized running time of extract minimum is therefore
O(log n).
Fibonacci heap from Figure 1 after decreasing key of node 9 to 0. This node as well as its two marked
ancestors are cut from the tree rooted at 1 and placed as new roots.
Operation decrease key will take the node, decrease the key and if the heap property becomes violated
(the new key is smaller than the key of the parent), the node is cut from its parent. If the parent is not a
root, it is marked. If it has been marked already, it is cut as well and its parent is marked. We continue
upwards until we reach either the root or an unmarked node. In the process we create some number, say k,
of new trees. Each of these new trees except possibly the first one was marked originally but as a root it
will become unmarked. One node can become marked. Therefore the potential decreases by at least k 2.
The actual time to perform the cutting was O(k), therefore the amortized running time is constant.
Finally, operation delete can be implemented simply by decreasing the key of the element to be deleted to
minus infinity, thus turning it into the minimum of the whole heap. Then we call extract minimum to
remove it. The amortized running time of this operation is O(log n).
Proof of degree bounds
The amortized performance of a Fibonacci heap depends on the degree (number of children) of any tree
root being O(log n), where n is the size of the heap. Here we show that the size of the (sub)tree rooted at
any node x of degree d in the heap must have size at least Fd+2, where Fk is the kth Fibonaccinumber. The
degree bound follows from this and the fact (easily proved by induction) that
integers
, where
for all
, and
as required.)
Consider any node x somewhere in the heap (x need not be the root of one of the main trees). Define
size(x) to be the size of the tree rooted at x (the number of descendants of x, including x itself). We prove
by induction on the height of x (the length of a longest simple path from x to a descendant leaf), that
size(x) Fd+2, where d is the degree of x.
Base case: If x has height 0, then d = 0, and size(x) = 1 = F2.
Inductive case: Suppose x has positive height and degree d>0. Let y1, y2, ..., yd be the children of x,
indexed in order of the times they were most recently made children of x (y1 being the earliest and yd the
latest), and let c1, c2, ..., cd be their respective degrees. We claim that ci i-2 for each i with 2id: Just
before yi was made a child of x, y1,...,yi1 were already children of x, and so x had degree at least i1 at
that time. Since trees are combined only when the degrees of their roots are equal, it must have been that
yi also had degree at least i-1 at the time it became a child of x. From that time to the present, yi can only
have lost at most one child (as guaranteed by the marking process), and so its current degree ci is at least
i2. This proves the claim.
CITSTUDENTS.IN
Page 94
10CS35
Since the heights of all the yi are strictly less than that of x, we can apply the inductive hypothesis to them
to get size(yi) Fci+2 F(i2)+2 = Fi. The nodes x and y1 each contribute at least 1 to size(x), and so we have
for any
Worst case
Although the total running time of a sequence of operations starting with an empty structure is bounded
by the bounds given above, some (very few) operations in the sequence can take very long to complete (in
particular delete and delete minimum have linear running time in the worst case). For this reason
Fibonacci heaps and other amortized data structures may not be appropriate for real-time systems. It is
possible to create a data structure which the same worst case performance as the Fibonacci heap has
amortized performance.[3] However the resulting structure is very complicated, so it is not useful in most
practical cases.
The amortized time per delete-min is O(logn).[1] The operations find-min, merge, and insert take
x
x
x
x
Implementation
CITSTUDENTS.IN
Page 95
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
A pairing heap is either an empty heap, or a pair consisting of a root element and a possibly empty list of
pairing heaps. The heap ordering property requires that all the root elements of the subheaps in the list are
not smaller that then root element of the heap. The following description assumes a purely functional
heap that does not support the decrease-key operation.
type PairingHeap[Elem] = Empty | Heap(elem: Elem, subheaps: List[PairingHeap[Elem]])
Operations
find-min
The function find-min simply returns the root element of the heap:
function find-min(heap)
if heap == Empty
error
else
return heap.elem
merge:
Merging with an empty heap returns the other heap, otherwise a new heap is returned that has the
minimum of the two root elements as its root element and just adds the heap with the larger root to the list
of subheaps:
function merge(heap1, heap2)
if heap1 == Empty
return heap2
elsif heap2 == Empty
return heap1
elseif heap1.elem < heap2.elem
return Heap(heap1.elem, heap2 :: heap1.subheaps)
else
return Heap(heap2.elem, heap1 :: heap2.subheaps)
Insert:
The easiest way to insert an element into a heap is to merge the heap with a new heap containing
just this element and an empty list of subheaps:
function insert(elem, heap)
return merge(Heap(elem, []), heap)
delete-min:
The only non-trivial fundamental operation is the deletion of the minimum element from the heap. The
standard strategy first merges the subheaps in pairs (this is the step that gave this datastructure its name)
from left to right and then merges the resulting list of heaps from right to left:
fuction delete-min(heap)
if heap == Empty
CITSTUDENTS.IN
Page 96
10CS35
error
elsif length(heap.subheaps) == 0
return Empty
elsif length(heap.subheaps) == 1
return heap.subheaps[0]
else
return merge-pairs(heap.subheaps)
This uses the auxiliary function merge-pairs:
function merge-pairs(l)
if length(l) == 0
return Empty
elsif length(l) == 1
return l[0]
else
return merge(merge(l[0], l[1]), merge-pairs(l[2.. ]))
That this does indeed implement the described two-pass left-to-right then right-to-left merging strategy
can be seen from this reduction:
merge-pairs([H1, H2, H3, H4, H5, H6, H7])
=> merge(merge(H1, H2), merge-pairs([H3, H4, H5, H6, H7]))
# merge H1 and H2 to H12, then the rest of the list
=> merge(H12, merge(merge(H3, H4), merge-pairs([H5, H6, H7])))
# merge H3 and H4 to H34, then the rest of the list
=> merge(H12, merge(H34, merge(merge(H5, H6), merge-pairs([H7]))))
# merge H5 and H5 to H56, then the rest of the list
=> merge(H12, merge(H34, merge(H56, H7)))
# switch direction, merge the last two resulting heaps, giving H567
=> merge(H12, merge(H34, H567))
# merge the last two resulting heaps, giving H34567
=> merge(H12, H34567)
# finally, merge the first merged pair with the result of merging the rest
=> H1234567
Page 97
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
CITSTUDENTS.IN
Page 98
10CS35
that key must appear in its left subtree while all keys greater than it must appear in its right subtree.
Stating the recursive algorithm based on these observations requires some notations:
OBST(i, j) denotes the optimal binary search tree containing the keys ki,
ki+1, , kj;
Wi, j denotes the weight matrix for OBST(i, j)
Wi, j can be defined using the following formula:
Page 99
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
Definitions
Height
Claim: AVL trees are balanced.
Proof. Let Nh denote the number of nodes in an AVL tree of depth h
Nh > Nh-1 + Nh-2 + 1
> 2Nh-2 + 1
> 1 + 2(1 + 2Nh-4)
= 1 + 2 + 22N h-4
> 1 + 2 + 22 + 23N h-6
...
> 1 + 2 + 22 + 23 + ... + 2h/2
= 2h/2 1
Hence,
2h/2 - 1 < n
h/2
A more careful analysis, based on Fibonacci numbers theory, implies the tighter bound of 1.44 log 2(n +
2).
CITSTUDENTS.IN
Page 100
10CS35
Rotations
LL
RR
LR
RL
CITSTUDENTS.IN
Page 101
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
LL
&
LR
LL
Delete 4
CITSTUDENTS.IN
Page 102
10CS35
Empty subtrees of a node are treated as subtrees with roots of black color.
The relation n > 2h/2 - 1 implies the bound h < 2 log 2(n + 1).
Insertions
x
x
x
Insert the new node the way it is done in binary search trees
Color the node red
If a discrepancy arises for the red-black tree, fix the tree according to the type of discrepancy.
A discrepancy can result from a parent and a child both having a red color. The type of discrepancy is
determined by the location of the node with respect to its grand parent, and the color of the sibling of the
parent.
Discrepancies in which the sibling is red, are fixed by changes in color. Discrepancies in which the
siblings are black, are fixed through AVL-like rotations.
Changes in color may propagate the problem up toward the root. On the other hand, at most one rotation
is sufficient for fixing a discrepancy.
LLr
CITSTUDENTS.IN
Page 103
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
LRr
LLb
LRb
Discrepancies of type RRr, RLr, RRb, and RLb are handled in a similar manner.
insert 1
insert 2
insert 3
RRb discrepancy
CITSTUDENTS.IN
Page 104
10CS35
insert 4
RRr discrepancy
insert 5
RRb discrepancy
Deletions
x
x
x
Delete a key, and a node, the way it is done in binary search trees.
A node to be deleted will have at most one child. If the deleted node is red, the tree is still a redblack tree. If the deleted node has a red child, repaint the child to black.
If a discrepancy arises for the red-black tree, fix the tree according to the type of discrepancy. A
discrepancy can result only from a loss of a black node.
Let A denote the lowest node with unbalanced subtrees. The type of discrepancy is determined by the
location of the deleted node (Right or Left), the color of the sibling (black or red), the number of red
children in the case of the black siblings, and and the number of grand-children in the case of red siblings.
In the case of discrepancies which result from the addition of nodes, the correction mechanism may
propagate the color problem (i.e., parent and child painted red) up toward the root, and stopped on the
way by a single rotation. Here, in the case of discrepancies which result from the deletion of nodes, the
discrepancy of a missing black node may propagate toward the root, and stopped on the way by an
application of an appropriate rotation.
Rb0
change of color, sending the deficiency up to the root of
the subtree
Rb1
CITSTUDENTS.IN
Page 105
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
Rb2
Rr0
might result in LLb discrepancy of parent and child having
both the red color
Rr1
CITSTUDENTS.IN
Page 106
10CS35
Rr2
Similar transformations apply to Lb0, Lb1, Lb2, Lr0, Lr1, and Lr2.
y
Zig (terminal case):
x
z
/
Zig-zag:
z
y
\
x
z
/
Zig-zig:
x
/ ====>
y
y
/
x
/
====>
/
y
x
x ====>
y z
x
y
\
====> / \ ====>
x z
\
z
/\
Notes (1) Each rule has a mirror image variant, which covers all the cases.
(2) The zig-zig rule is the one that distinguishes splaying from just rotating x to the root of the tree.
(3) Top-down splaying is much more efficient in practice. Code for doing this is on my web site
(www.cs.cmu.edu/~sleator).
Here are some examples:
CITSTUDENTS.IN
Page 107
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
6
0
3
/
\
/\
5
5
/ \
/
/\
/ \
4 splay(0)
3 6 splay(3) 0
/ =======>
/\
=======>
3
1 4
1 4 6
/
\
\
2
2
2
/
1
/
0
10CS35
5
\ /\
To analyze the performance of splaying, we start by assuming that each node x has a weight w(x) > 0.
These weights can be chosen arbitrarily. For each assignment of weights we will be able to derive a
bound on the cost of a sequence of accesses. We can choose the assignment that gives the best bound.
By giving the frequently accessed elements a high weight, we will be able to get tighter bounds on the
running time. Note that the weights are only used in the analysis, and do not change the algorithm at all.
(A commonly used case is to assign all the weights to be 1.)
Before we can state our performance lemma, we need to define two more quantities. The size of a node x
(denoted s(x)) is the total weight of all the nodes in the subtree rooted at x. The rank of a node x (denoted
r(x)) is the floor(log_2) of the size of x. Restating these:
9o
/
8o
/\
6o o1
/\
2o o3
/ /
1o o2
\
o1
3o
/
3o
/\
2o o0
/\
1o o1
/ /
0o o1
\
o0
Page 108
10CS35
(1) Doing a rotation between a pair of nodes x and y only effects the ranks of the nodes x and y, and no
other nodes in the tree. Furthermore, if y was the root before the rotation, then the rank of y before the
rotation equals the rank of x after the rotation.
(2) Assuming all the weights are 1, the potential of a balanced tree is O(n), and the potential of a long
chain (most unbalanced tree) is O(n log n).
(3) In the banker's view of amortized analysis, we can think of having r(x) tokens on node x.
Access lemma: The number of splaying steps done when splaying node x in a tree with root t is at most
3(r(t)-r(x))+1.
Proof:
As we do the work, we must pay one token for each splay step we do. Furthermore we must make sure
that there are always r(x) tokens on node x as we do our restructuring. We are going to allocate 3(r(t) r(x)) +1 tokens to do the splay. Our job in the proof is to show that this is enough.
First we need the following observation about ranks, called the Rank Rule. Suppose that two siblings
have the same rank, r. Then the parent has rank at least r+1. This is because if the rank is r, then the size
is at least 2^r. If both siblings have size at least 2^r, then the total size is at least 2^(r+1) and we conclude
that the rank is at least r+1. We can represent this with the following diagram:
x
/\
r r
Conversly, suppose we find a situation where a node and its parent have the same rank, r. Then the other
sibling of the node must have rank < r.So if we have three nodes configured as follows, with these ranks:
r
/\
x r
Then x < r
Now we can go back to proving the lemma. The approach we take is to show that the 3(r'(x) - r(x))
tokens are sufficient to pay for the a zig-zag or a zig-zig steps. And that 3(r'(x) - r(x)) +1 is sufficient to
pay for the zig step. (Here r'() represents the rank function after the step, and r() represents the rank
function before the step.)
When we sum these costs to compute the amortized cost for the entire splay operation, they telescope to:
2x
Page 109
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
/
/\
2o
0o o2
/
/
2o
splay(x)
2o
/
==========>
/\
2x
1o o0
/\
/
0o o1
0o
/
o0
----------------Total: 9
---------------7
We allocated:
3(2-2)+1 = 1
extra tokens from restructuring: 9-7 = 2
-----3
There are 2 splay steps. So 3 > 2, and we have enough.
It remains to show these bounds for the individual steps. There are three cases, one for each of the types
of splay steps.
Zig:
ro
/
ao
or
==>
\
o b <= r
The actual cost is 1, so we spend one token on this. We take the tokens on a and augment them with
another r-a and put them on b. Thus the total number of tokens needed is 1+r-a. This is at most 1+3(r-a).
Zig-zag: We'll split it into 2 cases:
Case 1: The rank does not increase between the starting node and ending node of the step.
ro
/
ro
\
ro
or
/\
===> a o o b
By the Rank Rule, one of a or b must be < r, so one token is released from the data structure. we use this
to pay for the work.
Thus, our allocation of 3(r-r) = 0 is sufficient to pay for this.
or
Page 110
10CS35
/\
===> c o o d
The tokens on c can be supplied from those on b. (There are enough there cause b >= c.)
Note that r-a > 0. So:
use r-a (which is at least 1) to pay for the work
use r-a to augment the tokens on a to make enough for d
Summing these two gives: 2(r-a). But 2(r-a) <= 3(r-a), which completes the zig-zag case.
Zig-zig: Again, we split into two cases.
Case 1: The rank does not increase between the starting node and the
ending node of the step.
ro
/
ro
/
ro
ro
or
/\
\
=====> r o o d ====>
o c <= r
\
(d<r by rank rule)
od<r
As in the zig-zag case, we use the token gained because d < r to pay for the step. Thus we have
3(r-r)=0 tokens and we need 0.
Case 2: The rank increases during the step.
ro
/
bo
/
ao
or
\
============>
o c <= r
\
o d <= r
Page 111
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
3 log(n) + 1
We now switching to the world of potentials (potential = total tokens in the tree). To bound the cost of
the sequence we add this amount for each splay, then add the initial minus the final potential. The initial
potential is at most n log(n), and the final potential is at least 0. This gives a bound of:
Splaying
Splay step at x
let p(x) = parent of node x
case 1 (zig) p(x) = root of the tree
case 2 (zig-zig) p(x) is not the root and x and p(x) are both left (right) children
case 3 (zig-zag) p(x) is not the root and x is a left (right) child and p(x) is a right(left ) child
CITSTUDENTS.IN
Page 112
10CS35
Splay B
Splay vs. Move-to-root
Case 1
Case 2
Case 3
CITSTUDENTS.IN
Page 113
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
Move-to-root A
Splay A
CITSTUDENTS.IN
Page 114
10CS35
so we get:
CITSTUDENTS.IN
Page 115
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
size(x) =
rank(x) = lg(size(x))
Example
Let w(x) = 1/n where n is the number of nodes in the tree
Lemma The amortized time to splay node x in a tree with root at t is at most 3(r(t) - r(x)) + 1 =
O(lg(s(t)/s(x)))
Let s, r denote the size, rank functions before a splay
Let s', r' denote the size, rank functions after a splay
Count rotations
Case 1 (zig) One rotation
CITSTUDENTS.IN
Page 116
10CS35
Claim 1
Set b = 1-a
CITSTUDENTS.IN
Page 117
8
ir
p
ht res
tp en
:/ te
/a d
df by
.l :t
y/ ea
37 m
8 7 pi
53 r@
/t te
ea
mp
10CS35
We have
so
but lg(1/2)+lg(1/2)= -2
End claim 1
Claim 2 2r'(x) - r(x) - r'(z) >= 2
Recall that:
We have:
r(x) + r'(z) - 2r'(x) = lg(s(x)) + lg(s'(z)) - 2lg(s'(x))
= lg(s(x)/s'(x)) + lg(s'(z)/s'(x))
Now s(x) + s'(z) <= s'(x)
(Why?)
so
0<= s(x)/s'(x) + s'(z)/s'(x) <= 1
Set s(x)/s'(x) = a and s'(z)/s'(x) =b in claim 1 to get
lg(s(x)/s'(x)) + lg(s'(z)/s'(x)) <= -2
CITSTUDENTS.IN
Page 118
10CS35
CITSTUDENTS.IN
Page 119