DS 2
DS 2
1
a) List different types of data structures.
Here are some of the common types of data structures:
Arrays: A collection of elements stored in contiguous memory locations.
Linked Lists: A linear data structure where elements are not stored in contiguous memory locations, but are linked to each other
using pointers.
Stacks: A LIFO (Last-In-First-Out) data structure where elements are added and removed from the same end.
Queues: A FIFO (First-In-First-Out) data structure where elements are added to one end (rear) and removed from the other end
(front).
Trees: A non-linear data structure where elements are organized in a hierarchical manner.
Graphs: A non-linear data structure consisting of nodes (vertices) and edges connecting them.
Hash Tables: A data structure that stores key-value pairs and provides efficient access to values based on their keys.
b) What is time complexity?
Time complexity is a measure of how the runtime of an algorithm increases with the size of the input data. It's a crucial concept in
computer science for analyzing the efficiency of algorithms and predicting their performance.
Time complexity is often expressed using Big O notation, which provides an upper bound on the growth rate of the algorithm's
runtime. Common time complexities include:
O(1): Constant time, the algorithm's runtime remains constant regardless of input size.
O(log n): Logarithmic time, the runtime grows logarithmically with the input size.
O(n): Linear time, the runtime grows linearly with the input size.
O(n log n): Linearithmic time, a common complexity for efficient sorting algorithms.
O(n^2): Quadratic time, the runtime grows quadratically with the input size.
O(2^n): Exponential time, the runtime grows exponentially with the input size.
By understanding time complexity, we can choose the most efficient algorithms for solving problems and make informed decisions
about the trade-offs between different approaches.
c) Explain Divide and Conquer strategy.
Divide and Conquer is a problem-solving strategy that involves breaking down a problem into smaller, self-similar subproblems,
solving each subproblem independently, and then combining the solutions to solve the original problem.
This approach often leads to efficient algorithms with lower time complexity.
This strategy is often used in algorithms like:
Merge Sort: Divides the array into two halves, recursively sorts each half, and then merges the sorted halves.
Quick Sort: Picks a pivot element, partitions the array into elements smaller and larger than the pivot, and recursively sorts the two
partitions.
Binary Search: Divides the sorted array in half in each step, discarding the half that doesn't contain the target element.
By breaking down a complex problem into simpler subproblems, Divide and Conquer can significantly improve the efficiency of
algorithms
d) Write representation of polynomial using linked list with suitable example.
A polynomial can be represented using a linked list where each node stores the coefficient and the exponent of a term.
For example, the polynomial 2x^3 + 4x^2 - 5x + 1 can be represented as a linked list with four nodes:
Node 1: Coefficient = 2, Exponent = 3
Node 2: Coefficient = 4, Exponent = 2
Node 3: Coefficient = -5, Exponent = 1
Node 4: Coefficient = 1, Exponent = 0
e) List applications of stack.
Stacks, a fundamental data structure, find applications in various domains:
Function Calls and Recursion:
Keeps track of function calls and their return addresses during recursion.
Manages local variables and parameters for each function call.
Expression Evaluation:
Converts infix expressions to postfix or prefix notation.
Evaluates postfix or prefix expressions using a stack.
Undo/Redo Functionality:
Stores a history of operations.
Allows users to undo or redo actions.
Browser History:
Stores the history of visited web pages.
Enables users to navigate back and forth.
Syntax Checking:
Checks for balanced parentheses and other syntactic elements.
Backtracking Algorithms:
Used to explore different possibilities and backtrack if a solution is not found.
2.2
f) Differentiate between circular queue & linear queue
Here are 5 key differences between circular and linear queues:
Memory Utilization:
Linear Queue: Can waste memory if elements are not inserted and deleted in a balanced manner.
Circular Queue: More efficient use of memory as it can reuse the space of deleted elements.
Implementation:
Linear Queue: Requires two pointers: front and rear.
Circular Queue: Requires two pointers: front and rear, but they are managed in a circular manner.
Queue Full Condition:
Linear Queue: Queue is full when the rear pointer reaches the end of the array.
Circular Queue: Queue is full when the next position of the rear pointer is the front pointer.
Queue Empty Condition:
Linear Queue: Queue is empty when the front and rear pointers are equal.
Circular Queue: Queue is empty when the front pointer is one position ahead of the rear pointer.
Flexibility:
Linear Queue: Less flexible, especially when dealing with fixed-size arrays.
Circular Queue: More flexible, as it can handle more elements efficiently.
a) What are different asymptotic notations?
Asymptotic notations are used to describe the growth rate of functions, particularly in the context of analyzing the time and space
complexity of algorithms. The most common asymptotic notations are:
Big O notation (O notation): Used to denote the upper bound of the time complexity, representing the worst-case scenario.
Omega notation (Ω notation): Used to denote the lower bound of the time complexity, representing the best-case scenario.
Theta notation (Θ notation): Used to denote both the upper and lower bounds of the time complexity, representing the average-
case scenario.
b) List the operations performed on dequeue.
A dequeue (double-ended queue) supports the following operations:
enqueueFront(x): Adds an element x to the front of the dequeue.
enqueueRear(x): Adds an element x to the rear of the dequeue.
dequeueFront(): Removes and returns the element from the front of the dequeue.
dequeueRear(): Removes and returns the element from the rear of the dequeue.
isEmpty(): Checks if the dequeue is empty.
isFull(): Checks if the dequeue is full (if it has a fixed size).
c) Write the postfix expression of the following (A+B) * (C-D).
Here's the step-by-step conversion of the infix expression (A+B) * (C-D) to its postfix equivalent:
Step 1: Scan the infix expression from left to right.
Encounter operand 'A': Add it to the postfix expression.
Encounter operand 'B': Add it to the postfix expression.
Encounter operator '+': Push it onto the stack.
Current postfix expression: AB+
Step 2: Encounter operator '*':
Since '*' has higher precedence than '+', pop '+' from the stack and add it to the postfix expression.
Push '*' onto the stack.
Current postfix expression: AB+
Step 3: Encounter operand 'C': Add it to the postfix expression.
Encounter operand 'D': Add it to the postfix expression.
Encounter operator '-': Push it onto the stack.
Current postfix expression: AB+CD-
Step 4: Encounter operator '*':
Since '*' has higher precedence than '-', pop '-' from the stack and add it to the postfix expression.
Push '*' onto the stack.
Current postfix expression: AB+CD-*
Step 5: Encounter closing parenthesis ')':
Pop operators from the stack and add them to the postfix expression until an opening parenthesis '(' is encountered.
Pop the opening parenthesis '(' from the stack and discard it.
Current postfix expression: AB+CD-*
Step 6: Encounter the end of the expression.
Pop any remaining operators from the stack and add them to the postfix expression.
Final postfix expression: AB+CD-*
Therefore, the postfix expression equivalent to (A+B) * (C-D) is AB+CD-*.
2.3
d) Write node structure of singly linked list.
struct Node {
int data;
struct Node* next;
};
a) Describe the term ADT.
An Abstract Data Type (ADT) is a mathematical model that defines a set of data and the operations that can be performed on that
data. It focuses on the logical properties of the data structure, rather than its implementation details. This allows for different
implementations of the same ADT, as long as they provide the same set of operations.
ADTs are used to abstract away the implementation details of data structures, making them easier to understand and use. They
promote code modularity, reusability, and maintainability. Some common examples of ADTs include stacks, queues, lists, trees, and
graphs.
b) What is the best case and worst case efficiency of quick sort?
Best Case: O(n log n)
Worst Case: O(n^2)