-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathRecursion.java
More file actions
205 lines (190 loc) · 8.47 KB
/
Recursion.java
File metadata and controls
205 lines (190 loc) · 8.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
public class Recursion {
/**
* Output the numbers in given integer range.
* @param start The start value of the range.
* @param end The end value of the range.
*/
public void printNumbers(int start, int end) {
if(start > end) { return; }
System.out.println(start);
printNumbers(start+1, end);
}
/**
* Output the numbers in given integer range.
* @param start The start value of the range.
* @param end The end value of the range.
*/
public void printNumbersOtherWay(int start, int end) {
if(start > end) { return; }
int mid = (start + end) / 2;
printNumbersOtherWay(start, mid - 1);
System.out.println(mid);
printNumbersOtherWay(mid + 1, end);
}
/**
* Output the numbers in given integer range in descending order.
* @param start The start value of the range.
* @param end The end value of the range.
*/
public void printNumbersReverse(int start, int end) {
if(start > end) { return; }
printNumbersReverse(start+1, end);
System.out.println(start);
}
/**
* Find the smallest element in the given subarray.
* @param a The array to search the smallest element in.
* @param start The start index of the subarray.
* @param end The end index of the subarray.
*/
public int min(int[] a, int start, int end) {
if(start == end) { return a[start]; }
int mid = (start + end) / 2;
int mleft = min(a, start, mid);
int mright = min(a, mid + 1, end);
return Math.min(mleft, mright);
}
/**
* Compute {@code a} raised to the power of {@code b}.
* @param a The base of the exponentiation.
* @param b The exponent.
* @return {@code a} raised to the power of {@code b}.
*/
public double power(double a, int b) {
if(b == 0) { return 1.0; }
if(b < 0) { return 1.0 / power(a, -b); }
if(b % 2 == 0) { return power(a * a, b / 2); }
return a * power(a, b - 1);
}
private double total = 0.0; // Shared fields outside recursion... just say no!
/**
* Compute the harmonic sum of the first {@code n} integers. This solution is made
* intentionally bad to illustrate an important point about recursion.
* @param n The number of terms in the harmonic sum.
* @return The harmonic sum up to the term {@code n}.
*/
public double harmonicSum(int n) {
if(n < 1) { return total; }
total += 1.0 / n;
return harmonicSum(n - 1);
}
// You need to remember to set such field back to zero between each call, and even
// then this does not work in multithreaded programming (see 209) where multiple
// execution threads can be calling this method simultaneously, since using fields
// for which only one copy exists makes the method not re-entrant.
/**
* Using only integer arithmetic, compute the sum of digits of parameter {@code n}.
* @param n The integers whose digits we sum.
* @return The sum of digits of the parameter integer.
*/
public int sumOfDigits(int n) {
if(n < 0) { return sumOfDigits(-n); }
if(n < 10) { return n; }
else { return n % 10 + sumOfDigits(n / 10); }
}
/**
* Using only integer arithmetic, reverse the digits of parameter {@code n}.
* For example, 12345 would become 54321.
* @param n The integers whose digits we reverse.
* @return The number constructed from reversing the digits of the original number.
*/
public int reverseDigits(int n, int acc) {
if(n < 1) { return acc; }
else return reverseDigits(n / 10, 10 * acc + n % 10);
}
/**
* Given an integer array and a goal value, determine whether there exists a subset
* of the first {@code n} elements in that array that add up exactly to the goal.
* @param a The array to search the subset in.
* @param goal The goal value to fulfill.
* @param n The length of the array prefix that we are constrained into.
* @return {@code true} if such a subset exists, {@code false} otherwise.
*/
public boolean subsetSum(int[] a, int goal, int n) {
if(goal == 0) { return true; }
if(n == 0) { return false; }
return subsetSum(a, goal - a[n-1], n - 1) || subsetSum(a, goal, n - 1);
}
/**
* Given an integer array and a goal value, count how many subsets of the first
* {@code n} elements in that array exist whose elements add up exactly to the goal.
* @param a The array to search the subset in.
* @param goal The goal value to fulfill.
* @param n The length of the array prefix that we are constrained into.
* @return The number of such subsets in the first {@code n} elements in the array.
*/
public int subsetSumCount(int[] a, int goal, int n) {
if(goal == 0) { return 1; }
if(n == 0) { return 0; }
return subsetSumCount(a, goal - a[n-1], n - 1) + subsetSumCount(a, goal, n - 1);
}
/**
* The classic n-queens problem. How many ways can you place {@code n} chess queens
* on an n*n chessboard so that no two queens are on the same row, column or diagonal?
* The solution is the first backtracking algorithm that you must teach by law.
* @param n The number of queens to place.
* @return The count of possible ways.
*/
public int nQueens(int n) {
queenLevelCount = new int[n + 1];
forwardCheckingCutoffCount = new int[n + 1];
int result = nQueens(n, new boolean[n], new boolean[2*n], new boolean[2*n]);
System.out.println("Calls per level:");
int sum = 0;
for(int i = n; i >= 0; i--) {
System.out.println(queenLevelCount[i] + " calls and " +
forwardCheckingCutoffCount[i] + " cutoffs on level " + i + ".");
sum += queenLevelCount[i];
}
System.out.println("Total number of calls was " + sum + ".");
return result;
}
private int[] queenLevelCount;
private int[] forwardCheckingCutoffCount;
// Utility method to use for forward checking: Does the given row still have any
// places possible to place the queen?
private boolean queenStillPossible(int row, boolean[] cols, boolean[] ne, boolean[] se) {
for(int col = 0; col < cols.length; col++) {
if(!(cols[col] || ne[cols.length - row + col] || se[row + col])) {
return true;
}
}
return false;
}
// How many ways can you place the remaining n queens, with rows etc. already taken?
// Just like in Subset Sum, the answer and the running time are exponential as function
// of n. Try values 10, 12, 14. For how big n can you still wait for the result?
private int nQueens(int n, boolean[] cols, boolean[] ne, boolean[] se) {
queenLevelCount[n]++;
if(n == 0) { return 1; } // All queens successfully placed.
// Loop through all the possibilities to place the current queen in row n-1.
int row = n - 1; // The row in which the current queen will be placed.
int sum = 0;
// Utilize the mirror symmetry when placing the queen on the first row, to
// cut the running time of recursion in half.
int limit = (n == cols.length - 1) ? cols.length / 2 + cols.length % 2 : cols.length;
for(int col = 0; col < limit; col++) {
if(cols[col]) { continue; } // This column was already taken.
if(ne[cols.length - row + col]) { continue; } // This diagonal was already taken.
if(se[row + col]) { continue; } // This diagonal was already taken.
// Place the queen in position (row, col) and update the arrays.
cols[col] = true;
ne[cols.length - row + col] = true;
se[row + col] = true;
// Recursively add up all the possible ways to place the remaining n - 1 queens.
if(n < 1 || n >= cols.length / 2 - 1 || n < cols.length / 4 || queenStillPossible(1, cols, ne, se)) {
int ways = nQueens(n - 1, cols, ne, se);
// Symmetry adjustment for the first queen.
sum += (n == cols.length - 1 && col < cols.length / 2) ? 2 * ways: ways;
}
else {
forwardCheckingCutoffCount[n]++;
}
// Undo the placement of the current queen before backtracking.
cols[col] = false;
ne[cols.length - row + col] = false;
se[row + col] = false;
}
return sum;
}
}