EE3490E - Fundamentals of
Embedded Programmings
Chapter 4: Data
Structures & Algorithms
© DIA 2020.2
Content
4.1. Introduction of data structures
4.2. Arrays and dynamic memory management
4.3. Vector structure
4.4. List structure
4.5. Sorting algorithms
4.6. Recursion
4.7. Bitwise operation
4.8. Event-driven programming
Chapter 4: Data Structures & Algorithms © DIA 2020.2 2
4.1 Introduction
Many real-life applications require complex data structures,
basic data type may not be sufficient to represent
Examples:
Student data: name, birthday, hometown, student identification (ID),
etc.
Transfer function: numerator, denominator polynomial
State space model: A, B, C, C matrices
Process (sensing) data: parameter name, measurement range, value,
unit, timestamp, accuracy, threshold, etc.
Graphic object: size, color, line weight, font, etc.
Data structure representation method: define a new data
type using structure (struct, class, union, etc.)
Chapter 4: Data Structures & Algorithms © DIA 2020.2 3
Problem: Represent a set of data
Most of the data belonging to an application are related to
each other it needs to represent a set with structure, e.g.:
Student list: data are in alphabet order
Generic model for a control system: include multiple components
interacting with each other
Process (sensing) data: a data set which carries value of an input at
discrete time, input values are related to output values
Graphic object: a window includes some graphic objects, a schematic
also include a number of graphic objects
In general, data in the same set has the same type, or at least
compatible types
Array is not always suitable
Chapter 4: Data Structures & Algorithms © DIA 2020.2 4
Problem: Manage (a set of) data
Any data set can be represented by properly utilizing
data structure and array
Algorithms (functions) process the data, in order to
manage it efficiently:
Add a new data record into a list, a table, a set, etc.
Delete a record from a list, a table, a set, etc.
Search a record in a list, a table, a set, etc. based on a criteria
Sort a list to meet a criteria
…
Chapter 4: Data Structures & Algorithms © DIA 2020.2 5
How to manage data efficiently
Minimize memory usage: “overhead” information is
insignificant when being compared with main data
Fast and convenient access: time taken to add, search or
delete data record should be short
Flexibility: number of data records should not be
limited, does not need to be known in advance, suitable
for small and large scale problem
Efficiency of data management depends on
Data structure
Algorithms used to add, search, sort, delete data
Chapter 4: Data Structures & Algorithms © DIA 2020.2 6
Common data structures
Array (extended meaning): set of data which can be
accessed by index
List: a data set contains elements each one links to another
and the set can be accessed in sequence
Tree: a data set contains elements linked to each other and
can be accessed in sequence from the root
A tree of which each node has at most two children (two branches) is
called binary tree
Map: a data set with sorted elements which can be accessed
fast by using “key”
Queue: a data set with elements sorted in sequence,
elements can be pushed into one end of the queue and
extracted from the other end of the queue, i.e. first-in first-
out list (FIFO)
Chapter 4: Data Structures & Algorithms © DIA 2020.2 7
Common data structures (cont.)
Set: data structure with elements sorted in a certain manner
but can be accessed effectively
Stack: data structure with elements sorted in sequence but
can be accessed from one end, i.e. last-in first-out (LIFO) or
first-in last-out (FILO)
Hash table: data structure with elements sorted with integer
codes generated by a special function
Ring buffer (circular buffer): similar to queue that uses a
single, fixed-size buffer as if it were connected end-to-end.
If it is full, it will replace the first element with the new one.
In mathematical calculation and control system: vector,
matrix, polynomial, rational fraction, transfer function, etc.
Chapter 4: Data Structures & Algorithms © DIA 2020.2 8
4.2 Array and dynamic memory
allocation
Data can be represented by using array efficiently:
Read and write data quickly via index or address
Saving memory
Fixed size array:
char buffer[SIZE];
Student student_list[100];
Size of array must be known before compiling, users cannot
enter the number of elements, in another way, this number
cannot be a variable less flexible
Occupy a fixed slot in stack (if it is a local variable) or data
segment (if it is a global variable) inefficient and inflexible
use of memory
Chapter 4: Data Structures & Algorithms © DIA 2020.2 9
Dynamic array
Dynamic array is allocated in the memory as required, during the
run-time
#include <stdlib.h>/* C */
int n = 50;
...
float* p1= (float*) malloc(n*sizeof(float)); /* C */
double* p2= new double[n];// C++
A pointer is used to handle a dynamic array, the usage is similar
to a fixed array
p1[0] = 1.0f;
p2[0] = 2.0;
Once it is no longer in use free the allocated memory
free(p1); /* C */
delete [] p2;// C++
Chapter 4: Data Structures & Algorithms © DIA 2020.2 10
Memory allocation revision
4 memory segments: code segment, data segment,
stack, heap
Code segment (sometime called text segment)
Stores constants and constant types which have been defined
Read-only
Handled by the compiler
Data segment
Used when the program is running
Contains global and static variables (initialized or uninitialized)
Readable and writable
Handled by the compiler
Chapter 4: Data Structures & Algorithms © DIA 2020.2 11
Memory allocation revision (cont.)
Stack
Temporary memory for local variables (automatic extent)
Data are in sequence based on the rule FILO
Data is pushed in when a function is called and it is freed (pop
out) when exiting the function
Handled by the compiler
Heap (Free-store)
Dynamic memory allocation
Handled by developers (not the compiler)
Managed by standard functions (malloc(), calloc() or
realloc(), free()) in C or operators (new, delete) in
C++
Chapter 4: Data Structures & Algorithms © DIA 2020.2 12
Risks
Developer takes all the responsibility in managing the
dynamic memory
No support from the compiler in handling memory
May cause real-time issues
Storing data in stack is much faster than that in heap
Stack is managed by handling pointer, while using heap
requires internal memory management
It is better to use stack if possible
Chapter 4: Data Structures & Algorithms © DIA 2020.2 13
Allocating and free dynamic memory
C:
malloc(): input argument is the number of byte, return a non-type
pointer (void*) which contain the address of memory block
allocated (in heap), return 0 if failed
free(): input argument is a non-type pointer (void*), free
memory which has the address as provided
C++:
Operator new[] allocates space for array with data type and number
of elements as defined in heap, return a pointer with data type, or
return 0 if failed
Operator delete[] deallocate the space and its input argument is a
pointer
Operator new[] and delete[] are applicable to allocate and
deallocate space of variables, objects besides arrays
Chapter 4: Data Structures & Algorithms © DIA 2020.2 14
Dynamic memory handling
Functions which handle dynamic memory are provided
by standard library, these are the only method to access
the heap
Allocating memory
void *malloc(size_t size)
int *p = malloc(10 * sizeof(int));
int *p = (int *) malloc(10 * sizeof(int));
Chapter 4: Data Structures & Algorithms © DIA 2020.2 15
malloc()
If malloc() is called successfully, it returns a
pointer which points to the address of the allocated
memory
Other wise it returns NULL
In fact, computers have virtual memory which can be
considered unlimited
It is a good habit to verify the returning value of this
function
Chapter 4: Data Structures & Algorithms © DIA 2020.2 16
free()
free() release the memory segment allocated by
malloc()
The memory is freed and ready for subsequent usage
void free(void *p)
Don’t need type casting (cast) xxx * to void *
int *p = (int *) malloc(10 * sizeof(int));
...
free(p);
Chapter 4: Data Structures & Algorithms © DIA 2020.2 17
calloc()
calloc() is similar to malloc() but the memory is
initialized with zero value (clear allocate)
Its interface is a bit different from malloc()
void *calloc(size_t n, size_t size)
int *p = calloc(10, sizeof(int));
Chapter 4: Data Structures & Algorithms © DIA 2020.2 18
realloc()
realloc() is used to change the memory allocated
by malloc(), calloc(), or realloc()
void *realloc(void *p, size_t size)
Multiple features
If p is NULL, it is the same as malloc()
If it is not done successfully, it returns NULL, the memory is
reserved
If size = 0, it is the same as free(), and returns NULL
Chapter 4: Data Structures & Algorithms © DIA 2020.2 19
More on realloc()
realloc() is used to increase or decrease dynamic
memory
If it increases, the existing elements are unchanged and
the newly added elements have no initial values
If it decreases, the existing elements are the unchanged
However, if the memory space is not sufficient,
realloc() will allocate new memory block and copy the
whole old memory block to the new one, then delete the
old one
This action disables the pointer which point to the old memory
block
Chapter 4: Data Structures & Algorithms © DIA 2020.2 20
Dynamic memory management
Handled by the developer
It includes
Pointer which points to the memory block
Allocating and free memory block
Size of the memory block
Errors may occur in most of the C applications.
Chapter 4: Data Structures & Algorithms © DIA 2020.2 21
Example of wrong usage
char *string_duplicate(char *s)
/* Dynamically allocate a copy of a
string. User must remember to free this
memory. */
{
/* +1 for ’\0’ */
char *p = malloc(strlen(s)+1);
return strcpy(p, s);
}
char *s1;
s1 = string_duplicate("this is a string");
...
free(s1);
Chapter 4: Data Structures & Algorithms © DIA 2020.2 22
Common errors
The pointer points to an undefined value
“memory corruption”
The pointer points to NULL
Program halts
Free a pointer pointing to a memory block which is not
dynamic memory like stack, constant data
Not free memory after using (memory leak).
Access elements which are not in the range of the
allocated array
Chapter 4: Data Structures & Algorithms © DIA 2020.2 23
Good habbits
Each malloc() should be followed by free()
Avoid memory corruption memory leak
Always use malloc() and free() within a function
Always build create() function and then destroy() with
complex objects
Pointer must be declared when starting
Initialized as NULL or an existing value
NULL means “point to nowhere”
Pointer should be set as NULL after being freed
free(NULL) has no meaning (no effect)
Chapter 4: Data Structures & Algorithms © DIA 2020.2 24
Example: Matrix
A matrix with fixed size (3x3), size of matrix can be
defined at compiler time
The following example shows a matrix created as
required by user
double **matrix = create_matrix(2,3);
matrix[0][2] = 5.4;
...
destroy_matrix(matrix);
Chapter 4: Data Structures & Algorithms © DIA 2020.2 25
Example: Matrix (cont.)
double **create_matrix1(int m, int n) {
double **p;
int i;
/* Allocate pointer array. */
p = (double **) malloc(m * sizeof(double*));
/* Allocate rows. */
for (i = 0; i < m; ++i)
p[i] = (double *) malloc(n * sizeof(double));
return p;
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 26
Example: Matrix (cont.)
double **create_matrix2(int m, int n) {
double **p; int i;
assert(m>0 && n>0);
p = (double **) malloc(m * sizeof(double*));
if (p == NULL)
return p;
for (i = 0; i < m; ++i) { /* Allocate rows. */
p[i] = (double *) malloc(n * sizeof(double));
if (p[i] == NULL)
goto failed; /* Allocation failed */
}
return p;
failed:
for (−−i; i >= 0; −−i)
free(p[i]); /* delete allocated memory.*/
free(p);
return NULL;
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 27
Example: Matrix (cont.)
/* Destroy an (m x n) matrix. Notice, the n
variable is not used, it is just there to
assist using the function. */
void destroy_matrix1(double **p, int m, int n) {
int i;
assert(m>0 && n>0);
for (i = 0; i < m; ++i)
free(p[i]);
free(p);
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 28
Example: Matrix (cont.)
double **create_matrix(int m, int n) {
double **p, *q;
int i;
assert(m>0 && n>0);
p = (double **) malloc(m * sizeof(double*));
if (p == NULL)
return p;
q = (double *) malloc(m * n * sizeof(double));
if (q == NULL) {
free(p);
return NULL;
}
for (i = 0; i < m; ++i, q += n)
p[i] = q;
return p;
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 29
Example: Matrix (cont.)
void destroy_matrix(double **p)
/* Destroy a matrix. Notice, due to the
method by which this matrix was created,
the size of the matrix is not required.*/
{
free(p[0]);
free(p);
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 30
Extendable array
Extend array to improve the disadvantage of fixed-size
array in C
Size of the array is up to requirement. Elements are
added to the end of the array and indexes are updated
automatically
Disadvantages:
The array always occupies memory
Adding new element results in copying all the old ones to new
positions
Thus, a memory block should be allocated instead of
providing each element a memory slot
Chapter 4: Data Structures & Algorithms © DIA 2020.2 31
Extendable array
Method
Each memory block:
newsize = K * oldsize;
Usually, we choose K = 2
Demonstration of realloc() usage
Chapter 4: Data Structures & Algorithms © DIA 2020.2 32
Example: Extendable array
/* Vector access operations. */
int push_back(int item);
int pop_back(void);
int* get_element(int index);
/* Manual resizing operations. */
int get_size(void);
int set_size(int size);
int get_capacity(void);
int set_capacity(int size);
Chapter 4: Data Structures & Algorithms © DIA 2020.2 33
Example: Extendable array (cont.)
#include "vector.h"
#include <stdlib.h>
#include <assert.h>
/* Private interface */
/* initial vector capacity */
static const int StartSize = 1;
/* geometric growth of vector capacity */
static const float GrowthRate = 1.5;
/* pointer to vector elements */
static int *data = NULL;
/* current size of vector */
static int vectorsize = 0;
/* current reserved memory for vector */
static int capacity = 0;
Chapter 4: Data Structures & Algorithms © DIA 2020.2 34
Example: Extendable array (cont.)
/* Add element to back of vector. Return index of new
element if successful, and -1 if fails. */
int push_back(int item) {
/* If out-of-space, allocate more. */
if (vectorsize == capacity) {
int newsize = (capacity == 0) ? StartSize :
(int)(capacity*GrowthRate + 1.0);
int *p = (int *)realloc(data, newsize*sizeof(int));
if (p == NULL)
return -1;
capacity = newsize; /* update data-structure */
data = p;
}
data[vectorsize] = item; /* We have enough room. */
return vectorsize++;
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 35
Example: Extendable array (cont.)
/* Return element from back of vector, and remove
it
from the vector. */
int pop_back(void) {
assert(vectorsize > 0);
return data[−−vectorsize];
}
/* Return pointer to the element at the specified
index. */
int* get_element(int index) {
assert(index >= 0 && index < vectorsize);
return data + index;
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 36
Example: Extendable array (cont.)
/* Manual size operations. */
int get_size(void) {return vectorsize;}
int get_capacity(void) {return capacity;}
/* Set vector size.
Return 0 if successful, -1 if fails. */
int set_size(int size) {
if (size > capacity) {
int *p = (int *) realloc(data, size*sizeof(int));
if (p == NULL)
return −1;
/* allocate succeeds, update data-structure */
capacity = size;
data = p;
}
vectorsize = size;
return 0;
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 37
Example: Extendable array (cont.)
/* Shrink or grow allocated memory reserve for array.
A size of 0 deletes the array. Return 0 if
successful, -1 if fails. */
int set_capacity(int size) {
if (size != capacity) {
int *p = (int *) realloc(data, size*sizeof(int));
if (p == NULL && size > 0)
return −1;
capacity = size;
data = p;
}
if (size < vectorsize)
vectorsize = size;
return 0;
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 38
Notes
A pointer is used to handle dynamic array, it is not a dynamic array
Allocate and deallocate memory space but not allocate and deallocate a
pointer
Free memory space only once
int* p;
p[0] = 1;// never do it
new(p);// access violation!
p = new int[100];// OK
p[0] = 1;// OK
int* p2=p;// OK
delete[] p2;// OK
p[0] = 1;// access violation!
delete[] p;// very bad!
p = new int[50];// OK, new array
...
Chapter 4: Data Structures & Algorithms © DIA 2020.2 39
Allocate dynamic memory for a single
variable
Purpose: Object can be created dynamically, while the program is
running, such as adding a new student to a list, drawing a new shape in
a schematic, inserting a component in a system, etc.
Syntax:
int* p = new int;
*p = 1;
p[0]= 2;// the same as above
p[1]= 1;// access violation!
int* p2 = new int(1);// with initialization
delete p;
delete p2;
Student* ps= new Student;
ps->code = 1000;
...
delete ps;
A single variable is not an array with one element
Chapter 4: Data Structures & Algorithms © DIA 2020.2 40
Summary on dynamic memory
Efficiency
Memory is allocated as much as it is required and when it is
required while the program is running
Memory is allocated within the free space of the computer
(heap), it depends on only computer memory
Allocated memory can be freed once it is not used anymore
Flexibility
Lifetime of the dynamically allocated memory may be longer
than the lifetime of the object allocated it
It is possible to call a function to allocated memory and another
function to deallocate it
The flexibility may cause memory leak
Chapter 4: Data Structures & Algorithms © DIA 2020.2 41
Example: using dynamic memory
Date* createDateList(int n) {
Date* p = new Date[n];
return p;
}
void main() {
int n;
cout << "Enter the number of your national holidays:";
cin >> n;
Date* date_list= createDateList(n);
for (int i=0; i < n; ++i) {
...
}
for (....) { cout << ....}
delete [] date_list;
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 42
Output argument is a pointer
void createDateList(int n, Date* &p) {
p = new Date[n];
}
void main() {
int n;
cout << "Enter the number of your national holidays:";
cin >> n;
Date* date_list;
createDateList(n, date_list);
for (int i=0; i < n; ++i) {
...
}
for (....) { cout<< ....}
delete [] date_list;
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 43
4.3 Vector structure
Problem: how to represent a mathematical vector in C/C++
Simple solution: normal dynamic array, but …
Inconvenient use: a user must call allocation and deallocation
functions himself; number of array dimensions need to be included
as well
Unsafe: a small mistake may cause a serious consequence
int n = 10;
double *v1,*v2, d;
v1 = (double*) malloc(n*sizeof(double));
v2 = (double*) malloc(n*sizeof(double));
d = scalarProd(v1,v2,n); // scalar_prod existed
d = v1 * v2;// OOPS!
v1.data[10] = 0;// OOPS!
free(v1);
free(v2);
Chapter 4: Data Structures & Algorithms © DIA 2020.2 44
Vector structure definition
File name: vector.h
Data structure:
structVector {
double *data;
int nelem;
};
Declaration of its basic functions:
Vector createVector(int n, double init);
void destroyVector(Vector);
double getElem(Vector, int i);
void putElem(Vector, int i, double d);
Vector addVector(Vector, Vector);
Vector subVector(Vector, Vector);
double scalarProd(Vector, Vector);
...
Chapter 4: Data Structures & Algorithms © DIA 2020.2 45
Define basic functions
File name: vector.cpp
#include <stdlib.h>
#include "vector.h"
Vector createVector(int n, double init) {
Vector v;
v.nelem= n;
v.data= (double*) malloc(n*sizeof(double));
while (n--) v.data[n] = init;
return v;
}
void destroyVector(Vector v) {
free(v.data);
}
double getElem(Vector v, int i) {
if (i < v.nelem&& i >= 0) return v.data[i];
return 0;
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 46
Define basic functions (cont.)
void putElem(Vector v, int i, double d) {
if (i >=0 && i < v.nelem) v.data[i] = d;
}
Vector addVector(Vector a, Vector b) {
Vector c = {0,0};
if (a.nelem== b.nelem) {
c = createVector(a.nelem,0.0);
for (inti=0; i < a.nelem; ++i)
c.data[i] = a.data[i] + b.data[i];
}
return c;
}
Vector subVector(Vector a, Vector b) {
Vector c = {0,0};
...
return c;
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 47
Usage
#include "vector.h"
void main() {
int n = 10;
Vector a, b, c;
a = createVector(10,1.0);
b = createVector(10,2.0);
c = addVector(a,b);
//...
destroyVector(a);
destroyVector(b);
destroyVector(c);
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 48
4.4 List structure
Problem: create a structure to manage dynamic data
efficiently and flexibly, e.g.:
Email
Todo list
Graphical objects in a figure
Dynamic blocks in a simulation model (similar to SIMULINK)
Requirements:
The number of records/elements in the list changes frequently
Add or delete data operations should be fast and simple
Minimize memory usage
Chapter 4: Data Structures & Algorithms © DIA 2020.2 49
Using array?
Number of elements in an array is actually fixed. Memory
space must be known when being allocated, it cannot be
extended or shrunk
If memory space used is less than allocated one wasting
memory
If memory space is full and more elements need to be
added, it is required to reallocate memory space and copy
the whole existing data to the new array time consuming
if the array size is large
If an element which needs to be added/deleted is at the first
position or in the middle of the array, it is required to copy
and shift the rest of the data time consuming
Chapter 4: Data Structures & Algorithms © DIA 2020.2 50
4.4.1 Linked list
pHead
Item A Data A
Item B Data B
Item C Data C
Item X Data X
Item Y 0x00 Data Z
Chapter 4: Data Structures & Algorithms © DIA 2020.2 51
Linked list: Insert data
pHead pHead
pHead Data T Data A
Data A Data B
Data B Data T
Data C Data C
Data X Data X
0x00 Data Z 0x00 Data Z
At the beginning of the list In the middle of the list
Chapter 4: Data Structures & Algorithms © DIA 2020.2 52
Linked list: Delete data
pHead pHead
Data A Data A
Data B Data B
Data C Data C
Data X Data X
0x00 Data Z 0x00 Data Z
At the beginning of the list In the middle of the list
Chapter 4: Data Structures & Algorithms © DIA 2020.2 53
Summary
Advantages:
Flexible usage, allocating memory when needed and
deallocating after using
Add/delete element via pointer; time taken to perform these
task is constant, doesn’t depend on data length or position
Access data in sequence
Disadvantages:
Added element must be allocated dynamic memory
Deleting element requires respected memory space to be freed
If data type is not large, the overhead may be dominant
Searching data is based on linear methods which consume
more time
Chapter 4: Data Structures & Algorithms © DIA 2020.2 54
Example: mail box
#include <string>
using namespace std;
struct MessageItem {
string subject;
string content;
MessageItem* pNext;
};
struct MessageList {
MessageItem* pHead;
};
void initMessageList(MessageList& l);
void addMessage(MessageList&, const string& sj,
const string& ct);
bool removeMessageBySubject(MessageList&l,
const string& sj);
void removeAllMessages(MessageList&);
Chapter 4: Data Structures & Algorithms © DIA 2020.2 55
Example: mail box (cont.)
#include "List.h"
void initMessageList(MessageList& l) {
l.pHead = 0;
}
void addMessage(MessageList& l, const string& sj,
conststring& ct) {
MessageItem* pItem = new MessageItem;
pItem->content = ct;
pItem->subject = sj;
pItem->pNext = l.pHead;
l.pHead = pItem;
}
void removeAllMessages(MessageList& l) {
MessageItem *pItem = l.pHead;
while (pItem != 0) {
MessageItem* pItemNext = pItem->pNext;
delete pItem;
pItem = pItemNext;
}
l.pHead = 0;
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 56
Example: mail box (cont.)
bool removeMessageBySubject(MessageList& l,
conststring& sj) {
MessageItem* pItem = l.pHead;
MessageItem* pItemBefore;
while (pItem != 0 && pItem->subject != sj) {
pItemBefore = pItem;
pItem = pItem->pNext;
}
if (pItem != 0) {
if (pItem == l.pHead)
l.pHead = 0;
else
pItemBefore->pNext = pItem->pNext;
delete pItem;
}
return pItem != 0;
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 57
Example: mail box usage (cont.)
#include <iostream>
#include "list.h"
using namespace std;
void main() {
MessageList myMailBox;
initMessageList(myMailBox);
addMessage(myMailBox,"Hi","Welcome, my friend!");
addMessage(myMailBox,"Test","Test my mailbox");
addMessage(myMailBox,"Lecture Notes","Programming Techniques");
removeMessageBySubject(myMailBox,"Test");
MessageItem* pItem = myMailBox.pHead;
while (pItem != 0) {
cout << pItem->subject << ":" << pItem->content << '\n';
pItem = pItem->pNext;
}
char c;
cin >> c;
removeAllMessages(myMailBox);
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 58
Homework
Create a linked-list consisting of public holidays of a
year and description of each day (as string), so that
A new public holiday can be added to the beginning of the list
Search for the description of the day (input argument is a date
including day and month)
Delete a public holiday at the beginning of the list
Delete a public holiday in the middle of the list (input argument
is a date including day and month)
Clear the whole list
Write a program to demonstrate the usage of the above
list
Chapter 4: Data Structures & Algorithms © DIA 2020.2 59
4.4.2 More on lists
Double Link List
Stack
Circular Buffer
Hash table
Chapter 4: Data Structures & Algorithms © DIA 2020.2 60
Double Link List
A Doubly Linked List (DLL) contains an extra pointer,
typically called previous pointer, together with next pointer
and data which are there in singly linked list.
struct List {
int item;
struct List *next;
struct List *prev;
};
Advantages:
A DLL can be traversed in both forward and backward direction
The delete operation in DLL is more efficient
We can quickly insert a new node before a given node
Chapter 4: Data Structures & Algorithms © DIA 2020.2 61
Double Link List example
struct List *insert_after(struct List *node, int item)
{
/* Allocate memory for new node. */
struct List *newnode = (struct List *)
malloc(sizeof(struct List));
if (newnode == NULL)
return NULL; /* allocation failed */
/* If list is not empty, splice new node into list. */
if (node) {
newnode−>next = node−>next;
node−>next = newnode;
}
else
newnode−>next = NULL;
newnode−>item = item;
return newnode;
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 62
typedef and Structure
typedef is similar to #define but it is a C keyword
It enables to create a new data type with a new name
typedef int Length;
Length len, maxlen;
Length lengths[50];
typedef simplifies the use of structure in C (but not required
in C++)
typedef struct Point {
int x;
int y;
} Point;
Point pt1, pt2;
Chapter 4: Data Structures & Algorithms © DIA 2020.2 63
typedef and Link List
Example:
typedef struct list_t List;
struct list_t {
int item;
List *next;
};
Main reasons to use typedef
Simplify the complex name
Example of function pointer
typedef int (*PFI)(char *, char *);
PFI pfarray[10];
Self-defined data type make the program easier to read, e.g.: Length
is more intelligible than int).
Chapter 4: Data Structures & Algorithms © DIA 2020.2 64
Example
Chapter 4: Data Structures & Algorithms © DIA 2020.2 65
Usage of typedef
typedef enable to hide the incompatible codes amongst
different microprocessors
E.g.: in a 32 bit computer, we can write:
typedef short INT16;
typedef int INT32;
In a 16 bit computer, they can be written as:
typedef int INT16;
typedef long INT32;
Chapter 4: Data Structures & Algorithms © DIA 2020.2 66
Usage of typedef
typedef enables to write the program compatible
with different data types
typedef int ValueType;
typedef struct List {
ValueType item;
struct List *next;
} List;
List *insert_back(List *node, ValueType item);
List *insert_after(List *node, ValueType item);
This is the most simple form of generic programming.
Chapter 4: Data Structures & Algorithms © DIA 2020.2 67
Stack
Basic Operations
Initializing, using it and then de-initializing the stack
Two primary operations:
push() − Pushing (storing) an element on the stack
pop() − Removing (accessing) an element from the stack
Chapter 4: Data Structures & Algorithms © DIA 2020.2 68
Stack
typedef struct Stack {
double buffer[MAXSIZE]; /* Stack buffer. */
int count; /* Number of elements in stack. */
} Stack;
void push(Stack *s, double item) {
s->buffer[s->count++] = item;
}
double pop(Stack *s) {
return s->buffer[--s->count];
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 69
Circular Buffer
An array with pre-defined size
It returns to the beginning position once it reaches the
last element
It is usually used in a real-time control system in which
many processes interacts with the object
Chapter 4: Data Structures & Algorithms © DIA 2020.2 70
Circular Buffer
1
1 2 3
6 7 8 9 3 4 5
6 7 8 9 A B 5
Chapter 4: Data Structures & Algorithms © DIA 2020.2 71
Circular Buffer
Chapter 4: Data Structures & Algorithms © DIA 2020.2 72
Software FIFO Implementation
(UART1 Receive)
Chapter 4: Data Structures & Algorithms © DIA 2020.2 73
Software FIFO Implementation
(UART1 Receive)
Chapter 4: Data Structures & Algorithms © DIA 2020.2 74
Software FIFO Implementation
(UART1 Receive)
Chapter 4: Data Structures & Algorithms © DIA 2020.2 75
Software FIFO Implementation
(UART1 Receive)
Chapter 4: Data Structures & Algorithms © DIA 2020.2 76
Circular Buffer
typedef struct CircBuf_t {
ValueType *array; /* Pointer to array of items */
int size; /* Maximum number of items in buffer */
int nelems; /* Current number of items in buffer */
int front; /* Index to front of buffer */
int back; /* Index to back of buffer */
} CircBuf;
Chapter 4: Data Structures & Algorithms © DIA 2020.2 77
Example
typedef double ValueType;
typedef struct CircBuf_t CircBuf;
/* create-destroy buffer */
CircBuf *create_buffer(int size);
void destroy_buffer(CircBuf *cb);
/* add-remove elements */
int add_item(CircBuf *cb, const ValueType *item);
int get_item(CircBuf *cb, ValueType *item);
/* query state */
int get_nitems(const CircBuf *cb);
int get_size(const CircBuf *cb);
Chapter 4: Data Structures & Algorithms © DIA 2020.2 78
Example
int add_item(CircBuf *cb, const ValueType *item) {
/* Add a new element to front of buffer.
Returns 0 for success, and -1 if buffer is full. */
if (cb−>nelems == cb−>size)
return −1;
cb−>array[cb−>front] = *item;
if (++cb−>front == cb−>size) /* wrap around */
cb−>front = 0;
++cb−>nelems;
return 0;
}
int get_item(CircBuf *cb, ValueType *item) {
/* Remove element from back of buffer, and assign it to *item.
Returns 0 for success, and -1 if buffer is empty. */
if (cb−>nelems == 0)
return −1;
−−cb−>nelems;
*item = cb−>array[cb−>back];
if (++cb−>back == cb−>size) /* wrap around */
cb−>back = 0;
return 0;
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 79
Hash Table
Data is stored in an array format, where each data value
has its own unique index value
Use for fast looking up if we know the index of desired
data
The most common implementation is a data array
combined with link-list
Basic Operations are Search, Insert, Delete
Chapter 4: Data Structures & Algorithms © DIA 2020.2 80
Example
typedef struct Dictionary_t Dictionary;
Dictionary *create_table(void);
void destroy_table(Dictionary *);
int add_word(Dictionary *, const char *key, const char
*defn);
char *find_word(const Dictionary *, const char *key);
void delete_word(Dictionary *, const char *key);
Chapter 4: Data Structures & Algorithms © DIA 2020.2 81
Example
#define HASHSIZE 101
struct Nlist {
char *word; /* search word */
char *defn; /* word definition */
struct Nlist *next; /* pointer to next entry in chain */
};
struct Dictionary t {
/* table is an array of pointers to entries */
struct Nlist *table[HASHSIZE];
};
static unsigned hash_function(const char *str) {
/* Hashing function converts a string to an index within hash table. */
const int HashValue = 31;
unsigned h;
for (h = 0; *str != ’\0’; ++str)
h = *str + HashValue * h;
return h % HASHSIZE;
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 82
Example
int add_word(Dictionary *dict, const char *key, const char *defn) {
/* Add new word to table. Replaces old definition if word already exists.
Return 0 if successful, and -1 is fails. */
unsigned i = hash function(key); /* get table index */
struct Nlist *pnode = dict−>table[i];
while (pnode && strcmp(pnode−>word, key) != 0) /* search chain */
pnode = pnode−>next;
if (pnode) { /* match found, replace definition */
char *str = allocate string(defn);
if (str == NULL) /* allocation fails, return fail and keep old defn */
return −1;
free(pnode−>defn);
pnode−>defn = str;
}
else { /* no match, add new entry to head of chain */
pnode = makenode(key, defn);
if (pnode == NULL)
return −1;
pnode−>next = dict−>table[i];
dict−>table[i] = pnode;
}
return 0;
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 83
Example
char *find_word(const Dictionary *dict, const char *key) {
/* Find definition for keyword.
Return NULL if key not found. */
unsigned i = hash function(key); /* get table index */
struct Nlist *pnode = dict−>table[i];
while (pnode && strcmp(pnode−>word, key) != 0)
pnode = pnode−>next; /* search index chain */
if (pnode) /* match found */
return pnode−>defn;
return NULL;
}
Chapter 4: Data Structures & Algorithms © DIA 2020.2 84