Cracking Programming Interviews: 500 Questions with Solutions by Sergei Nakariakov by Sergei Nakariakov - Read Online
Cracking Programming Interviews
0% of Cracking Programming Interviews completed

About

Summary

Part I Algorithms and Data Structures

1 Fundamentals:

Approximating the square root of a number
Generating Permutation Efficiently
Unique 5-bit Sequences
Select Kth Smallest Element
The Non-Crooks Problem
Is this (almost) sorted?
Sorting an almost sorted list
The Longest Upsequence Problem
Fixed size generic array in C++
Seating Problem
Segment Problems
Exponentiation
Searching two-dimensional sorted array
Hamming Problem
Constant Time Range Query
Linear Time Sorting
Writing a Value as the Sum of Squares
The Celebrity Problem
Transport Problem
Find Length of the rope
Switch Bulb Problem
In, On or Out
The problem of the balanced seg
The problem of the most isolated villages

2 Arrays

The Plateau Problem
Searching in Two Dimensional Sequence
The Welfare Crook Problem
2D Array Rotation
A Queuing Problem in A Post Office
Interpolation Search
Robot Walk
Linear Time Sorting
Write as sum of consecutive positive numbers
Print 2D Array in Spiral Order
The Problem of the Circular Racecourse
Sparse Array Trick
Bulterman’s Reshuffling Problem
Finding the majority
Mode of a Multiset
Circular Array
Find Median of two sorted arrays
Finding the missing integer
Finding the missing number with sorted columns
Re-arranging an array
Switch and Bulb Problem
Compute sum of sub-array
Find a number not sum of subsets of array
Kth Smallest Element in Two Sorted Arrays
Sort a sequence of sub-sequences
Find missing integer
Inplace Reversing
Find the number not occurring twice in an array

3 Trees

Lowest Common Ancestor(LCA) Problem
..
4 Dynamic Programming

5 Graphs

6 Miscellaneous

Compute Next Higher Number
Searching in Possibly Empty Two Dimensional Sequence
Matching Nuts and Bolts Optimally
Random-number generation
Weighted Median
Compute a^n
Compute a^n revisited
Compute the product a × b
Compute the quotient and remainder
Compute GCD
Computed Constrained GCD
Alternative Euclid’ Algorithm
Revisit Constrained GCD
Compute Square using only addition and subtraction
Factorization
Factorization Revisited
Decimal Representation
Reverse Decimal Representation
Solve Inequality
Solve Inequality Revisited
Print Decimal Representation
Decimal Period Length
Sequence Periodicity Problem
Compute Function
Emulate Division and Modulus Operations
Sorting Array of Strings : Linear Time
LRU data structure
Exchange Prefix and Suffix

7 Parallel Algorithms

8 Low Level Algorithms

Manipulating Rightmost Bits
Counting 1-Bits
Counting the 1-bits in an Array
Computing Parity of a word
Counting Leading/Trailing 0’s
Bit Reversal
Bit Shuffling
Integer Square Root
Newton’s Method
Integer Exponentiation
LRU Algorithm
Shortest String of 1-Bits
Fibonacci words
Computation of Power of 2
Round to a known power of 2
Round to Next Power of 2
Efficient Multiplication by Constants
Bit-wise Rotation
Gray Code Conversion
Average of Integers without Overflow
Least/Most Significant 1 Bit
Next bit Permutation
Modulus Division

Part II C++

Published: Sergei Nakariakov on
ISBN: 9781507071007
List price: $9.99
Availability for Cracking Programming Interviews: 500 Questions with Solut...
With a 30 day free trial you can read online for free
  1. This book can be read on up to 6 mobile devices.

Reviews

Book Preview

Cracking Programming Interviews - Sergei Nakariakov

You've reached the end of this preview. Sign up to read more!
Page 1 of 1

sergei.nakariakov.5@gmail.com

Preface

This book contains 500 programming questions most frequently asked in technical interviews in top technical companies including Facebook, Microsoft, Google, Apple, Yahoo and others. Detailed solutions are provided for all of these including tips and techniques for solving similar problems.

For suggestions, feedback and comments, please contact :

sergei.nakariakov.5@gmail.com

Sergei Nakariakov

Senior Scientist(Defence Lab)

USA

January 4, 2015

List of Chapters

Preface

I  Algorithms and Data Structures

Fundamentals

1.1 Approximating the square root of a number

1.2 Generating Permutation Efficiently

1.3 Unique 5-bit Sequences

1.4 Select Kth Smallest Element

1.5 The Non-Crooks Problem

1.6 Is this (almost) sorted?

1.7 Sorting an almost sorted list

1.8 The Longest Upsequence Problem

1.9 Fixed size generic array in C++

1.10 Seating Problem

1.11 Segment Problems

1.12 Exponentiation

1.13 Searching two-dimensional sorted array

1.14 Hamming Problem

1.15 Constant Time Range Query

1.16 Linear Time Sorting

1.17 Writing a Value as the Sum of Squares

1.18 The Celebrity Problem

1.19 Transport Problem

1.20 Find Length of the rope

1.21 Switch Bulb Problem

1.22 In, On or Out

1.23 The problem of the balanced segments

1.24 The problem of the most isolated villages

Arrays

2.1 The Plateau Problem

2.2 Searching in Two Dimensional Sequence

2.3 The Welfare Crook Problem

2.4 2D Array Rotation

2.5 A Queuing Problem in A Post Office

2.6 Interpolation Search

2.7 Robot Walk

2.8 Linear Time Sorting

2.9 Write as sum of consecutive positive numbers

2.10 Print 2D Array in Spiral Order

2.11 The Problem of the Circular Racecourse

2.12 Sparse Array Trick

2.13 Bulterman’s Reshuffling Problem

2.14 Finding the majority

2.15 Mode of a Multiset

2.16 Circular Array

2.17 Find Median of two sorted arrays

2.18 Finding the missing integer

2.19 Finding the missing number with sorted columns

2.20 Re-arranging an array

2.21 Switch and Bulb Problem

2.22 Compute sum of sub-array

2.23 Find a number not sum of subsets of array

2.24 Kth Smallest Element in Two Sorted Arrays

2.25 Sort a sequence of sub-sequences

2.26 Find missing integer

2.27 Inplace Reversing

2.28 Find the number not occurring twice in an array

Trees

3.1 Lowest Common Ancestor(LCA) Problem

3.2 Spying Campaign

Dynamic Programming

4.1 Stage Coach Problem

4.2 Matrix Multiplication

4.3 TSP Problem

4.4 A Simple Path Problem

4.5 String Edit Distance

4.6 Music recognition

4.7 Max Sub-Array Problem

Graphs

5.1 Reliable distribution

5.2 Independent Set

5.3 Party Problem

Miscellaneous

6.1 Compute Next Higher Number

6.2 Searching in Possibly Empty Two Dimensional Sequence

6.3 Matching Nuts and Bolts Optimally

6.4 Random-number generation

6.5 Weighted Median

6.6 Compute an

6.7 Compute an revisited

6.8 Compute the product a × b

6.9 Compute the quotient and remainder

6.10 Compute GCD

6.11 Computed Constrained GCD

6.12 Alternative Euclid’ Algorithm

6.13 Revisit Constrained GCD

6.14 Compute Square using only addition and subtraction

6.15 Factorization

6.16 Factorization Revisited

6.17 Decimal Representation

6.18 Reverse Decimal Representation

6.19 Solve Inequality

6.20 Solve Inequality Revisited

6.21 Print Decimal Representation

6.22 Decimal Period Length

6.23 Sequence Periodicity Problem

6.24 Compute Function

6.25 Emulate Division and Modulus Operations

6.26 Sorting Array of Strings : Linear Time

6.27 LRU data structure

6.28 Exchange Prefix and Suffix

Parallel Algorithms

7.1 Parallel Addition

7.2 Find Maximum

7.3 The Parallel Prefix Problem

7.4 Finding Ranks in Linked Lists

7.5 Finding the kth Smallest Element on a Tree

Low Level Algorithms

8.1 Introduction

8.1.1 Manipulating Rightmost Bits

8.1.1.1 Extending De Morgan’s Laws

8.1.1.2 Compute Next higher number with same number of 1-bits

8.2 Bit Counting Algorithms

8.2.1 Counting 1-Bits

8.2.1.1 Counting the 1-bits in an Array

8.2.2 Computing Parity of a word

8.2.3 Counting Leading 0’s

8.2.4 Counting Trailing 0’s

8.3 Rearranging Algorithms

8.3.1 Bit Reversal

8.3.2 Bit Shuffling

8.4 Computing Functions

8.4.1 Integer Square Root

8.4.1.1 Newtons Method

8.4.2 Integer Exponentiation : Compute xn

8.5 Miscellaneous

8.5.1 LRU Algorithm : Reference Matrix

8.5.2 Find Shortest String of 1-Bits

8.5.3 Fibonacci words

8.5.4 Computation of Power of 2

8.5.4.1 Round to a known power of 2

8.5.4.2 Round to Next Power of 2

8.5.5 Efficient Multiplication by Constants

8.5.6 Bit-wise Rotation

8.5.7 Gray Code Conversion

8.5.8 Average of Integers without Overflow

8.5.9 Double Linked List with One Pointer Field

8.5.10 Least Significant 1 Bit

8.5.11 Most Significant 1 Bit

8.5.12 Swap Values Without a Temporary

8.5.13 Next bit Permutation

8.5.14 Compute Modulus Division

8.5.15 Conditionally set or clear bits without branching

8.5.16 Conditionally negate a value without branching

II  C++

General

Constant Expression

Type Specifier

Namespaces

Misc

Classes

Templates

Standard Library

*

List of Algorithms

1 Computing Square Root Program

2 Unique 5-bit Sequence Program

3 Maximum of a sequence

4 Generic Kth Select Minimum

5 Partitioning a sequence

6 Randomized Partition Algorithm

7 Randomized Quicksort Algorithm

8 Randomized Kth Min Select Algorithm

9 Iterative Version of Quick Select Algorithm

10 The Non-Crooks Program

11 Binary Search on unsorted list

12 is almost sorted

13 Sort Almost Sorted Algorithm

14 The Longest Upsequence Program

15 Seat Algorithm

16 ReSeat Algorithm

17 ReSeat Linear Time Algorithm

18 All Zeros Program

19 Exponentiation

20 Exponentiation Revisited

21 Efficient Exponentiation

22 Saddleback Search Algorithm

23 Saddleback Search Algorithm in practice

24 Saddleback Search Algorithm : Find First Occurrence

25 Saddleback Search Algorithm : Find All Occurrences

26 Saddleback Count Algorithm : Initial Approach

27 Saddleback Count : Correct Algorithm

28 Hamming Problem

29 Counting Sort Algorithm

30 Radix Sort Algorithm

31 Writing a Value as the Sum of Squares

32 Writing a Value as the Sum of Squares

33 Celebrity Algorithm

34 Celebrity Algorithm Brute Force

35 Celebrity Algorithm Optimized

36 Transport Algorithm

37 triangle analysis

38 The Plateau Problem

39 The Plateau Problem Revisited

40 Searching in a 2D Array

41 Searching in a 2D Array : Another Program

42 Searching in a 2D Array : Another Program(Simplified)

43 The Welfare Crook Program

44 The Welfare Crook Program Simplified

45 Interpolation Search Algorithm

46 Robot Walk Algorithm

47 Write as sum of consecutive positive numbers

48 Circular Racecourse Program

49 Bulterman’s Reshuffling Problem

50 Find Majority Simple Algorithm

51 Find Majority Algorithm Revisited

52 Find Majority Algorithm Simplified

53 Find Majority Algorithm Final

54 Circular Array Algorithm

55 Find Median of two sorted array

56 Median Search

57 Re-arranging an array

58 Re-arranging an array revisited

59 Switches and Bulbs

60 Find a number not sum of subsets of array

61 Kth Smallest Element in Two Sorted Arrays

62 Reversing an array inplace

63 Kadane’s 1D Algorithm

64 Kadane’s 1D Algorithm : Find Indices

65 Maximum sub-array sum using prefix array

66 K-Maximum sub-array sum using prefix array

67 Independent Set on trees

68 Searching in a possibly empty 2D Array

69 Partitioning a sequence

70 Quicksort to sort a sequence

71 Randomized Partition Algorithm

72 Randomized Quicksort Algorithm

73 Selecting a c-approximate median of X with O(n) complexity

74 Back-Tracking

75 Finding Misplaced Nuts and Bolts

76 Random number generation using binary search

77 Random number generation : Preprocess

78 Random number generation

79 Weighted Median

80 Linear Time Weighted Median

81 Compute an program

82 Compute an alternative program

83 Compute an program revisited

84 Compute the product a × b

85 Compute the quotient and remainder

86 Compute GCD

87 Compute GCD : Euclid’s Algorithm

88 Computed Constrained GCD

89 Alternative Euclid’ Algorithm

90 Compute Square using only addition and subtraction

91 Factorization Program

92 Factorization Alternative Program

93 Factorization Program Revisited

94 Decimal Representation

95 Reverse Decimal Representation

96 Solve Inequality Program

97 Solve Inequality Revisited

98 Print Decimal Representation

99 Decimal Period Length

100 Sequence Periodicity Problem

101 Compute Function

102 Emulate Division and Modulus Operations

103 LRU Data Structure : Drop program

104 LRU Data Structure : Use program

105 LRU Data Structure : Insert program

106 Exchange Prefix and Suffix

107 Parallel Prefix Algorithm

108 Parallel Prefix : Efficient Algorithm

109 List Rank Algorithm

List of Programs

Preface

I  Algorithms and Data Structures

Fundamentals

1.1 Approximating the square root of a number

1.2 Generating Permutation Efficiently

1.3 Unique 5-bit Sequences

1.4 Select Kth Smallest Element

1.5 The Non-Crooks Problem

1.6 Is this (almost) sorted?

1.7 Sorting an almost sorted list

1.8 The Longest Upsequence Problem

1.9 Fixed size generic array in C++

1.10 Seating Problem

1.11 Segment Problems

1.12 Exponentiation

1.13 Searching two-dimensional sorted array

1.14 Hamming Problem

1.15 Constant Time Range Query

1.16 Linear Time Sorting

1.17 Writing a Value as the Sum of Squares

1.18 The Celebrity Problem

1.19 Transport Problem

1.20 Find Length of the rope

1.21 Switch Bulb Problem

1.22 In, On or Out

1.23 The problem of the balanced segments

1.24 The problem of the most isolated villages

Arrays

2.1 The Plateau Problem

2.2 Searching in Two Dimensional Sequence

2.3 The Welfare Crook Problem

2.4 2D Array Rotation

2.5 A Queuing Problem in A Post Office

2.6 Interpolation Search

2.7 Robot Walk

2.8 Linear Time Sorting

2.9 Write as sum of consecutive positive numbers

2.10 Print 2D Array in Spiral Order

2.11 The Problem of the Circular Racecourse

2.12 Sparse Array Trick

2.13 Bulterman’s Reshuffling Problem

2.14 Finding the majority

2.15 Mode of a Multiset

2.16 Circular Array

2.17 Find Median of two sorted arrays

2.18 Finding the missing integer

2.19 Finding the missing number with sorted columns

2.20 Re-arranging an array

2.21 Switch and Bulb Problem

2.22 Compute sum of sub-array

2.23 Find a number not sum of subsets of array

2.24 Kth Smallest Element in Two Sorted Arrays

2.25 Sort a sequence of sub-sequences

2.26 Find missing integer

2.27 Inplace Reversing

2.28 Find the number not occurring twice in an array

Trees

3.1 Lowest Common Ancestor(LCA) Problem

3.2 Spying Campaign

Dynamic Programming

4.1 Stage Coach Problem

4.2 Matrix Multiplication

4.3 TSP Problem

4.4 A Simple Path Problem

4.5 String Edit Distance

4.6 Music recognition

4.7 Max Sub-Array Problem

Graphs

5.1 Reliable distribution

5.2 Independent Set

5.3 Party Problem

Miscellaneous

6.1 Compute Next Higher Number

6.2 Searching in Possibly Empty Two Dimensional Sequence

6.3 Matching Nuts and Bolts Optimally

6.4 Random-number generation

6.5 Weighted Median

6.6 Compute an

6.7 Compute an revisited

6.8 Compute the product a × b

6.9 Compute the quotient and remainder

6.10 Compute GCD

6.11 Computed Constrained GCD

6.12 Alternative Euclid’ Algorithm

6.13 Revisit Constrained GCD

6.14 Compute Square using only addition and subtraction

6.15 Factorization

6.16 Factorization Revisited

6.17 Decimal Representation

6.18 Reverse Decimal Representation

6.19 Solve Inequality

6.20 Solve Inequality Revisited

6.21 Print Decimal Representation

6.22 Decimal Period Length

6.23 Sequence Periodicity Problem

6.24 Compute Function

6.25 Emulate Division and Modulus Operations

6.26 Sorting Array of Strings : Linear Time

6.27 LRU data structure

6.28 Exchange Prefix and Suffix

Parallel Algorithms

7.1 Parallel Addition

7.2 Find Maximum

7.3 The Parallel Prefix Problem

7.4 Finding Ranks in Linked Lists

7.5 Finding the kth Smallest Element on a Tree

Low Level Algorithms

8.1 Introduction

8.1.1 Manipulating Rightmost Bits

8.1.1.1 Extending De Morgan’s Laws

8.1.1.2 Compute Next higher number with same number of 1-bits

8.2 Bit Counting Algorithms

8.2.1 Counting 1-Bits

8.2.1.1 Counting the 1-bits in an Array

8.2.2 Computing Parity of a word

8.2.3 Counting Leading 0’s

8.2.4 Counting Trailing 0’s

8.3 Rearranging Algorithms

8.3.1 Bit Reversal

8.3.2 Bit Shuffling

8.4 Computing Functions

8.4.1 Integer Square Root

8.4.1.1 Newtons Method

8.4.2 Integer Exponentiation : Compute xn

8.5 Miscellaneous

8.5.1 LRU Algorithm : Reference Matrix

8.5.2 Find Shortest String of 1-Bits

8.5.3 Fibonacci words

8.5.4 Computation of Power of 2

8.5.4.1 Round to a known power of 2

8.5.4.2 Round to Next Power of 2

8.5.5 Efficient Multiplication by Constants

8.5.6 Bit-wise Rotation

8.5.7 Gray Code Conversion

8.5.8 Average of Integers without Overflow

8.5.9 Double Linked List with One Pointer Field

8.5.10 Least Significant 1 Bit

8.5.11 Most Significant 1 Bit

8.5.12 Swap Values Without a Temporary

8.5.13 Next bit Permutation

8.5.14 Compute Modulus Division

8.5.15 Conditionally set or clear bits without branching

8.5.16 Conditionally negate a value without branching

II  C++

General

Constant Expression

Type Specifier

Namespaces

Misc

Classes

Templates

Standard Library

Part I

Algorithms and Data Structures

Chapter 1

Fundamentals

1.1 Approximating the square root of a number

Problem Description

Write a program that, given a fixed integer n ≥ 0, establishes the truth of

R : 0 ≤ a2 ≤ n < (a + 1)2

Solution

Taking the square root of all terms in R, we find that R is equivalent to 0 ≤ a < a + 1. Hence, a . The first step is to rewrite R as a set of conjuncts:

R : 0 ≤ a2 ∧ a2 ≤ n n < (a + 1)2

Deleting the third conjunct of R yields a possible invariant:

P : 0 ≤ a2 ≤ n.

Because n ≥ 0, P can be established by the assignment a ← 0. For the condition of the loop, use the complement of the deleted conjunct, so that when the loop terminates because the condition is false, the deleted conjunct is true. This yields the almost completed program :

1:  a ← 0

2:  while (a + 1)2 ≤ n do

3:   ?

4:  end while

The purpose of the command of the loop is to progress towards termination. Clearly, if the condition of the loop is true then a is too small, so that progress can be made by increasing a. Since a , a possible bound function is t ⌉- a. Using the easiest way to increase a, incrementing by 1, yields the program

1:  a ← 0

2:  while (a + 1)2 ≤ n do

3:   a a + 1

4:  end while

We show that P is indeed an invariant of the loop:

P B = 0 ≤ a2 ≤ n ∧ (a + 1)2 ≤ n = 0 ≤ (a + 1)2 ≤ n

. This program is developed by deleting the conjunct n < (a + 1)2. Now let us try using the method of replacing a constant by a variable.

First try replacing the expression a + 1 by a fresh variable b to yield

a2 ≤ n < b2

Clearly, b must be greater than a if this predicate is to be true. Moreover, the predicate can be established by executing a ← 0 and b n + 1. Hence, b is bounded by a + 1 and n + 1, and the invariant is

P : a < b n + 1 ∧ a2 ≤ n < b2

The condition B for the loop, obtained by investigating P ∧¬B R, is a + 1≠b. Thus far, the program is

1:  a ← 0

2:  b n + 1

3:  while a + 1≠b do

4:   ?

5:  end while

Since P indicates that a + 1 ≤ b and the loop should terminate with a + 1 = b, the task of each iteration is to bring a and b closer together, i. e. to decrease the value of b-a. Execution should continue until b-a = 1. Hence a possible bound function t is b - a - 1.

The size of the interval (a,b) could be decreased by one at each iteration, but perhaps a faster technique exists. Perhaps the interval could be halved, by setting either a or b . If so, the command of the loop could have the form

1:  if ? then

2:   a

3:  else if ? then

4:   b

5:  end if

Each command must maintain the invariant P. To find a suitable condition for the first command, first calculate

< b n )2 ≤ n b2 > n

The precondition will be the invariant together with the condition of the loop

P a + 1≠b

)2 ≤ n)2 > n. Introducing a fresh variable d to save local calculations, we arrive at the program

1:  a ← 0

2:  b n + 1

3:  Invariant : P : a < b n + 1 ∧ a2 ≤ n < b2

4:  bound t : b - a + 1

5:  while a + 1≠b do

6:   d

7:   if d × d n then

8:   a d

9:   else if d × d > n then

10:   b d

11:   end if

12:  end while a2 ≤ n < (a + 1)2

It may seem that the technique of halving the interval was pulled out of a hat. It is simply one of the useful techniques that programmers must know about, for its use often speeds up programs considerably. The execution time of this program is proportional to log n.

This program illustrates another reason to introduce a variable : d has been introduced to make a local optimization. The introduction of d is evaluated, it also makes the program more readable.

Note that no definition is given for d. Variable d is essentially a constant of the loop body. It is assigned a value upon entrance to the loop body, and this values is used throughout the body. It carries no value from iteration to iteration. Moreover, d is used only in two adjacent lines, and its use is obvious from these two lines. A definition of d would belabor the obvious and is therefore omitted.

If the difference b - a is always even, then d can be calculated using

d a

Therefore, let us attempt to deal with the difference, say c, between b and a and to keep this difference even. This will be easiest if c is always a power of 2. Thus we have:

b = a + c

d = a + c∕2

p : 1 ≤ p : c = 2p c is even.

Because b and d are defined in terms of a and c, we may be able to write the program using only a and c. Thus, we try the loop invariant and bound function

P : a2 ≤ n < (a + c)2 ∧ (p : 1 ≤ p : c = 2p)

t : c + 1

The initialization will require a loop to establish P, since c must be a power of 2. The rest of the program is derived from the previous program. essentially by deleting the assignments to b and d and transforming the other commands into commands involving c:

Algorithm 1: Computing Square Root Program

1:  a ← 0

2:  c ← 1

3:  while c2 ≤ n do

4:   c ← 2 × c

5:  end while P

6:  while c≠1 do

7:   c

8:   if (a + c)2 ≤ n then

9:   a a + c

10:   else if (a + c)2 > n then

11:   skip

12:   end if

13:  end while a2 ≤ n < (a + 1)2

1.2 Generating Permutation Efficiently

Problem Description

Design a program that will generate the n! permutations of the values from 0 through n-1 in such an order that the transition from one permutation to the next is always performed by exactly one swap of two neighbors.

Solution

Reduction Approach(to inversion)

In a permutation each pair of values such that the larger value precedes the smaller one, presents a so-called inversion. In particular: the one and only permutation with zero inversions is the one in which the values are placed in monotonically increasing order.

The notion of inversions can be expected to be relevant here because the swapping of two neighbors changes the total number of inversions : i.e. the number of pairs in the wrong order: by (plus or minus) 1, and it is, therefore, suggested to

characterize each permutation by its inversions.

This can be done by introducing n inversion counters inversion[i] for 0 ≤ i < n, where inversion[i] equals the number of inversions between the value i and the values smaller than i.

In other words, inversion[i] equals the number of numbers ¡ i , that are placed at the wrong side of i, so we can say that

inversion[i] = the number of inversions between the value i and smaller values.

From this definition

0 ≤ inversion[i] ≤ i

follows;

the total number of inversions of a permutation is the sum of the corresponding inversion[i] values.

i.e., The total number of inversions of the permutation is the sum of all the inversion[i] values.

So it is obvious that:

1.

Each permutation defines the inversion[i] values uniquely, and

2.

The inverse[i] define the permutation uniquely.

The second point above is easily seen by considering the algorithm constructing the permutation from the inversion[i] values, thus processing these values in the order of increasing i.

So there is a one-to-one correspondence between the n! possible inversion values and the n! permutations. With this insight, the original problem is reduced to the following problem :

which modifications of the inversion value correspond to a swap of neighbors

Each swap of two neighbors changes exactly one inversion[i] value by 1, viz. with i = the larger of the two values swapped. The value of inversion[i] is to be increased if the swap increases the number of inversions; otherwise it is to be decreased.

A feasible sequence of inversion values to be generated is now reasonably obvious: it is the generalization of the Grey-code. For n = 4 it would begin

Algorithm

The logic is simple: a number is changeable when

1.

if the sum of the numbers to its left is even and it has not reached its maximum value.

2.

if the sum of the numbers to its left is odd and it has not reached its minimum value zero.

Please note that at each step, always the right-most changeable number is changed. It is not difficult to see that in the permutation, the value i is, indeed, swapped with a smaller value.

After having established the value i, such that inversion[i] has to be changed, and, also, whether the value i has to be swapped with its predecessor in the permutation (corresponding to an increase of inversion[i]) or with its successor in the permutation (corresponding to a decrease of inversion[i]), we have to establish the place c in the permutation, where the value i is located, because ∀j > i, inversion[j] has an extreme value, c is given by:

c = i - inversion[i]+(the number of values j ∣j > i && inversion[j] = j)

Reason is simple : i is its original position, inversion[i] is the number of smaller elements to the right of it; we have to add to it the number of larger elements in front of the section with elements i .

C++ Implementation

Putting this algorithm in C++ code is easy now:

Program 1.1: generate efficient permutation

1#include <iostream>

2#include <algorithm>

4bool odd(int i) 

5{ 

6    return i & 1; 

7} 

9bool even(int i) 

10{ 

11    return !odd(i); 

12} 

13 

14int main() 

15{ 

16    const int n = 4; 

17 

18    int permuted_array[n], inversion[n]; 

19    bool ready; 

20    int total_inversion, i, c, sum_left_inversion; 

21    i = 0; 

22 

23    while (i < n) 

24    { 

25        permuted_array[i] = i; 

26        inversion[i] = 0; 

27        ++i; 

28    } 

29 

30    inversion[0] = -2; 

31 

32    ready = false

33    total_inversion = 0; 

34 

35    while (!ready) 

36    { 

37        for(auto e : permuted_array) 

38        std::cout << e << ’\t’; 

39 

40        std::cout << std::endl

41 

42        i = n - 1; 

43        c = 0; 

44        sum_left_inversion = total_inversion - inversion[i]; 

45 

46        while ((inversion[i] == i)  && even(sum_left_inversion)) 

47        { 

48            c = c + 1; 

49            i = i - 1; 

50            sum_left_inversion = sum_left_inversion - inversion[i]; 

51        } 

52 

53        while ((inversion[i] == 0) && odd(sum_left_inversion)) 

54        { 

55            i = i - 1; 

56            sum_left_inversion = sum_left_inversion - inversion[i]; 

57        } 

58 

59        c = c + i - inversion[i]; 

60 

61        if (even(sum_left_inversion) && i > 0) 

62        { 

63            inversion[i] = inversion[i] + 1; 

64            total_inversion = total_inversion + 1; 

65            std::swap(permuted_array[c-1], permuted_array[c]); 

66        } 

67        else if(odd(sum_left_inversion) && i > 0) 

68        { 

69            inversion[i] = inversion[i] - 1; 

70            total_inversion = total_inversion - 1; 

71            std::swap(permuted_array[c], permuted_array[c + 1]); 

72        } 

73        else if(i == 0) 

74        { 

75            ready = true

76        } 

77    } 

78}

In the program given above, inversion[0], which should be constantly 0, has been assigned the funny value -2; this is the usual, mean, little coding trick, in order to let the search for the right-most changeable inversion[i] value terminate artificially when there is no more such an inversion[i] value. The value total_inversion records the total number of inversions in the array permuted_array, that is used to record the permutation; the variable sum_left_inversion records the sum of the (non-funny) inversion[j] values to the left of inversion[i] (i.e. with j ¡ i).

1.3 Unique 5-bit Sequences

Problem Description

Consider sequences of 36 bits. Each such sequence has 32 5 - bit sequences consisting of adjacent bits. For example, the sequence 1101011... contains the 5 -bit sequences 11010, 10101, 01011,.... Write a program that prints all 36 -bit sequences with the two properties:

1.

The first 5 bits of the sequence are 00000.

2.

No two 5 - bit subsequences are the same.

Call a sequence of zeros and ones that begins with 0000, i.e. 4 zeros, and satisfy property (2) a good sequence. Call a sequence with k bits a k-sequence.

Define an ordering among good sequences as follows:

Sequence s1 is less than sequence s2, written as

s1. < .s2, if, when viewed as decimal numbers with the decimal point to the extreme left, s1 is less than s2.

For example, 101. < .1011 because .101 < .1011.

In a similar manner, we write 101. = .101000, because .101 = .101000. Appending a zero to a sequence yields an equal sequence; appending a one yields a larger sequence.

Any good sequence s to be printed satisfies 0. < .s. < .00001, and must begin with 00000.

The program below iteratively generates, in order, all good sequences satisfying 0. .s. .00001, printing the 36 - bit ones as they are generated. The sequence currently under consideration will be called s. There will be no variable s; it is just a name for the sequence currently under consideration. s always contains at least 5 bits. Further, to eliminate problems with equal sequences, we will always be sure that s is the longest good sequence equal to itself.

P1 : good(s) ∧¬good(s∣0) ∧ 5 ≤∣s∣∧ 0. .s. .00001∧ All good sequences . < .s are printed.

Sequence s with n bits could be represented by a bit array. However, it is better to represent s by an integer array c[4..n- 1], where c[i] is the decimal representation of the 5 - bit subsequence of s ending in bit i. Thus, we will maintain as part of the invariant of the main loop the assertion

P2 : 5 ≤ n = ∣s∣≤ 36 ∧ c[i] = s[i - 4] × 24 + s[i - 3] × 23 + s[i - 2] × 22 + s[i - 1] × 2 + s[i], for 4 ≤ i < n

Further, in order to keep track of which 5 - bit subsequence s contains, we use a Boolean array in[0..31]:

P3 : i : 0 ≤ i < 32 : in[i] = (i c[4..n - 1])

With this introduction, the program should be easy to follow.

Algorithm 2: Unique 5-bit Sequence Program

1:  n ← 5

2:  c[4] ← 0

3:  in[0] ← T

4:  in[1..31] ← F s = (0, 0, 0, 0, 0)

5:  invariant : P1 ∧ P2 ∧ P3 ∧¬good(s∣0)

6:  while c[4]≠1 do

7:   if n == 36 then

8:   Print sequence s

9:   else if n≠36 then

10:   skip

11:   end if

12:   Change s to next higher good sequence:

13:   while in[(c[n - 1] × 2 + 1) mod32] do i.e. ¬good(s∣1)

14:   Delete ending 1’s from s:

15:   while odd(c[n - 1]) do

16:   n n - 1

17:   in[c[n]] ← F

18:   end while

19:   Delete ending 0:

20:   n n - 1

21:   in[c[n]] ← F

22:   end while

23:   Append 1 to s:

24:   c[n] ← (c[n - 1] × 2 + 1) mod32

25:   in[c[n]] ← T

26:   n n + 1

27:  end while

1.4 Select Kth Smallest Element

Problem

Design and implement an efficient algorithm to select the Kth Smallest Element of an array.

Basic Analysis

Simultaneous Min-Max Algorithm

Before embarking on this selection problem, let us work out a general scheme of finding maximum and minimum of the input sequence. Min-max algorithms are ubiquitous in various applications specially geometric ones. In this section we will revisit several versions with primary focus being finding the most efficient one.

Design an efficient algorithm to find the minimum and maximum of an integer sequence simultaneously.

Let us revisit a typical set-up for finding the maximum of an integer sequence where we end up examine each element of the sequence in turn along with keeping track of the largest element seen so far.

Algorithm 3: Maximum of a sequence

1:  function maxval(a, l, r)

2:   0 ≤ n

3:   a[k] ≥ a[0..n - 1]

4:   i ← 1

5:   k ← 0

6:   while 0 ≤ n do

7:   if a[i] ≤ a[kthen

8:   do nothing

9:   else if a[i] ≥ a[kthen

10:   k i

11:   end if

12:   i i + 1

13:   end while

14:   return k

15:  end function

––––––––

Program 1.2: Finding Maximum in an integer array

1#include <vector>

2#include <algorithm>

3#include <cassert>

5template <typename T>

6size_t maxValArray(std::vector<T> & v) 

7{ 

8    size_t i = 1, k = 0; 

9    size_t n = v.size(); 

10 

11    while(i <= n) 

12    { 

13        if(v[i] >= v[k]) k = i; 

14        ++i; 

15    } 

16 

17    assert(v[k] == *std::max_element(v.begin(), 

18                                     v.end())); 

19    return k; 

20} 

21 

22int main() 

23{ 

24    std::vector<int> v {10, 12, 2, 8, 5, 20, 7}; 

25    maxValArray(v); 

26}

As can be seen that this doesn’t address the scenario in presence of multiple occurrences. Let us put forth obvious solutions.

Program 1.3: Finding First Maximum in an integer array

1template <typename ForwardIterator>

2ForwardIterator first_max_element( 

3    ForwardIter first, ForwardIter last) 

4{ 

5    if (first == last) return last; 

6    ForwardIter max_result = first; 

7    while (++first != last) 

8    { 

9        if (*max_result < *first) 

10            max_result = first; 

11    } 

12    return max_result; 

13}

Program 1.4: Finding First Maximum Satisfying Predicate

1#include <boost/iterator_adaptors.hpp>

3template <typename ForwardIterator, 

4          typename Predicate>

5ForwardIterator 

6first_max_element_if(ForwardIter first, 

7                     ForwardIter last, 

8                     Predicate pred) 

9{ 

10    return first_max_element( 

11        boost::make_filter_iterator(first, last, 

12                                    pred), 

13        boost::make_filter_iterator(last, last, 

14                                    pred) 

15    ); 

16}

Program 1.5: Finding First Minimum in an integer array

1template <typename ForwardIterator>

2ForwardIterator first_min_element( 

3    ForwardIter first, ForwardIter last) 

4{ 

5    if (first == last) return last; 

6    ForwardIter min_result = first; 

7    while (++first != last) 

8    { 

9        if (*first < *min_result) 

10            min_result = first; 

11    } 

12    return min_result; 

13}

Please note that :

Program 1.6: Ordering Equivalence

1std::min_element(v.begin(), v.end(), 

2                 std::less<int>()) 

3== 

4std::max_element(v.begin(), v.end(), 

5                 std::greater<int>())

Program 1.7: Finding Last Maximum in an integer array

1template <typename ForwardIterator>

2ForwardIterator last_max_element( 

3    ForwardIter first, ForwardIter last) 

4{ 

5    if (first == last) return last; 

6    ForwardIter max_result = first; 

7    while (++first != last) 

8    { 

9        if (*first < *max_result) 

10        max_result = first; 

11    } 

12    return max_result; 

13}

Program 1.8: Finding Last Minimum in an integer array

1template <typename ForwardIterator>

2ForwardIterator last_min_element( 

3    ForwardIter first, ForwardIter last) 

4{ 

5    if (first == last) return last; 

6    ForwardIter min_result = first; 

7    while (++first != last) 

8    { 

9        if (*min_result < *first) 

10            min_result = first; 

11    } 

12    return min_result; 

13}

Please note that:

Program 1.9: Another Ordering Equivalence

1std::reverse_iterator( 

2    first_min_element(v.begin(), v.end(), 

3                      std::less<int>())) 

4== 

5last_max_element(v.rbegin(), v.rend(), 

6                 std::greater<int>())

All of these algorithms work in similar way requiring n - 1 comparisons in worst case.

How about simultaneously finding maximum and minimum of the sequence?

Naively, we can get this done in two passes : once for finding maximum and another for finding minimum : total of 2n - 2 comparisons. But we can definitely do better if we confine ourselves to a single pass and reply on maintaining maximum and minimum elements seen so far. Instead of picking one element and probing it against the current maximum or minimum, we can rather examine two elements at a time treating them as pairs. The process goes like this:

1.

Maintain the minimum and maximum of elements seen so far.

2.

Don’t compare each element to the minimum and maximum separately, which requires two comparisons per element.

3.

Pick up the elements in pairs.

4.

Compare the elements of a pair to each other.

5.

Then compare the larger element to the maximum so far, and compare the smaller element to the minimum so far.

The above requires only three comparisons per two elements. Setting up the initial values for the min and max depends on whether n is odd or even.

If n is even, compare the first two elements and assign the larger to max and the smaller to min.

more comparisons. Thus total number of comparisons =

- 3

- 2.

Then process the rest of the elements in pairs.

comparisons.

Program 1.10: C++ Implementation of first min and first max

1template <typename ForwardIterator>

2std::pair<ForwardIterator, ForwardIterator>

3first_min_first_max_element( 

4    ForwardIterator first, 

5    ForwardIterator last) 

6{ 

7    if (first == last) 

8        return std::make_pair(last,last); 

10    ForwardIterator min_result, 

11                    max_result = first; 

12 

13    // if only one element

14    ForwardIterator second = first; ++second; 

15 

16    if (second == last) 

17    return std::make_pair(min_result, 

18                          max_result); 

19 

20    // treat first pair separately

21    //(only one comparison for

22    //first two elements)

23    ForwardIterator 

24      potential_min_result = last; 

25 

26    if (*first < *second) max_result = second; 

27    else

28    { 

29        min_result = second; 

30        potential_min_result = first; 

31    } 

32 

33    // then each element by pairs,

34    // with at most 3 comparisons per pair

35    first = ++second; 

36 

37    if (first != last) ++second; 

38 

39    while (second != last) 

40    { 

41        if (*first < *second) 

42        { 

43            if (*first < *min_result) 

44            { 

45                min_result = first; 

46                potential_min_result = last; 

47            } 

48 

49            if (*max_result < *second) 

50                max_result = second; 

51        } 

52        else

53        { 

54            if (*second < *min_result) 

55            { 

56                min_result = second; 

57                potential_min_result = first; 

58            } 

59 

60            if (*max_result < *first) 

61                max_result = first; 

62        } 

63 

64        first = ++second; 

65 

66        if (first != last) ++second; 

67    } 

68 

69    // if odd number of elements,

70    //treat last element

71    if (first != last) 

72    { // odd number of elements

73        if (*first < *min_result) 

74        { 

75            min_result = first; 

76            potential_min_result = last; 

77        } 

78        else if (*max_result < *first) 

79            max_result = first; 

80    } 

81 

82    // resolve min_result being incorrect

83    // with one extra comparison

84    // (in which case potential_min_result

85    // is necessarily the

86    // correct result)

87    if (potential_min_result != last && 

88        !(*min_result < *potential_min_result)) 

89    min_result = potential_min_result; 

90 

91    return

92      std::make_pair(min_result,max_result); 

93}

Please note that only one comparison is required for first two elements(aka first pair). The above requires at most three comparisons per pair.

In similar spirit, there are multiple combinations possible like:

first_min_first_max_element

first_min_last_max_element

last_min_first_max_element

last_min_last_max_element

Let us look at the implementation of

first_min_last_max_element as inspiration.

Program 1.11: first_min_last_max_element

1template <typename ForwardIterator>

2std::pair<ForwardIterator,ForwardIterator>

3first_min_last_max_element( 

4    ForwardIterator first, 

5    ForwardIterator last) 

6{ 

7    if (first == last) 

8        return std::make_pair(last,last); 

10    ForwardIterator min_result, 

11                    max_result = first; 

12 

13    ForwardIterator second = ++first; 

14 

15    if (second == last) 

16    return std::make_pair(min_result, 

17                          max_result); 

18 

19    if (*second < *min_result) 

20        min_result = second; 

21    else max_result = second; 

22 

23    first = ++second; 

24 

25    if (first != last) ++second; 

26 

27    while (second != last) 

28    { 

29        if (!(*second < *first)) 

30        { 

31            if (*first < *min_result) 

32                min_result = first; 

33            if (!(*second < *max_result)) 

34                max_result = second; 

35        } 

36        else

37        { 

38            if (*second < *min_result) 

39                min_result = second; 

40            if (!(*first < *max_result)) 

41                max_result = first; 

42        } 

43 

44        first = ++second; 

45 

46        if (first != last) ++second; 

47    } 

48 

49    if (first != last) 

50    { 

51        if (*first < *min_result) 

52            min_result = first; 

53        else if (!(*first < *max_result)) 

54            max_result = first; 

55    } 

56    return std::make_pair(min_result, max_result); 

57}

Generic Select

Selection can be reduced to sorting by sorting the sequence and then extracting the sought after element. This method is more efficient when many selections need to be made from a sequence, in which case only one initial, so-called expensive sort is needed, followed by many relatively less expensive extraction operations, usually in amortized constant time. In general, this method requires O(n log n) time, where n is the length of the sequence.

Let us try using similar ideas as in finding minimum and maximum of a given sequence for finding the kth smallest or kth largest element in a sequence.

Algorithm 4: Generic Kth Select Minimum

1:  function generic-kth-min-select(a, l, r, k)

2:   numElements r - l + 1

3:   for i