Data Structures Lab Syllabus 2024-25
Data Structures Lab Syllabus 2024-25
Converting an infix expression (where operators are placed between operands) to a postfix expression (where operators follow their operands) involves the use of a stack data structure to temporarily hold operators and ensure proper precedence and associativity. The algorithm iterates over each token in the infix expression while differentiating between operand and operator precedence, using the stack to manage operators . Operators are pushed onto the stack until lower precedence operators or right parentheses are encountered, at which point the stack is popped to the output until the higher priority operator or left parenthesis is removed. Parentheses act as override tools to take control of precedence as this allows order differences seen naturally in expression evaluation, maintaining the sequence of operations that would be executed in an infix manner without evaluating them immediately . Care must be taken to process operators appropriately to avoid misordered expressions or priority mishandling.
Depth-first search (DFS) and breadth-first search (BFS) are fundamental graph traversal algorithms that differ primarily in their exploration strategy. DFS explores as far as possible along one branch before backtracking, favoring depth over breadth, and typically utilizes a stack structure, either explicitly or via recursion. It is well-suited for algorithms working to pathfind in mazes or puzzles where the depth from the start node is relevant . BFS, in contrast, explores all the nodes at the present depth level before proceeding to nodes at the next depth level, using a queue to track the traversal frontier. This strategy is optimal for finding the shortest path in unweighted graphs and is useful in level-order traversal of trees. BFS also assures the shortest path construct in any unweighted graph or tree, whereas DFS could potentially find suboptimal paths if constraints aren't applied .
Topological sorting is a linear ordering of vertices in a Directed Acyclic Graph (DAG) such that for every directed edge u-v, vertex u comes before v in the ordering. It is applicable only to DAGs because it assumes the absence of cycles, which guarantees a partial order where certain nodes precede others based on dependency . If applied to graphs with cycles, topological sorting would contradict the cycle's constraints as it would inherently demand an ordering violating the cycle's expectations. The significance of topological sorting lies in its application to scenarios like task scheduling, where tasks are interconnected by dependency constraints and need to be executed in a non-cyclic order to respect prerequisites. It is crucial in areas like compiling tasks, order of execution planning, and the resolution of dependencies in version control .
Skip lists offer several advantages over traditional linked lists, especially in scenarios where fast search and update times are important. By using multiple layers of linked lists and allowing multiple pointers for each node, skip lists provide a probabilistic alternative to balanced trees for maintaining order among elements . Their average complexity for search, insert, and delete operations is O(log n), similar to that of balanced trees, making them more efficient than singly linked lists which perform these operations in O(n) time. This efficiency is particularly beneficial in concurrent programming where mutable data structures can pose challenges. The layered structure allows certain operations to progress quickly while maintaining simplicity and ease of implementation compared to self-balanced trees .
A circular linked list is similar to a singly linked list but differs in that the last node points back to the first node rather than null, creating a loop-like structure . A doubly linked list, on the other hand, includes pointers in both directions, allowing traversal both forwards and backwards. This bidirectional access simplifies deletions and insertions as nodes have direct access to their predecessors . Circular lists are particularly advantageous in scenarios where iterations over the list are frequent or continuously rotating over elements is required, such as in round-robin scheduling in operating systems. This is because a circular list does not require resetting to the head after reaching the end. However, when operations involve backward traversals or frequent removals, doubly linked lists are preferred for their flexibility in navigation .
Binary search trees (BST) provide a simple way to implement dynamic sets and support basic operations such as insert, delete, and search. They are relatively easy to implement and understand. However, BSTs can become unbalanced, leading to O(n) performance time in the worst-case scenario . AVL trees, which are a type of self-balancing binary search tree, maintain a balance factor for each node, ensuring that the height of the tree remains approximately logarithmically proportional to the number of nodes (O(log n)), which improves the time complexity for operations. This balancing makes AVL trees preferable for situations where read operations are frequent and efficient access times are critical. The key disadvantage of AVL trees is the additional complexity and overhead for maintaining balance during insert and delete operations .
Quick sort stands out for its divide-and-conquer strategy, which recursively partitions the array around a pivot element, ensuring elements smaller than the pivot are on the left, and larger ones on the right. This partitioning divides the array into subarrays that are sorted independently. The efficiency derives from consistently reducing the problem size, leading to an average time complexity of O(n log n), though the worst-case scenario may reach O(n²) if poor pivot selections are made. Enhancements like randomized pivoting or median-of-three rule help counter these worst-cases . Quick sort is often preferred for in-place sorting due to its lower memory overhead compared to techniques like Merge sort, which requires additional storage. Its average logarithmic time efficiency and low overhead make it ideal for large datasets, granting significant advantages in scenarios where execution speed and memory usage are important, such as in system software development and general-purpose datasets sorting .
Hashing provides an efficient approach to determine the frequency of each character in a string by mapping characters to their frequency count using a hash table. For each character in the string, the hash table updates the count associated with that character, leveraging the constant-time complexity of hash lookups and insertions on average, resulting in an overall time complexity of O(n) concerning the string's length . The advantages of using hashing for this task include ease of implementation, high efficiency in both time and space, and the ability to handle large datasets rapidly. It provides immediate access to each character's frequency distribution, enabling quick statistical analyses or preprocessing tasks necessary for subsequent data processing steps, such as text compression or language parsing .
Linear search iterates through each element in the array sequentially until the key is found or the end of the array is reached, with a time complexity of O(n). It is simple to implement but inefficient for large datasets . Binary search requires the array to be sorted beforehand and follows a divide-and-conquer approach by repeatedly dividing the search interval in half. If the key is less than the middle element, the search continues in the left subarray; otherwise, it continues in the right subarray. Binary search offers better time complexity of O(log n), significantly improving performance on large datasets compared to linear search. However, the prerequisite of having a sorted array adds overhead .
Sparse matrices are best represented using data structures that allow efficient storage of only non-zero elements to save space. Using arrays for sparse matrices involves storing non-zero elements alongside their row and column indices, commonly known as the Compressed Sparse Row (CSR) format. This allows for efficient fixed-time access to elements but can be space-consuming relative to a linked list due to redundant storage and potential wasted space for dynamic scaling . Linked list representations, such as linked nodes containing row, column, and value information, enable dynamic allocation and deallocation of elements, which can be more space-efficient, particularly for very sparse matrices. The trade-off lies in the access time; linked lists typically require traversal of nodes to access specific elements, which is typically slower compared to arrays. Therefore, arrays may be preferred when the non-zero elements are spread regularly, while linked lists are more suitable for irregular sparse distributions .