Comprehensive Study Guide for Data Structures and Algorithms
Introduction to Data Structures and Algorithms
- Data Structures: This refers to the arrangement of data so that they can be used efficiently in memory. It deals specifically with the organization of data items.
- Algorithms: This is a sequence of steps performed on data using efficient data structures to solve a given problem.
- Database: A collection of information stored in permanent storage used for faster retrieval and updation.
- Data Warehousing: The management of huge amounts of legacy data for the purpose of better analysis.
- Big Data: The analysis of data sets that are too large or complex to be dealt with by traditional data processing applications.
- Programming Context: Data structures and algorithms are fundamental to programming. For example, in languages like C, a programmer uses Arrays (a data structure) and a sequence of processing steps to solve a problem (an algorithm).
Memory Layout of C Programs
When a program starts, its code is copied to the main memory (RAM). The layout consists of several segments:
- Code Segment: This section contains the literal program code.
- Initialized Data Segment: This holds global and static variables that have been initialized by the programmer.
- Uninitialized Data Segment: This holds global and static variables that are not explicitly initialized (often called the BSS segment).
- Stack: This area of memory holds the memory occupied by functions, including local variables and function call frames.
- Heap: This contains data that is requested by the program as dynamic memory during runtime.
Time Complexity and Big O Notation
- Definition: Time Complexity is the study of the efficiency of algorithms. It determines how the time taken to execute an algorithm grows relative to the size of the input.
- The Scaling Problem (Pizza Example):
- If a brother needs to get 2 pizzas from Dominos ( away) on his bike, he can do it easily.
- If he needs to get pizzas for 29 friends, the input size () becomes too huge for him to handle in the same amount of time/way on a bike.
- Developer Comparison Example: Consider Shubham and Rohan, who developed sorting algorithms. Records of their runtimes for different input sizes () show how efficiency changes:
- For : Shubham (), Rohan ().
- For : Shubham (), Rohan ().
- For : Shubham (), Rohan ().
- For : Shubham (), Rohan ().
- Conclusion: While Shubham’s algorithm is faster for small inputs, Rohan's algorithm is superior as the input size increases.
- Data Transfer Example (GTA V):
- Sending a file (GTA V) to a friend away using a connection with a limit is inefficient.
- Online/Linear Sending: As the file size grows, the time taken for online sending increases linearly, represented as .
- Physical Sending: If you copy the game to a Hard Disk and deliver it physically, the time remains constant regardless of the file size (within reason), represented as or .
- Comparison: Small files like Minesweeper (in KBs) are better sent via internet, but massive files are more efficient to send physically.
Calculating and Visualizing Big O
- Order Calculation: To calculate the order of growth, only the most impactful term containing (input size) is considered, and lower-order terms and constants are ignored.
- Example 1: .
- Example 2: .
- Example 3: If the formula is just constants (), it results in .
- Visualizing Growth:
- (Constant) appears as a flat horizontal line on a time vs. input size graph.
- (Linear) appears as a straight diagonal line.
- Big O Complexity Chart (Better to Worse):
- Amazing: and
- Good:
- Fair:
- Bad: , , and
- Terrible:
- Common Runtime Hierarchy:
Asymptotic Notations
- Big Oh Notation (): Used to describe the asymptotic upper bound. It defines that for a given function , is if and only if there exist positive constants and such that for all . Note: if a function is , it is technically also .
- Big Omega Notation (): Used to describe the asymptotic lower bound. is if there exist positive constants and such that for all . Note: if a function is , it is automatically .
- Big Theta Notation (): This provides a tight bound by combining the upper and lower bounds. It means the function is "sandwiched". Mathematically, for all . This is the notation most developers mean when they say "Order of".
Case Analysis and Searching
- Best Case: When the algorithm performs optimally due to ideal input (e.g., finding the search item at the first position).
- Worst Case: When the input is as difficult as possible for the algorithm.
- Expected Case: The average of all possible case runtimes.
- Searching in a Sorted Array:
- Linear Search: Starts from the first element and moves sequentially until the element is found or a larger element is reached.
- Best Case:
- Worst Case:
- Binary Search: Continually halves the search range by checking the center element.
- Best Case:
- Worst Case:
- Linear Search: Starts from the first element and moves sequentially until the element is found or a larger element is reached.
- Understanding : This represents the number of times you need to half an array of size before it is exhausted. For example, because .
Space Complexity and Practical Rules
- Space Complexity: Measures how the memory requirements of an algorithm grow with input size.
- Creating an array of size results in space complexity.
- If a function calls itself recursively times, its space complexity is .
- Prohibiting Time in Seconds: Complexity cannot be calculated in seconds because hardware power varies between computers. Asymptotic analysis focuses on growth relative to input.
- Simplification Rules:
- Drop Constants: becomes .
- Drop Non-Dominant Terms: becomes .
- Consider All Variables: Use all input variables provided, e.g., painting a park of dimension results in .
Questions & Discussion
- Problem 1: Find the complexity of
func1which contains two separate loops, each running from tolength.- Answer: .
- Problem 2: Find the complexity of
funcwith a nested loop where bothiandjgo from ton.- Answer: .
- Problem 3: For a recursive algorithm where
i = random(n - 1)and the return isfunction(i) + function(n - 1 - i), what is the value of ? - Problem 4: Which are equivalent to ?
- a) where .
- b) .
- c) .
- d) .
- Problem 5: Summing values in a balanced binary search tree.
- Answer: because every node is visited once.
- Problem 6: Complexity of primality test
for (int i = 2; i * i < n; i++).- Answer: .
- Problem 7: Complexity of
for (int i = 2; i * i < 10000; i++).- Answer: as the loop runs a constant number of times.
Array Operations
- Traversal: Visiting every element of an array exactly once (e.g., printing or storing data using loops).
- Insertion: Adding an element at a specific position. Requires shifting elements to the right to maintain order. Requires checking array capacity.
- Deletion: Removing an element at a position. Creates a void that must be fixed by shifting subsequent elements to the left. Alternatively, the last element can fill the void if order is unimportant.
- Searching: Finding the index of a value. Faster in sorted arrays () than unsorted ().
- Sorting: Arranging elements in ascending or descending order.
Linked Lists
- Description: Linear data structures where elements are stored in non-contiguous memory locations. Each node contains data and a pointer to the next node.
- Pros vs. Cons:
- Pros: Dynamic capacity (no fixed size), easy insertion/deletion.
- Cons: Requires extra memory for pointers, no random access (must traverse sequentially).
- C Implementation:
c struct Node { int data; struct Node *next; }; - Deletion Cases:
- Deleting the first node (Update head pointer).
- Deleting at an index (Rewire previous node's pointer).
- Deleting the last node.
- Deleting a node with a specific value.
- Note: Memory must be freed using
free().
- Circular Linked List: The last element points back to the head node, forming a chain.
- Doubly Linked List: Each node contains pointers to both the previous and next nodes. Requires adjusting two pointers during insertion/deletion.
Stack Data Structure
- Definition: A linear data structure following the LIFO (Last In First Out) principle.
- Applications:
- Function calls.
- Infix to Postfix conversion.
- Parenthesis matching.
- Stack ADT Operations:
push(): Add an element to the top.pop(): Remove the topmost element.peek(index): Return value at a specific position.isEmpty()/isFull(): Check the status of the stack.
- Implementation: Can be achieved using either an Array or a Linked List.