Unit 5: Transform-and-Conquer and Advanced Algorithmic Techniques - Subjective Questions
CSE408 — Design And Analysis Of Algorithms • Practice Questions with Detailed Answers
20 questions
Define a balanced search tree. Why is balancing necessary in a binary search tree?
A balanced search tree is a binary search tree in which the height is kept proportional to the logarithm of the number of stored keys. For nodes, its height is typically .
Balancing is necessary because:
- An ordinary binary search tree can become skewed when keys are inserted in sorted or nearly sorted order.
- A skewed tree can have height , making search, insertion, and deletion linear-time operations.
- A balanced tree limits its height to .
- Therefore, search, insertion, and deletion can be performed in time in the worst case.
Examples of balanced search trees include AVL trees, red-black trees, 2-3 trees, and B-trees.
Explain the balance condition of an AVL tree and describe the four rotations used to restore balance after insertion.
For every node in an AVL tree, the balance factor is defined as:
The AVL balance condition requires to be one of , , or .
When insertion violates this condition, one of four cases occurs:
- Left-Left case: The new key is inserted in the left subtree of the left child. Perform a single right rotation.
- Right-Right case: The new key is inserted in the right subtree of the right child. Perform a single left rotation.
- Left-Right case: The new key is inserted in the right subtree of the left child. First rotate the left child to the left, then rotate the unbalanced node to the right.
- Right-Left case: The new key is inserted in the left subtree of the right child. First rotate the right child to the right, then rotate the unbalanced node to the left.
Rotations preserve the binary-search-tree ordering while restoring a height of .
Compare AVL trees and red-black trees with respect to balancing rules, operation costs, and practical applications.
AVL trees and red-black trees are self-balancing binary search trees, but they use different balancing policies.
- Balance strictness: AVL trees maintain a balance factor of , , or at every node. Red-black trees use coloring rules and permit greater height variation.
- Height: AVL trees are usually shorter and more rigidly balanced. A red-black tree with internal nodes has height at most .
- Search: AVL trees may provide faster searches because of their smaller height.
- Insertion and deletion: Red-black trees generally require fewer rotations and are often better for update-intensive workloads.
- Complexity: Both support search, insertion, and deletion in worst-case time.
- Applications: AVL trees are suitable for search-heavy systems, while red-black trees are common in standard-library maps, sets, schedulers, and operating-system structures.
Thus, AVL trees emphasize tighter balance, whereas red-black trees provide a practical compromise between search efficiency and update cost.
Describe how insertion and deletion are performed in a balanced search tree. Derive their worst-case time complexities.
Insertion:
- Follow the ordinary binary-search-tree search path to locate the insertion position.
- Insert the new key as a leaf.
- Move upward toward the root, updating heights, balance factors, colors, or other balancing information.
- Apply rotations or restructuring wherever a balancing rule is violated.
Deletion:
- Locate the key using binary search on the tree.
- If the node has two children, replace it with its inorder successor or predecessor.
- Remove the resulting node with at most one child.
- Traverse upward and repair balance violations using rotations, recoloring, merging, or redistribution, depending on the tree type.
If the balanced tree contains nodes, its height is . Searching for the affected location takes . Repair work is performed along at most one root-to-leaf path, so it also takes . Therefore:
The additional space is for an iterative implementation or for the recursion stack.
Explain the working of counting sort with a suitable example. State its time and space complexities.
Counting sort is a non-comparison sorting algorithm for integer keys drawn from a limited range through .
For the input :
- Create a count array initialized to zero.
- Count each key. The nonzero counts are , , , , and .
- Convert counts to cumulative counts when a stable output is required.
- Scan the input from right to left, placing each element in its final output position and decrementing its cumulative count.
- The sorted output is .
For input elements and key range size :
- Time complexity:
- Auxiliary space: for the stable version
Counting sort is efficient when , but it becomes wasteful when the key range is much larger than the number of elements.
Why is counting sort considered a stable non-comparison sort? Explain the role of cumulative counts and reverse traversal.
Counting sort is a non-comparison sort because it does not determine order by comparing pairs of keys. Instead, it counts how many times each key occurs.
For a count array , cumulative counts are computed as:
After this transformation, gives the number of elements with keys less than or equal to . Therefore, it identifies the final position immediately after the last occurrence of key .
To preserve stability, the input is traversed from right to left. For an element :
- Place it at position in the output array.
- Decrement .
Reverse traversal ensures that if two records have equal keys, the record appearing later in the input is placed later in the output. Thus, their original relative order is preserved.
Stability is essential when counting sort is used as an intermediate stable sorting step in algorithms such as least-significant-digit radix sort.
Describe least-significant-digit radix sort and trace it for the keys .
Least-significant-digit radix sort processes digits from the least significant position to the most significant position. A stable sorting algorithm, commonly counting sort, is applied at each digit position.
For decimal keys:
- Units digit pass:
- Tens digit pass:
- Hundreds digit pass:
The final array is sorted because every pass preserves the ordering established by earlier, less significant digits.
If there are digits, keys, and radix , and counting sort is used for each pass, the running time is:
The auxiliary space is . Radix sort is particularly effective when is bounded and is chosen so that each digit pass is efficient.
Distinguish between most-significant-digit radix sort and least-significant-digit radix sort. Why must the digit sort in LSD radix sort be stable?
LSD radix sort:
- Processes digits from the least significant digit to the most significant digit.
- Usually performs the same stable sorting operation on the complete array during every pass.
- Naturally handles fixed-length integer keys.
- Requires the digit-level sort to be stable.
MSD radix sort:
- Processes the most significant digit first.
- Divides keys into groups according to the current digit and recursively sorts each group using the next digit.
- Can stop early for groups of identical prefixes.
- Is useful for variable-length strings and lexicographic sorting.
Stability is required in LSD radix sort because the ordering produced for less significant digits must survive later passes. If two keys have the same current digit, a stable pass keeps them in the order determined by previously processed digits. Without stability, a later pass could destroy earlier ordering, and the final result might not be sorted.
Explain the steps of bucket sort and derive its expected running time under a uniform-distribution assumption.
Bucket sort distributes elements among several buckets, sorts each bucket, and concatenates the results.
For real numbers uniformly distributed in :
- Create empty buckets .
- Place value into bucket .
- Sort each bucket, often using insertion sort.
- Concatenate the buckets in index order.
Distribution and concatenation each require time. Let be the number of elements in bucket . If insertion sort is used, bucket sorting costs:
Under a uniform distribution, the expected number of elements per bucket is constant, and:
Therefore, the expected total running time is . If all elements fall into one bucket, however, insertion sort takes , giving the worst-case running time.
Compare counting sort, radix sort, and bucket sort in terms of assumptions, stability, complexity, and suitable input.
Counting sort:
- Assumes integer keys from a small known range.
- Runs in time, where represents the key range.
- Can be stable when cumulative counts and an output array are used.
- Is suitable when is not much larger than .
Radix sort:
- Assumes keys can be decomposed into digits or components.
- Runs in time for digits and radix .
- LSD radix sort requires a stable digit-level sort.
- Is suitable for fixed-width integers, identifiers, and strings.
Bucket sort:
- Assumes keys are approximately uniformly distributed over a known interval.
- Has expected complexity with suitable buckets, but can take in the worst case.
- Stability depends on how elements are inserted and how individual buckets are sorted.
- Is suitable for uniformly distributed real-valued data.
All three techniques can outperform comparison sorting because they exploit additional information about the keys or their distribution.
Define a monotonic stack. Explain how it can be used to find the next greater element for every element of an array.
A monotonic stack stores elements or indices in consistently increasing or decreasing order. Elements that violate the required order are removed before a new element is pushed.
To find the next greater element to the right:
- Initialize an empty stack of indices.
- Scan the array from left to right.
- While the stack is nonempty and the current element is greater than the element at the index on top of the stack, pop that index.
- Assign the current element as the next greater element for every popped index.
- Push the current index.
- Assign to indices remaining in the stack after the scan.
For , the next greater elements are .
Each index is pushed once and popped at most once. Therefore, the total time complexity is and the auxiliary space is .
Describe how a monotonic stack solves the largest rectangle in a histogram problem. Justify the running time.
Maintain a stack of bar indices whose heights are in nondecreasing order. Append a sentinel bar of height so that all remaining bars are processed.
For each index :
- While the stack is nonempty and the current height is smaller than the height at the top index, pop an index .
- The popped bar has height .
- Its right boundary is .
- Its left boundary is one position after the new stack top. If the stack is empty, the left boundary is .
- Thus, the width is when the stack is empty; otherwise it is .
- Compute the area as and update the maximum.
- Push after all taller bars have been removed.
The stack identifies the maximal interval over which each popped bar is the minimum height. Every index is pushed once and popped once, so the total number of stack operations is . Hence, the algorithm runs in time and uses space.
Explain the role of monotonic stacks in finding previous smaller and next smaller elements. How are these boundaries useful in range problems?
To find a previous smaller element, scan from left to right while maintaining an increasing stack. Before processing , pop indices whose values are greater than or equal to . The new top, if present, is the nearest previous index containing a smaller value.
To find a next smaller element, either scan from right to left using a similar increasing stack or resolve waiting indices during a left-to-right scan whenever a smaller value appears.
These boundaries define the maximal interval over which can act as the minimum element. They are useful for:
- Computing the largest rectangle in a histogram.
- Calculating the sum of subarray minimums.
- Determining spans of influence for array elements.
- Finding nearest obstructing or limiting values.
Because every index is pushed and popped at most once in each scan, all boundaries can be computed in time using space.
Define a monotonic queue and explain how it differs from an ordinary queue and a monotonic stack.
A monotonic queue is usually implemented with a deque and maintains its stored values or indices in increasing or decreasing order.
When maintaining a decreasing queue:
- Elements smaller than or equal to a newly inserted element are removed from the back.
- The new element is inserted at the back.
- Expired elements are removed from the front.
- The front always contains the maximum element in the active range.
An ordinary queue preserves insertion order and supports insertion at the rear and removal from the front, but it does not maintain an ordered extremum.
A monotonic stack supports operations at one end and is commonly used for nearest-greater or nearest-smaller boundary problems. A monotonic queue uses both ends of a deque and is particularly useful for maintaining a minimum or maximum over a moving window.
Each element enters and leaves the deque at most once, so a sequence of operations takes total time.
Develop the monotonic-queue algorithm for finding the maximum of every sliding window of size . Trace it for with .
Store array indices in a deque such that their corresponding values are in decreasing order.
For each index :
- Remove the front index if it is outside the current window, that is, if it is at most .
- Remove indices from the back while their values are less than or equal to .
- Insert at the back.
- Once , report the value at the front as the window maximum.
For and , the windows and maxima are:
Therefore, the output is . Each index is inserted and removed at most once, so the time complexity is and the deque uses space.
Explain how a monotonic queue can optimize dynamic programming recurrences involving a sliding-window minimum.
Consider a recurrence of the form:
A direct implementation examines up to previous states for every , resulting in time.
A monotonic increasing deque can maintain candidate indices :
- Remove the front index when it becomes smaller than .
- The front now contains the index with the minimum valid value.
- Compute using that front value.
- Remove indices from the back while their values are greater than or equal to , because they cannot become a future minimum before expires.
- Insert index at the back.
Every state enters and leaves the deque at most once. Consequently, the recurrence is evaluated in time instead of , while the deque requires space.
Explain the two-pointers technique and describe its main variants with suitable use cases.
The two-pointers technique uses two indices that move through a data structure according to conditions that eliminate unnecessary repeated work.
Main variants include:
- Opposite-direction pointers: One pointer begins at each end and they move inward. This is used for pair-sum search in a sorted array, palindrome checking, and container-area problems.
- Same-direction pointers: Both pointers move forward, often representing the beginning and end of a window. This is used for removing duplicates and partitioning sequences.
- Fast and slow pointers: One pointer moves faster than the other. This is used for cycle detection, locating the middle of a linked list, and in-place filtering.
- Sliding-window pointers: The right pointer expands a range and the left pointer contracts it when a condition is violated. This is used for subarray and substring optimization.
Two pointers often reduce a nested-loop solution from to after any required preprocessing.
Design a two-pointers algorithm to determine whether a sorted array contains two elements whose sum equals a target . Prove its correctness and analyze its complexity.
Initialize and . While , compute:
- If , return the pair.
- If , increment .
- If , decrement .
- If the pointers meet without equality, no valid pair exists.
Correctness:
If , pairing with any element at an index smaller than cannot produce , because those elements are no larger than . Therefore, can be safely eliminated.
If , pairing with any element at an index larger than cannot produce , because those elements are no smaller than . Therefore, can be safely eliminated.
Each step discards only a value that cannot belong to a solution within the remaining interval. Hence, if a pair exists, the algorithm eventually finds it.
The running time is and the auxiliary space is . If the array must first be sorted, the total time becomes .
Describe how sliding-window two pointers find the minimum-length subarray with sum at least when all array elements are positive.
Maintain a window and its current sum.
- Initialize , , and the best length to infinity.
- Move from left to right, adding to the sum.
- While , record the current length , subtract , and increment .
- Continue until all right endpoints have been processed.
The method is correct for positive elements because expanding the window cannot decrease its sum, while removing elements from the left cannot increase it. Therefore, once a window reaches , repeatedly moving the left pointer identifies the shortest valid window for that right endpoint.
Each pointer advances at most times, so the algorithm runs in time and uses auxiliary space.
This direct method does not generally work with negative elements because window expansion and contraction no longer change the sum monotonically.
Compare the two-pointers technique, monotonic stack, and monotonic queue. Explain how the invariant maintained by each technique determines its applications.
All three techniques process sequences efficiently by maintaining an invariant that prevents repeated examination of irrelevant elements.
- Two pointers: The invariant usually describes a valid interval or an eliminated search region. Pointer movement is justified by sorted order, positivity, or another monotonic property. Typical applications include pair-sum search, partitioning, and variable-length windows.
- Monotonic stack: The stack maintains increasing or decreasing order among unresolved elements. Popping identifies the nearest element that violates or satisfies an order relation. Typical applications include next-greater elements, histogram areas, and subarray-boundary calculations.
- Monotonic queue: A deque maintains ordered candidates within an active window. The front supplies the current minimum or maximum, while expired and dominated candidates are removed. Typical applications include sliding-window extrema and dynamic-programming optimization.
Their linear-time behavior comes from amortization: each pointer moves only forward, or each element is inserted and removed only a constant number of times. The correct technique depends on whether the problem concerns an interval, a nearest ordered boundary, or an extremum over a moving range.
Define a balanced search tree. Why is balancing necessary in a binary search tree?
A balanced search tree is a binary search tree in which the height is kept proportional to the logarithm of the number of stored keys. For nodes, its height is typically .
Balancing is necessary because:
- An ordinary binary search tree can become skewed when keys are inserted in sorted or nearly sorted order.
- A skewed tree can have height , making search, insertion, and deletion linear-time operations.
- A balanced tree limits its height to .
- Therefore, search, insertion, and deletion can be performed in time in the worst case.
Examples of balanced search trees include AVL trees, red-black trees, 2-3 trees, and B-trees.
Did this save you a night before the exam?
LPU Notes is free, and it stays free. Ads cover part of the server bill. The rest comes out of a student's own pocket: the domain, the storage, and keeping the site up through the weeks everyone needs it at once.
The payment button didn't load. An ad blocker or a filtered network is the usual reason. to try again.
Nothing here is ever locked, and nothing unlocks. Chip in only if it was worth it. What it pays for →