Arrays
Prev: Primitive Types Next: Strings
Problems
6.1
The quicksort algorithm proceeds recursively: it selects an element, the pivot, and reorders the array so that elements less than or equal to the pivot appear first, followed by elements greater than the pivot. With many duplicates, it is better to partition into three groups: less than the pivot, equal to the pivot, and greater than the pivot.
Write a program that takes an array and an index into , and rearranges the elements so that all elements less than appear first, followed by elements equal to the pivot, followed by elements greater than the pivot.
Hint: Think about the partition step in quicksort.
Variant: Assuming keys take one of three values, reorder the array so that all objects with the same key appear together. The order of the subarrays is not important. Use additional space and time.
Variant: Given an array of objects with keys taking one of four values, reorder the array so that all objects with the same key appear together. Use additional space and time.
Variant: Given an array of objects with Boolean-valued keys, reorder the array so that objects with key false appear first. Use additional space and time.
Variant: Same as the previous variant, but preserve the relative ordering of the objects with key true. Use additional space and time.
6.2
Write a program which takes as input an array of digits encoding a decimal number and updates the array to represent the number . For example, if the input is , update the array to . Your algorithm should work even if implemented in a language with finite-precision arithmetic.
Hint: Experiment with concrete examples.
Variant: Write a program which takes as input two strings and of bits encoding binary numbers and , respectively, and returns a new string of bits representing the number .
6.3
Applications sometimes require arbitrary-precision arithmetic using arrays of digits. Use one digit per array entry, with the most significant digit first, and use a negative leading digit to denote a negative integer. For example, represents , and represents .
Write a program that takes two arrays representing integers and returns an integer representing their product. For example, since , if the inputs are and , your function should return .
Hint: Use arrays to simulate the grade-school multiplication algorithm.
6.4
In a board game, each position has a nonnegative integer that gives the maximum you can advance from that position in one move. You begin at the first position and win by reaching the last position. For example, can be won by going from to , then to , then to . In contrast, if , the game cannot be won.
Write a program which takes an array of integers, where denotes the maximum you can advance from index , and returns whether it is possible to advance to the last index starting from the beginning of the array.
Hint: Analyze each location, starting from the beginning.
Variant: Write a program to compute the minimum number of steps needed to advance to the last location.
6.5
This problem concerns deleting repeated elements from a sorted array. For example, if the array is , then after deletion, the array is . After deleting repeated elements, there are 6 valid entries. There are no requirements on the values stored beyond the last valid element.
Write a program which takes as input a sorted array and updates it so that all duplicates have been removed and the remaining elements have been shifted left to fill the emptied indices. Return the number of valid elements. Do not use library routines that directly perform this operation.
Hint: There is an time and space solution.
Variant: Implement a function which takes as input an array and a key, and updates the array so that all occurrences of the input key have been removed and the remaining elements have been shifted left to fill the emptied indices. Return the number of remaining elements.
Variant: Write a program which takes as input a sorted array of integers and a positive integer , and updates so that if appears times in it appears exactly times in . Perform the update in one pass, and allocate no additional storage.
6.6
This problem concerns optimally buying and selling a stock once. For example, for prices , the maximum profit is 30, achieved by buying at 260 and selling at 290. Note that 260 is not the lowest price, and 290 is not the highest price.
Write a program that takes an array denoting daily stock prices, and returns the maximum profit that could be made by buying and then selling one share of the stock.
Hint: Identifying the minimum and maximum is not enough, since the minimum may appear after the maximum.
Variant: Write a program that takes an array of integers and finds the length of a longest subarray all of whose entries are equal.
6.7
The max-difference problem asks for the maximum profit that can be made by buying and then selling a single share over a day range.
Write a program that computes the maximum profit that can be made by buying and selling a share at most twice. The second buy must be made on another date after the first sale.
Hint: What do you need to know about the first elements when processing the th element?
Variant: Solve the same problem in time and space.
6.8
A natural number is called prime if it is bigger than 1 and has no divisors other than 1 and itself.
Write a program that takes an integer argument and returns all the primes between 1 and that integer. For example, if the input is 18, return .
Hint: Exclude the multiples of primes.
6.9
A permutation can be specified by an array , where represents the destination of the element at index . For example, maps the element at location 0 to location 2, the element at location 1 to location 0, the element at location 2 to location 1, and leaves the element at location 3 unchanged. Applied to , the result is .
Given an array of elements and a permutation , apply to .
Hint: Any permutation can be viewed as a set of cyclic permutations. For an element in a cycle, how would you identify if it has been permuted?
Variant: Given an array of integers representing a permutation, update to represent the inverse permutation using only constant additional storage.
6.10
There are exactly permutations of elements. They can be totally ordered using dictionary ordering: permutation appears before permutation if, at the first position where they differ, the corresponding entry for is less than that for . For example, . The permutation is the smallest, and is the largest.
Write a program that takes as input a permutation, and returns the next permutation under dictionary ordering. If the permutation is the last permutation, return the empty array. For example, if the input is your function should return . If the input is , return .
Hint: Study concrete examples.
Variant: Compute the th permutation under dictionary ordering, starting from the identity permutation.
Variant: Given a permutation , return the permutation corresponding to the previous permutation of under dictionary ordering.
6.11
This problem is motivated by the need for a company to select a random subset of its customers to roll out a new feature.
Implement an algorithm that takes as input an array of distinct elements and a size, and returns a subset of the given size of the array elements. All subsets should be equally likely. Return the result in the input array itself.
Hint: How would you construct a random subset of size given a random subset of size ?
Variant: The rand() function in the standard C library returns a uniformly random number in . Does generate a number uniformly distributed in ?
6.12
This problem is motivated by the design of a packet sniffer that provides a uniform sample of packets for a network session.
Design a program that takes as input a size , and reads packets continuously, maintaining a uniform random subset of size of the read packets.
Hint: Suppose you have a procedure which selects packets from the first packets as specified. How would you deal with the th packet?
6.13
Generating random permutations is subtler than it looks. For example, iterating through and swapping each element with another randomly selected element does not generate all permutations with equal probability. For , there are 6 permutations, but this flawed procedure has equally likely execution paths, so some permutations occur more often than others.
Design an algorithm that creates uniformly random permutations of . You are given a random number generator that returns integers in with equal probability; use as few calls to it as possible.
Hint: If the result is stored in , how would you proceed once is assigned correctly?
6.14
The set has subsets of size . Return any one of these size- subsets with equal probability.
Write a program that takes as input a positive integer and a size , and returns a size- subset of . Represent the subset as an array. All subsets should be equally likely and, in addition, all permutations of the elements of the array should be equally likely. You may assume you have a function which takes as input a nonnegative integer and returns an integer in the set with uniform probability.
Hint: Simulate Problem 6.11, using an appropriate data structure to reduce space.
6.15
Suppose you need to write a load test for a server. You studied the inter-arrival times of requests over a year and computed a histogram of the distribution. You want to generate synthetic requests whose inter-arrival times follow the same distribution.
You are given numbers as well as probabilities , which sum to 1. Given a random number generator that produces values in uniformly, how would you generate one of the numbers according to the specified probabilities? For example, if the numbers are 3, 5, 7, 11, and the probabilities are , then in 1,000,000 calls, 3 should appear roughly 500,000 times, 5 should appear roughly 333,333 times, 7 roughly 111,111 times, and 11 roughly 55,555 times.
Hint: Look at the graph of the probability that the selected number is less than or equal to . What do the jumps correspond to?
Variant: Given a random number generator that produces values in uniformly, how would you generate a value from according to a continuous probability distribution, such as the exponential distribution?
6.16
Sudoku is a popular logic-based combinatorial number-placement puzzle. The objective is to fill a grid with digits subject to the constraint that each column, each row, and each of the nine sub-grids contains unique integers in . A partially completed puzzle uses 0 to denote a blank entry.
Check whether a 2D array representing a partially completed Sudoku is valid. Specifically, check that no row, column, or 2D subarray contains duplicates.
Hint: Directly test the constraints. Use an array to encode sets.
6.17
A 2D array can be written as a sequence in several orders, such as row-by-row or column-by-column. In this problem, write the array in spiral order. For the array in Figure 6.3(a), the spiral order is . For Figure 6.3(b), it is .
Write a program which takes an 2D array and returns the spiral ordering of the array.
Hint: Use case analysis and divide-and-conquer.
Variant: Given a dimension , generate a 2D array whose spiral order is . For example, if , the result should be
Variant: Given a sequence of integers , compute a 2D array whose spiral order is . Assume the size of is for some integer .
Variant: Write a program to enumerate the first pairs of integers in spiral order, starting from followed by . For example, if , output .
Variant: Compute the spiral order for an 2D array .
Variant: Compute the last element in spiral order for an 2D array in time.
Variant: Compute the th element in spiral order for an 2D array in time.
6.18
Image rotation is a fundamental operation in computer graphics. In this problem, a 2D array represents a bitmap of an image, and the image is rotated by 90 degrees clockwise.
Write a function that takes as input an 2D array, and rotates the array by 90 degrees clockwise.
Hint: Focus on the boundary elements.
Variant: Implement an algorithm to reflect , assumed to be an 2D array, about the horizontal axis of symmetry. Repeat the same for reflections about the vertical axis, the diagonal from top-left to bottom-right, and the diagonal from top-right to bottom-left.
6.19
Figure 6.5 shows the first five rows of Pascal’s triangle. Each row contains one more entry than the previous one. Except for entries in the last row, each entry is adjacent to one or two numbers in the row below it. The first row holds 1. Each entry holds the sum of the numbers in the adjacent entries above it.
Write a program which takes as input a nonnegative integer and returns the first rows of Pascal’s triangle.
Hint: Write the given fact as an equation.
Variant: Compute the th row of Pascal’s triangle using space.
Answers
6.1
Maintain four regions: elements less than the pivot, elements equal to the pivot, unclassified elements, and elements greater than the pivot. Scan through the unclassified region and swap each incoming element into the correct region. This is the classic Dutch-national-flag partition and runs in time with extra space.
6.2
Increment the last digit, then propagate carries from right to left. If the most significant digit overflows to 10, replace it with 0 and prepend 1. This works directly on the digit array, avoids integer overflow, and runs in time.
6.3
Simulate grade-school multiplication on arrays of digits. Normalize the signs first, use an output array of length , accumulate per-digit products into the correct positions, propagate carries, strip leading zeroes, and restore the sign. The running time is .
6.4
Track the furthest reachable index while scanning left to right. At position , update the furthest reachable point to . If you ever reach an index beyond the current furthest reachable point, the end is impossible; if the furthest reachable point reaches the last index, succeed. This is time and space.
6.5
Because the input is sorted, duplicates are consecutive. Keep a write index for the next valid slot, scan once from left to right, and copy a value forward only when it differs from the previously written value. Return the final write index as the number of valid entries. This is time and space.
6.6
Track the minimum price seen so far and, for each day, compute the profit from selling that day after buying at that minimum. Keep the maximum such profit over the scan. This is time and space.
6.7
Do a forward pass computing the best single-buy/single-sell profit ending at or before each day. Then do a backward pass computing the best second transaction starting at or after each day, and combine the two. The chapter solution uses an auxiliary array and runs in time and space. The -space variant folds the same state into a constant number of running values.
6.8
Use sieving instead of independent primality tests. Maintain a Boolean candidate array, mark off multiples of each prime, and collect the survivors. The improved version starts crossing off at and skips even numbers. This yields the usual sieve behavior: time and space.
6.9
Decompose the permutation into disjoint cycles and rotate each cycle in place. One way to avoid extra storage is to mark entries of the permutation array as processed by subtracting , then restore the permutation afterward. That gives time and extra space if temporary mutation of the permutation is allowed.
6.10
Find the longest decreasing suffix. If the entire permutation is decreasing, there is no next permutation. Otherwise:
- Find the pivot just before the suffix, where .
- Find the smallest suffix entry larger than .
- Swap it with .
- Reverse the suffix.
This makes the smallest possible increase and runs in time with space.
6.11
Construct the subset incrementally in place. For position from 0 to , choose a random index in and swap that entry into position . After iterations, the first entries form a uniformly random subset. This is time and extra space.
6.12
Use reservoir sampling. Keep the first packets. When the th packet arrives, include it in the sample with probability ; if included, replace a uniformly random existing sample entry. This maintains a uniform sample of size over the entire stream, with work per packet and space.
6.13
Generate the permutation by sampling in place from shrinking suffixes: for , choose a random index in and swap it into position . This is the Fisher-Yates shuffle. It produces every permutation with equal probability in time and uses no storage beyond the permutation array itself.
6.14
Simulate the in-place sampling idea from Problem 6.11 without materializing the full array . Store only the positions whose values differ from their defaults in a hash table. Each step randomly selects an index in the remaining range and updates the hash table as though a swap had occurred in the conceptual array. Because only positions are touched, the algorithm runs in expected time and uses space.
6.15
Build prefix sums of the probabilities:
.
Draw a uniform random number in and binary-search for the interval containing it. Return the value associated with that interval. Building the prefix array is time and space; each subsequent sample is .
6.16
Check every row, every column, and every subgrid for duplicates among the nonzero entries. The clean implementation uses a small Boolean array or bitset to represent which digits have appeared while checking a region. For an Sudoku with boxes, the time is and the auxiliary space is .
6.17
Process the matrix layer by layer from the outside in. For each layer, append the top row, right column, bottom row in reverse, and left column in reverse; handle the center separately when is odd. That gives time and extra space beyond the output. The chapter also presents a directional simulation that walks right, down, left, and up while marking visited entries.
6.18
Rotate the matrix in place, layer by layer. Within each layer, perform a four-way swap for corresponding boundary elements:
- top goes to right
- right goes to bottom
- bottom goes to left
- left goes to top
Processing all layers gives time and extra space.
6.19
Build Pascal’s triangle row by row. In row , the endpoints are 1, and each interior entry is the sum of the two entries above it from the previous row. Since there are total entries across the first rows, both time and space are . The -space variant computes one row at a time.