Professional Documents
Culture Documents
Lecture 1&2 PDF
Lecture 1&2 PDF
•Understanding data structures and algorithms is crucial for writing efficient and effective code
Data Structures
Definitions
•Data structures are the way in which data is organized and stored in a computer
•Different types of data structures are better suited for different types of tasks
5
Example (adjacent African
countries)
Problem
Definition: adjacency: if two countries share a boundary, the two countries are adjacent.
Given a country X, print a country Z that is not adjacent to X, but is adjacent to a country Y
adjacent to X.
◦ for example,
◦ Input: Egypt
◦ Output: Algeria, Niger, Chad, South Sudan, Central African Republic, Ethiopia, Eritria
7
Come up with Data Structures
Suppose you have only the following information
◦ for each country x, the list of countries that are adjacent to country x.
◦ for example,
◦ Egypt: Libya, Sudan
How are you going to store this adjacency information to solve the problem efficiently?
8
Lessons
Arrays have a fixed size, which must be specified at the time of declaration
struct Node {
int data;
Node* next;
};
Linked lists have dynamic size, as nodes can be added or removed at any time
Linked lists have a faster insertion and deletion time compared to arrays
12
Stacks
• There are several types of trees, including binary trees and AVL (self-balancing binary search tree) trees
struct Node {
int data;
Node* left;
Node* right;
};
15
Graphs
17
Algorithms
•Algorithms can be divided into two types:
• Sorting algorithms, which rearrange elements in a specific order
• Searching algorithms, which search for a specific element in a data structure
•Algorithms can also be used on graphs, including graph traversal algorithms and graph search
algorithms
18
C++ versus C
C vs C++
In CSE 121, we focused on C
// Function to print the elements of the linked list // Function to print the elements of the linked list
void print_list(struct Node* node) { void print_list(Node* node) {
while (node != NULL) { while (node != NULL) {
printf("%d ", node->data); cout << node->data << " ";
node = node->next; node = node->next;
} }
} }
struct Node {
int data;
Node* next;
};
To insert a new node at the beginning of the linked list, we can use the following code:
newNode->next = head; // Point the next pointer of the new node to head
cout << temp->data << " "; // Print the data of the current node
•The above code will print the data of all the nodes in the linked list
Linked List Deletion
To delete a node from the linked list, we can use the // Find the node to be deleted
following code:
while (temp != NULL && temp->data !=
Node* temp = head; // Initialize a temp key) {
pointer to head
prev = temp;
Node* prev = NULL; // Initialize a prev
pointer to NULL temp = temp->next;
next = current->next;
head = prev;
The above code will reverse the linked list and update the head pointer to the new first node.
Stacks in C++
Stack Declaration and Push
Operation
In C++, a stack can be implemented using an array or a linked list
int pop() {
if (top == -1) { // If stack is empty, show underflow error
cout << "Stack underflow" << endl;
return -1;
}
int item = stack[top]; // Store the top item
top--; // Decrement top
return item; // Return the removed item
}
Peek Operation
The peek operation returns the top item of the stack without removing it
int peek() {
if (top == -1) { // If stack is empty, show underflow error
cout << "Stack underflow" << endl;
return -1;
}
return stack[top]; // Return the top item
}
Stack Traversal
void display() {
if (top == -1) { // If stack is empty, show underflow error
cout << "Stack is empty" << endl;
return;
}
cout << "Stack elements: ";
for (int i = 0; i <= top; i++) { // Loop through stack elements
cout << stack[i] << " ";
}
cout << endl;
}
Queues in C++
Queue Declaration & Enqueue
Operation
In C++, a queue can be implemented using an array or a linked list
int dequeue() {
if (front > rear) { // If queue is empty, show underflow error
cout << "Queue underflow" << endl;
return -1;
}
int item = queue[front]; // Store the front item
front++; // Increment front
return item; // Return the removed item
}
Peek Operation
The peek operation returns the front item of the queue without removing it
int peek() {
if (front > rear) { // If queue is empty, show underflow error
cout << "Queue is empty" << endl;
return -1;
}
return queue[front]; // Return the front item
}
Queue Traversal
To traverse a queue, we can use the following code:
void display() {
if (front > rear) { // If queue is empty, show underflow error
cout << "Queue is empty" << endl;
return;
}
cout << "Queue elements: ";
for (int i = front; i <= rear; i++) { // Loop through queue
elements
cout << queue[i] << " ";
}
cout << endl;
}
Complexity Analysis
& Asymptotic
Notation
Introduction to Algorithm
Complexity
•Algorithm complexity refers to the amount of resources (such as time and memory) an algorithm
requires to solve a problem of a given size.
•The goal of algorithm complexity analysis is to compare the relative efficiency of different
algorithms for the same problem.
Time Complexity
•Time complexity of an algorithm is the amount of time it takes to run as a function of the size of
the input.
•In C++, the time complexity of an algorithm is often measured in terms of the number of basic
operations (such as additions, multiplications, and comparisons) performed by the algorithm.
Asymptotic Notation
•Asymptotic notation is a way of expressing the behavior of an algorithm in terms of the size of the
input, as it approaches infinity.
•It describes an algorithm's worst-case time complexity, meaning the maximum time the algorithm
could take for any input size.
•For example, the time complexity of a linear search algorithm is O(n), meaning that the algorithm
takes proportional to the size of the input (n).
Ω and Θ Notation
•The Ω notation provides a lower bound on the growth rate of a function.
•It describes an algorithm's best-case time complexity, meaning the minimum time the algorithm
could take for any input size.
•The Θ notation is used to describe an algorithm's average-case time complexity, and it provides a
tight bound on the growth rate of a function. It means that the algorithm's performance is within a
constant factor of the lower and upper bounds.
Space Complexity of Algorithms
•Space complexity of an algorithm refers to the amount of memory it uses as a function of the size
of the input.
•The goal of analyzing space complexity is to compare the relative efficiency of different algorithms
in terms of their memory usage.
Calculating Space Complexity
•Space complexity can be calculated by considering the amount of memory required by the
algorithm for different parts of the computation, such as:
• Memory required to store input data
• Memory required to store intermediate results
• Memory required for function calls
•The space complexity of an algorithm is usually expressed in terms of the size of the input (n) and
is usually denoted by O(f(n)), where f(n) is a function that describes the memory usage of the
algorithm.