Related
Given a 2D matrix:
matrix = [
[ 1, 2, 3, 4 ],
[ 5, 6, 7, 8 ],
[ 9, 10, 11, 12 ],
[ 13, 14, 15, 16 ]
]
How can we rotate the matrix anti-clockwise so that values are pushed like this?
matrix = [
[ 2, 3, 4, 8 ]
[ 1, 7, 11, 12 ]
[ 5, 6, 10, 16 ]
[ 9, 13, 14, 15 ]
]
Note
This question is not a duplicate of this & this because what I'm trying to achieve is by rotating the values in anti-clockwise fashion.
My current implementation & Problem
My current implementation only prints out the values in anti-clockwise fashion, but it does not rotate the values.
layers = [_rows, _cols].min / 2
r1, r2, c3, c4 = 0, _rows, _cols, _cols
new_matrix = Array.new(_rows + 1) { Array.new(_cols + 1) }
(0..layers).each do |layer|
row_top_left, row_bottom_left, col_top_right, col_bottom_right = r1, r2, c3, c4
result = []
while row_top_left < row_bottom_left
result << matrix[row_top_left][layer]
row_top_left += 1
end
row_bottom_left = layer
while row_bottom_left < col_bottom_right
result << matrix[row_top_left][row_bottom_left]
row_bottom_left += 1
end
temp_col_bottom_right = col_bottom_right
temp_col_top_right = layer
while col_bottom_right > temp_col_top_right
result << matrix[col_bottom_right][temp_col_bottom_right]
col_bottom_right -= 1
end
# p row_top_left
tmp_row_top_left = layer
while col_top_right > tmp_row_top_left
result << matrix[tmp_row_top_left][col_top_right]
col_top_right -= 1
end
p result.cycle
r1 += 1
r2 -= 1
c3 -= 1
c4 -= 1
update v0.1
The key idea is that the matrix needs to be rotated in the correct way. For example, let's say our matrix requires 2 rotation. Therefore:
matrix_rotation(
matrix.length - 1, # rows
matrix[0].length - 1, # columns
2, # Nom. of rotation
matrix # The matrix
)
matrix = [
# Original Iter: 1 Iter: 2
[ 1, 2, 3, 4 ], # [ 2, 3, 4, 8 ] # [ 3, 4, 8, 12 ]
[ 5, 6, 7, 8 ], # [ 1, 7, 11, 12 ] # [ 2, 11, 10, 16 ]
[ 9, 10, 11, 12 ], # [ 5, 6, 10, 16 ] # [ 1, 7, 6, 15 ]
[ 13, 14, 15, 16 ] # [ 9, 13, 14, 15 ] # [ 5, 9, 13, 14 ]
]
Update v0.2
The dimension of the array is denoted: NxM where N and M can be any numbers, even or odd. For example 5x4, 4,4, 4x8 etc..
There is no such thing as "empty squares".
TL:DR
If you want to jump straight to the solution code, jump to the bottom section of this answer.
Explanation
You need to break down the problem and solve each one independently.
Problems
Get the number of layers
Loop in reverse spiral form to just get the expected values
Shift them based on the rotation parameter given
Let us walk through each point separately:
Get the number of layers
You need a way to get the number of layers. The below matrix has 2 layers. How?
given a matrix:
matrix layers
--------------------------------
| 1, 2, 3, 4 | 0 0 0 0 |
| 5, 6, 7, 8 | 0 1 1 0 |
| 9, 10, 11, 12 | 0 1 1 0 |
| 13, 14, 15, 16 | 0 0 0 0 |
--------------------------------
To find the number of layers, simply do:
[rows, cols].min / 2
Thus the first problem is done.
Loop in reverse spiral form to just get the expected values
This part requires a lot of thinking. Let us visualise:
matrix layers
--------------------------------
| 1, 2, 3, 4 | ↓ ← ← ↰ | 0 0 0 0 |
| 5, 6, 7, 8 | ↓ 1 1 ↑ | 0 ↓ ↰ 0 |
| 9, 10, 11, 12 | ↓ 1 1 ↑ | 0 ↳ → 0 |
| 13, 14, 15, 16 | ↳ → → → | 0 0 0 0 |
--------------------------------
This is achievable. We will have 4 for loops. Each loop will take care of:
left (top to bottom)
bottom (left to right)
right (bottom to top)
top (right to left)
Before I get into the loops, we need some container to store our values in spiral form.
Let us have a temp array to store the values:
# this array will get us the output of borders of the layer
row = []
For the sake of explanation, let us only work on the outer most layer. (i.e. 0th layer:
1st Loop (Left: top to bottom)
# this loop will output the top-left side of the matrix
# ==============================
# ↓ [ 1, 2, 3, 4 ]
# ↓ [ 5, 6, 7, 8 ]
# ↓ [ 9, 10, 11, 12 ]
# ↓ [ 13, 14, 15, 16 ]
# Output: [[1, 5, 9], [6] ]
# ==============================
(0...rows - 1 - layer).each do |i|
row << matrix[i][layer]
end
Note: 0 means the 0th layer.
2nd Loop (Bottom: Left to Right)
# this loop will output the bottom side of the matrix
# ==============================
# ↓ [ 1, 2, 3, 4 ]
# ↓ [ 5, 6, 7, 8 ]
# ↓ [ 9, 10, 11, 12 ]
# ↓ [ 13, 14, 15, 16 ]
# ↪ → → → →
# Output: [[1, 5, 9, 13, 14, 15], [6, 10]]
# ==============================
(0...cols - 1 - layer).each do |i|
row << matrix[rows - 1 - layer][i]
end
3rd Loop (Right: Bottom to Top)
# this loop will output the right side of the matrix
# ==============================
# ↓ [ 1, 2, 3, 4 ] ↑
# ↓ [ 5, 6, 7, 8 ] ↑
# ↓ [ 9, 10, 11, 12 ] ↑
# [ 13, 14, 15, 16 ] ↑
# ↪ → → → → ⤻
# Output: [[1, 5, 9, 13, 14, 15, 16, 12, 8], [6, 10, 11]]
# ==============================
(rows - 1 - layer).step(0 + 1, -1).each do |i|
row << matrix[i][cols - 1 - layer]
end
4th Loop (Top: Right to Left)
# this loop will output the top side of the matrix
# ==============================
# ← ← ← ← ↰
# ↓ [ 1, 2, 3, 4 ] ↑
# ↓ [ 5, 6, 7, 8 ] ↑
# ↓ [ 9, 10, 11, 12 ] ↑
# [ 13, 14, 15, 16 ] ↑
# ↪ → → → → ⤻
# Output: [[1, 5, 9, 13, 14, 15, 16, 12, 8, 4, 3, 2], [6, 10, 11, 7]]
# ==============================
(cols - 1 - layer).step(0 + 1, -1).each do |i|
row << matrix[layer][i]
end
Shift them based on the rotation parameter given
So now at this point, we have the values in the spiral form. But the most important aspect of this problem lies in this section. How does one shift the values? Funnily enough, we will use modulo.
The modulo will do the main thing here. It will allow us to shift values based on the rotate. But also give us the correct index in the array to start the shift. For example, if we want to rotate 2 times: 2 % 12 = 2 for the outer most layer.
# row = [1, 5, 9, 13, 14, 15, 16, 12, 8, 4, 3, 2]
shift = rotate % row.size
# if we negate shift variable, we can get correct index
# i.e. row[-2] = 3
idx = -shift
Before we shift values, let us create another matrix which will contain the correct values:
# let us create a new matrix
result = (1..( rows * cols )).each_slice(rows).to_a
We will loop again in the same manner, but get the values from the idx in row. For example:
(0...rows - 1 - 0).each do |i|
result[i][layer] = row[idx]
idx += 1
idx %= row.size
end
(0...cols - 1 - 0).each do |i|
result[rows - 1 - 0][i] = row[idx]
idx += 1
idx %= row.size
end
(rows - 1 - 0).step(0 + 1, -1).each do |i|
result[i][cols - 1 - 0] = row[idx]
idx += 1
idx %= row.size
end
(cols - 1 - 0).step(0 + 1, -1).each do |i|
result[0][i] = row[idx]
idx += 1
idx %= row.size
end
Note: 0 is the 0th layer (for the sake of explanation)
Solution
matrix_4_x_4 = (1..16).each_slice(4).to_a
matrix_8_x_8 = (1..64).each_slice(8).to_a
def matrix_rotation(*args)
# let us extract rows & cols from our matrix. We also need to know how
# times to rotate.
rows, cols, rotate, matrix = args
# to find out how many layers our matrix have, simply get the min of the two (rows, cols)
# and divide it
layers, str_cols = [rows, cols].min / 2, ""
# needed to beatify our console output in table format
cols.times do str_cols << "%5s " end
# we will work on a temporary array
temp_rows = []
# so the first task is to loop n times, where n is the number of layers
(0...layers).each do |layer|
# this array will get us the output of borders of the layer
row = []
# this loop will output the top-left side of the matrix
# ==============================
# ↓ [ 1, 2, 3, 4 ]
# ↓ [ 5, 6, 7, 8 ]
# ↓ [ 9, 10, 11, 12 ]
# ↓ [ 13, 14, 15, 16 ]
# Output: [[1, 5, 9], [6] ]
# ==============================
(layer...rows - 1 - layer).each do |i|
row << matrix[i][layer]
end
# this loop will output the bottom side of the matrix
# ==============================
# ↓ [ 1, 2, 3, 4 ]
# ↓ [ 5, 6, 7, 8 ]
# ↓ [ 9, 10, 11, 12 ]
# ↓ [ 13, 14, 15, 16 ]
# ↪ → → → →
# Output: [[1, 5, 9, 13, 14, 15], [6, 10]]
# ==============================
(layer...cols - 1 - layer).each do |i|
row << matrix[rows - 1 - layer][i]
end
# this loop will output the right side of the matrix
# ==============================
# ↓ [ 1, 2, 3, 4 ] ↑
# ↓ [ 5, 6, 7, 8 ] ↑
# ↓ [ 9, 10, 11, 12 ] ↑
# [ 13, 14, 15, 16 ] ↑
# ↪ → → → → ⤻
# Output: [[1, 5, 9, 13, 14, 15, 16, 12, 8], [6, 10, 11]]
# ==============================
(rows - 1 - layer).step(layer + 1, -1).each do |i|
row << matrix[i][cols - 1 - layer]
end
# this loop will output the top side of the matrix
# ==============================
# ← ← ← ← ↰
# ↓ [ 1, 2, 3, 4 ] ↑
# ↓ [ 5, 6, 7, 8 ] ↑
# ↓ [ 9, 10, 11, 12 ] ↑
# [ 13, 14, 15, 16 ] ↑
# ↪ → → → → ⤻
# Output: [[1, 5, 9, 13, 14, 15, 16, 12, 8, 4, 3, 2], [6, 10, 11, 7]]
# ==============================
(cols - 1 - layer).step(layer + 1, -1).each do |i|
row << matrix[layer][i]
end
temp_rows << row
end
# let us create a new matrix
result = (1..( rows * cols )).each_slice(rows).to_a
# we're going to loop in the same manner as before
(0...layers).each do |layer|
# based on current layer, get the values around that layer
row = temp_rows[layer]
# !important: the modulo will do the main thing here:
# It will allow us to shift values based on the rotate. But also
# gives us the correct index in the array to start the shift.
# For example, if we want to rotate 2 times: 2 % 12 = 2 for the outer most layer
shift = rotate % row.size
# when whe negate the shift value, we will get the correct index from the end of the array.
# row = [1, 5, 9, 13, 14, 15, 16, 12, 8, 4, 3, 2]
# So -2 in row[-2] for the outer layer is 3. We increment idx, then row[-1] is 2 etc..
idx = -shift
(layer...rows - 1 - layer).each do |i|
result[i][layer] = row[idx]
idx += 1
idx %= row.size
end
(layer...cols - 1 - layer).each do |i|
result[rows - 1 - layer][i] = row[idx]
idx += 1
idx %= row.size
end
(rows - 1 - layer).step(layer + 1, -1).each do |i|
result[i][cols - 1 - layer] = row[idx]
idx += 1
idx %= row.size
end
(cols - 1 - layer).step(layer + 1, -1).each do |i|
result[layer][i] = row[idx]
idx += 1
idx %= row.size
end
end
result.each do |row| printf("#{str_cols}\n", *row) end
end
matrix_rotation(
matrix_8_x_8.size,
matrix_8_x_8.first.size,
2,
matrix_8_x_8
)
Code
def nxt(rows, cols, row, col)
case row
when rows[:first]
col == cols[:last] ? [row+1, col] : [row, col+1]
when rows[:last]
col == cols[:first] ? [row-1, col] : [row, col-1]
else
col == cols[:last] ? [row+1, col] : [row-1, col]
end
end
def rotate_array_times(matrix, n)
arr = matrix.dup.map(&:dup)
nrows, ncols = arr.size, arr.first.size
0.upto([nrows, ncols].min/2-1) do |m|
rows = { first: m, last: nrows-m-1 }
cols = { first: m, last: ncols-m-1 }
rect_size = 2 * (nrows + ncols) - 8*m - 4
rotations = n % rect_size
row = col = rrow = rcol = m
rotations.times { rrow, rcol = nxt(rows, cols, rrow, rcol) }
rect_size.times do
arr[row][col] = matrix[rrow][rcol]
row, col = nxt(rows, cols, row, col)
rrow, rcol = nxt(rows, cols, rrow, rcol)
end
end
arr
end
Examples
matrix = [
[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15, 16]
]
(1..3).each { |n| p rotate_array_times(matrix, n) }
[[2, 3, 4, 8],
[1, 7, 11, 12],
[5, 6, 10, 16],
[9, 13, 14, 15]]
[[3, 4, 8, 12],
[2, 11, 10, 16],
[1, 7, 6, 15],
[5, 9, 13, 14]]
[[4, 8, 12, 16],
[3, 10, 6, 15],
[2, 11, 7, 14],
[1, 5, 9, 13]]
matrix = (1..24).each_slice(4).to_a
#=> [[ 1, 2, 3, 4],
# [ 5, 6, 7, 8],
# [ 9, 10, 11, 12],
# [13, 14, 15, 16],
# [17, 18, 19, 20],
# [21, 22, 23, 24]]
(1..3).each { |n| p rotate_array_times(matrix, n) }
#=> [[ 2, 3, 4, 8],
# [ 1, 7, 11, 12],
# [ 5, 6, 15, 16],
# [ 9, 10, 19, 20],
# [13, 14, 18, 24],
# [17, 21, 22, 23]]
# [[ 3, 4, 8, 12],
# [ 2, 11, 15, 16],
# [ 1, 7, 19, 20],
# [ 5, 6, 18, 24],
# [ 9, 10, 14, 23],
# [13, 17, 21, 22]]
# [[ 4, 8, 12, 16],
# [ 3, 15, 19, 20],
# [ 2, 11, 18, 24],
# [ 1, 7, 14, 23],
# [ 5, 6, 10, 22],
# [ 9, 13, 17, 21]]
matrix = (1..48).each_slice(8).to_a
#=> [[ 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]]
(1..3).each { |n| p rotate_array_times(matrix, n) }
[[ 2, 3, 4, 5, 6, 7, 8, 16],
[ 1, 11, 12, 13, 14, 15, 23, 24],
[ 9, 10, 20, 21, 22, 30, 31, 32],
[17, 18, 19, 27, 28, 29, 39, 40],
[25, 26, 34, 35, 36, 37, 38, 48],
[33, 41, 42, 43, 44, 45, 46, 47]]
[[ 3, 4, 5, 6, 7, 8, 16, 24],
[ 2, 12, 13, 14, 15, 23, 31, 32],
[ 1, 11, 21, 22, 30, 29, 39, 40],
[ 9, 10, 20, 19, 27, 28, 38, 48],
[17, 18, 26, 34, 35, 36, 37, 47],
[25, 33, 41, 42, 43, 44, 45, 46]]
[[ 4, 5, 6, 7, 8, 16, 24, 32],
[ 3, 13, 14, 15, 23, 31, 39, 40],
[ 2, 12, 22, 30, 29, 28, 38, 48],
[ 1, 11, 21, 20, 19, 27, 37, 47],
[ 9, 10, 18, 26, 34, 35, 36, 46],
[17, 25, 33, 41, 42, 43, 44, 45]]
Explanation
nxt
Given row and column indices row and col, nxt(rows, cols, row, col) returns the indices [next_row, next_col] of the "next" element on the perimeter of a subarray that is to replace the element (also on the perimeter) at indices [row, col] in a single iteration. The subarray is given by the hashes rows and cols which each have keys :first and :last.
Let's consider an an array arr with 4 elements (rows), each element (row) having 6 values (columns). Then
nrows, ncols = arr.size, arr.first.size
#=> [4, 6]
If m = 0
rows = { first: m, last: nrows-m-1 }
#=> {:first=>0, :last=>3}
cols = { first: m, last: ncols-m-1 }
#=> {:first=>0, :last=>5}
It is seen that rows and cols describes the "perimeter" of he array matrix. We can see how nxt works as follows.
first_row, first_col = rows[:first], cols[:first]
row, col = first_row, first_col
print "[#{row}, #{col}]"
loop do
next_row, next_col = nxt(rows, cols, row, col)
print "->[#{next_row}, #{next_col}]"
row, col = next_row, next_col
(puts; break) if [row, col] == [first_row, first_col]
end
[0, 0]->[0, 1]->[0, 2]->[0, 3]->[0, 4]->[0, 5]->[1, 5]->[2, 5]->[3, 5]->
[3, 4]->[3, 3]->[3, 2]->[3, 1]->[3, 0]->[2, 0]->[1, 0]->[0, 0]
If m = 1, the above calculation yields
[1, 1]->[1, 2]->[1, 3]->[1, 4]->[2, 4]->[2, 3]->[2, 2]->[2, 1]->[1, 1]
rotate_array_times
This method constructs a deep copy of matrix, arrr, whose elements are rotated in the prescribed matter n times and then returns the resulting array.
To speed calculations, n is replaced by a modulus of itself. For a 4x4 array, for example, after 12 iterations the perimeter of the array would be back to its original value. Therefore, it is sufficient to perform n % 12 rotations.
matrix contains n = [matrix.size, matrix.first.size].min subarrays whose perimeters are to be rotated. The top-left corner of each subarray is given by the coordinate [m,m], where m = 0..n-1.
For the subarray specified by m the first step is to determine the location of the element of matrix that is to replace the element of arr at [m,m]. That is done in the line
rotations.times { rrow, rcol = nxt(rows, cols, rrow, rcol) }
("rrow" and "rcol" for "replacement row" and "replacement col", respectively). At this time the element of arr at location row #=> m, col #=> m is to be replaced the element of matrix at the location given by rrow and rcol. The following operations then performed as many times as there are elements in the perimeter of the subarray which are to be rotated:
arr[row][col] = matrix[rrow][rcol]
row, col = nxt(rows, cols, row, col)
rrow, rcol = nxt(rows, cols, rrow, rcol)
Tweaking efficiency
A modest improvement in efficiency could be achieved by replacing the line
rotations.times { rrow, rcol = nxt(rows, cols, rrow, rcol) }
with
rrow, rcol = first_replacement_loc(rows, cols, rotations)
and adding the following method.
def first_replacement_loc(rows, cols, rotations)
ncm1 = cols[:last]-cols[:first]
nrm1 = rows[:last]-rows[:first]
return [rows[:first], cols[:first]+rotations] if rotations <= ncm1
rotations -= ncm1
return [rows[:first]+rotations, cols[:last]] if rotations <= nrm1
rotations -= nrm1
return [rows[:last], cols[:last]-rotations] if rotations <= ncm1
rotations -= ncm1
[rows[:last]-rotations, cols[:first]]
end
This is another implementation (I didn't make a method, just the logic that needs to be improved).
array = (1..24).each_slice(6).to_a
array.each { |e| p e }
puts
n = 4 # sub matrix rows
m = 6 # sub matrix cols
x = 0 # x row origin (corner) of the rotation
y = 0 # y col origin (corner) of the rotation
rotations = 2 # negative is ccw, positive is cw
raise "Sub matrix too small, must be 2x2 at least" if m < 2 || n < 2
# to add: check if the submatrix is inside the matrix, given the origin x, y
y_size = array.size
x_size = array.size
idx_map = Array.new(n){ [] }
m.times.map { |mm| n.times.map { |nn| idx_map[nn][mm] = [nn + x, mm + y] } }
before = [(idx_map.map(&:shift)).concat(idx_map.pop).concat(idx_map.map(&:pop).reverse).concat(idx_map.shift.reverse)].flatten(1)
after = before.rotate(rotations)
tmp = array.map(&:dup)
before.size.times.map { |idx| array[before[idx][0]][before[idx][1]] = tmp[after[idx][0]][after[idx][1]]}
array.each { |e| p e }
#=> [1, 2, 3, 4, 5, 6]
#=> [7, 8, 9, 10, 11, 12]
#=> [13, 14, 15, 16, 17, 18]
#=> [19, 20, 21, 22, 23, 24]
#=>
#=> [13, 7, 1, 2, 3, 4]
#=> [19, 8, 9, 10, 11, 5]
#=> [20, 14, 15, 16, 17, 6]
#=> [21, 22, 23, 24, 18, 12]
You can also rotate a 3x3 sub-matrix starting in (1, 1), so, for example n = 3, m = 3, x = 1, y = 1 and rotations = -1:
#=> [1, 2, 3, 4, 5, 6]
#=> [7, 9, 10, 16, 11, 12]
#=> [13, 8, 15, 22, 17, 18]
#=> [19, 14, 20, 21, 23, 24]
I thought it would be interesting to benchmark my code against #Humbledore's. (#iGian: I can add your code to the benchmark if you can edit your answer to wrap it in a method with arguments matrix and nbr_rotations).
def nxt(rows, cols, row, col)
case row
when rows[:first]
col == cols[:last] ? [row+1, col] : [row, col+1]
when rows[:last]
col == cols[:first] ? [row-1, col] : [row, col-1]
else
col == cols[:last] ? [row+1, col] : [row-1, col]
end
end
def cary1(matrix, n)
arr = matrix.dup.map(&:dup)
nrows, ncols = arr.size, arr.first.size
0.upto([nrows, ncols].min/2-1) do |m|
rows = { first: m, last: nrows-m-1 }
cols = { first: m, last: ncols-m-1 }
rect_size = 2 * (nrows + ncols) - 8*m - 4
rotations = n % rect_size
row = col = rrow = rcol = m
rotations.times { rrow, rcol = nxt(rows, cols, rrow, rcol) }
rect_size.times do
arr[row][col] = matrix[rrow][rcol]
row, col = nxt(rows, cols, row, col)
rrow, rcol = nxt(rows, cols, rrow, rcol)
end
end
arr
end
def first_replacement_loc(rows, cols, rotations)
ncm1 = cols[:last]-cols[:first]
nrm1 = rows[:last]-rows[:first]
return [rows[:first], cols[:first]+rotations] if rotations <= ncm1
rotations -= ncm1
return [rows[:first]+rotations, cols[:last]] if rotations <= nrm1
rotations -= nrm1
return [rows[:last], cols[:last]-rotations] if rotations <= ncm1
rotations -= ncm1
[rows[:last]-rotations, cols[:first]]
end
def cary2(matrix, n)
arr = matrix.dup.map(&:dup)
nrows, ncols = arr.size, arr.first.size
0.upto([nrows, ncols].min/2-1) do |m|
rows = { first: m, last: nrows-m-1 }
cols = { first: m, last: ncols-m-1 }
rect_size = 2 * (nrows + ncols) - 8*m - 4
rotations = n % rect_size
row = col = m
rrow, rcol = first_replacement_loc(rows, cols, rotations)
rect_size.times do
arr[row][col] = matrix[rrow][rcol]
row, col = nxt(rows, cols, row, col)
rrow, rcol = nxt(rows, cols, rrow, rcol)
end
end
arr
end
def humbledore(matrix, rotate)
rows, cols = matrix.size, matrix.first.size
layers, str_cols = [rows, cols].min / 2, ""
# cols.times do str_cols << "%5s " end
temp_rows = []
(0...layers).each do |layer|
row = []
(layer...rows - 1 - layer).each do |i|
row << matrix[i][layer]
end
(layer...cols - 1 - layer).each do |i|
row << matrix[rows - 1 - layer][i]
end
(rows - 1 - layer).step(layer + 1, -1).each do |i|
row << matrix[i][cols - 1 - layer]
end
(cols - 1 - layer).step(layer + 1, -1).each do |i|
row << matrix[layer][i]
end
temp_rows << row
end
result = (1..( rows * cols )).each_slice(rows).to_a
(0...layers).each do |layer|
row = temp_rows[layer]
shift = rotate % row.size
idx = -shift
(layer...rows - 1 - layer).each do |i|
result[i][layer] = row[idx]
idx += 1
idx %= row.size
end
(layer...cols - 1 - layer).each do |i|
result[rows - 1 - layer][i] = row[idx]
idx += 1
idx %= row.size
end
(rows - 1 - layer).step(layer + 1, -1).each do |i|
result[i][cols - 1 - layer] = row[idx]
idx += 1
idx %= row.size
end
(cols - 1 - layer).step(layer + 1, -1).each do |i|
result[layer][i] = row[idx]
idx += 1
idx %= row.size
end
end
result
end
require 'benchmark'
def test(rows, cols, rotations)
puts "\nrows = #{rows}, cols = #{cols}, rotations = #{rotations}"
matrix = (1..rows*cols).each_slice(cols).to_a
Benchmark.bm do |x|
x.report("Cary1") { cary1(matrix, rotations) }
x.report("Cary2") { cary2(matrix, rotations) }
x.report("Humbledore") { humbledore(matrix, rotations) }
end
end
test 10,10,1
rows = 10, cols = 10, rotations = 1
user system total real
Cary1 0.000000 0.000000 0.000000 ( 0.000077)
Cary2 0.000000 0.000000 0.000000 ( 0.000074)
Humbledore 0.000000 0.000000 0.000000 ( 0.000051)
test 10,10,78
rows = 10, cols = 10, rotations = 78
user system total real
Cary1 0.000000 0.000000 0.000000 ( 0.000079)
Cary2 0.000000 0.000000 0.000000 ( 0.000061)
Humbledore 0.000000 0.000000 0.000000 ( 0.000053)
test 100,100,378
rows = 100, cols = 100, rotations = 378
user system total real
Cary1 0.000000 0.000000 0.000000 ( 0.007673)
Cary2 0.015625 0.000000 0.015625 ( 0.005168)
Humbledore 0.000000 0.000000 0.000000 ( 0.002919)
test 500,500,1950
rows = 500, cols = 500, rotations = 1950
user system total real
Cary1 0.171875 0.000000 0.171875 ( 0.166671)
Cary2 0.140625 0.000000 0.140625 ( 0.137141)
Humbledore 0.046875 0.000000 0.046875 ( 0.053705)
test 500,1000,2950
rows = 500, cols = 1000, rotations = 2950
user system total real
Cary1 0.296875 0.000000 0.296875 ( 0.292997)
Cary2 0.234375 0.000000 0.234375 ( 0.248384)
Humbledore 0.125000 0.000000 0.125000 ( 0.103964)
Benchmark reports execution times in seconds. The results are found to be quite consistent.
Notice that in all of the tests I performed the number of columns of the array is at least as large as the number of rows. That's because a NoMethodError (undefined method '[]=' for nil:NilClass) exception was raised in Humbledore's code whenever the number of rows exceeded the number of columns. (Try test 3,2,1, for example.) The error message occurred in the second line of the following block of code.
(layer...cols - 1 - layer).each do |i|
result[rows - 1 - layer][i] = row[idx]
idx += 1
idx %= row.size
end
I expect the problem is easily fixable.
I am writing a method that takes two sorted arrays and I want it to return a merged array with all the values sorted. Given the two arrays below:
array_one = [3, 4, 8]
array_two = [1, 5, 7]
I want my merge_arrays method to return:
[1, 3, 4, 5, 7, 8]
My current algorithm is below:
def merge_arrays(array_one, array_two)
merged_array_size = array_one.length + array_two.length
merged_array = []
current_index_on_one = 0
current_index_on_two = 0
current_merged_index = 0
for i in (0..merged_array_size - 1)
if array_one[current_index_on_one] < array_two[current_index_on_two]
merged_array[current_merged_index] = array_one[current_index_on_one]
current_index_on_one += 1
current_merged_index += 1
else
merged_array[current_merged_index] = array_two[current_index_on_two]
current_index_on_two += 1
current_merged_index += 1
end
end
return merged_array
end
I am getting an error 'undefined method `<' for nil:NilClass'. I don't understand how the conditional is receiving this. I debugged the variables in the conditionals and they are giving true or false values. I'm not sure what is causing this error.
Maybe I am missing the point but you can do:
(array_one + array_two).sort
=> [1, 3, 4, 5, 7, 8]
I am getting an error 'undefined method `<' for nil:NilClass'. I don't understand how the conditional is receiving this.
You start by comparing index 0 to index 0:
[3, 4, 8] [1, 5, 7]
0-----------0 #=> 3 < 1
Then you increment the lower value's index by 1:
[3, 4, 8] [1, 5, 7]
0--------------1 #=> 3 < 5
And so on:
[3, 4, 8] [1, 5, 7]
1-----------1 #=> 4 < 5
[3, 4, 8] [1, 5, 7]
2--------1 #=> 8 < 5
[3, 4, 8] [1, 5, 7]
2-----------2 #=> 8 < 7
At that point you get:
[3, 4, 8] [1, 5, 7]
2--------------3 #=> 8 < nil
Index 3 is outside the array's bounds, so array_two[current_index_on_two] returns nil and:
if array_one[current_index_on_one] < array_two[current_index_on_two]
# ...
end
becomes
if 8 < nil
# ...
end
resulting in ArgumentError(comparison of Integer with nil failed). If nil is on the left hand side, you'd get NoMethodError (undefined method `<' for nil:NilClass).
Here's one way you can write merge using recursion. Note, as you specified, both inputs must already be sorted otherwise the output will be invalid. The inputs can vary in size.
def merge (xs, ys)
if xs.empty?
ys
elsif ys.empty?
xs
else
x, *_xs = xs
y, *_ys = ys
if x < y
[x] + (merge _xs, ys)
else
[y] + (merge xs, _ys)
end
end
end
merge [ 1, 3, 4, 6, 8, 9 ], [ 0, 2, 5, 7 ]
# => [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Assuming you have two sorted arrays. You need to create pipeline using recursion going to crunch through each array. checking at each iteration to see
which value at index 0 of either array is lower, removing that from the array and appending that value to the result array.
def merge_arrays(a, b)
# build a holder array that is the size of both input arrays O(n) space
result = []
# get lower head value
if a[0] < b[0]
result << a.shift
else
result << b.shift
end
# check to see if either array is empty
if a.length == 0
return result + b
elsif b.length == 0
return result + a
else
return result + merge_arrays(a, b)
end
end
> a = [3, 4, 6, 10, 11, 15]
> b = [1, 5, 8, 12, 14, 19]
> merge_arrays(a, b)
#=> [1, 3, 4, 5, 6, 8, 10, 11, 12, 14, 15, 19]
I made slight changes to your code in order to make it work. See the comments inside.
array_one = [2, 3, 4, 8, 10, 11, 12, 13, 15]
array_two = [1, 5, 6, 7, 9, 14]
def merge_arrays(array_one, array_two)
array_one, array_two = array_two, array_one if array_one.length > array_two.length # (1) swap arrays to make satement (3) work, need array_two always be the longest
merged_array_size = array_one.length + array_two.length
merged_array = []
current_index_on_one = 0
current_index_on_two = 0
current_merged_index = 0
for i in (0...merged_array_size-1) # (2) three points to avoid the error
if (!array_one[current_index_on_one].nil? && array_one[current_index_on_one] < array_two[current_index_on_two]) # (3) check also if array_one is nil
merged_array[current_merged_index] = array_one[current_index_on_one]
current_index_on_one += 1
current_merged_index += 1
else
merged_array[current_merged_index] = array_two[current_index_on_two]
current_index_on_two += 1
current_merged_index += 1
end
end
merged_array[current_merged_index] = array_one[current_index_on_one] || array_two[current_index_on_two] # (4) add the missing element at the end of the loop, looks what happen if you comment out this line
return merged_array
end
p merge_arrays(array_one, array_two)
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
The error was coming because the loop was making one step over. The solution is to stop before and insert the missing element at the end of the loop.
It works also with:
# for i in (1...merged_array_size)
# and
# for i in (1..merged_array_size-1)
# and
# (merged_array_size-1).times do
arr1 = [3, 4, 8, 9, 12]
arr2 = [1, 5, 7, 8, 13]
arr = [arr1, arr2]
idx = [0, 0]
(arr1.size + arr2.size).times.with_object([]) do |_,a|
imin = [0, 1].min_by { |i| arr[i][idx[i]] || Float::INFINITY }
a << arr[imin][idx[imin]]
idx[imin] += 1
end
#=> [1, 3, 4, 5, 7, 8, 8, 9, 12, 13]
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 7 years ago.
Improve this question
I want to select consecutive integers from an array, for example:
Input: (doesn't have to be sorted)
array = [1, 3, 6, 7, 8, 9, 10, 12, 13, 17]
Expected output:
#=> [6, 7, 8, 9, 10, 12, 13]
This is my code so far:
array = [1, 3, 9, 6, 7, 10, 8, 12, 13, 17]
newarray = []
z = 0
array.sort.each_cons(2) do |x, y|
if y == x + 1
if z == 0
newarray.insert(0, x, y)
else
newarray.push(y)
end
end
z = z + 1
end
newarray #=> [7, 8, 9, 10, 13]
As you can see, 6 and 12 are missing.
What's wrong with my code and - besides the bug - is there a better way to write it?
Your code compares adjacent elements x and y, but pushes x and y only if they are the first elements in the array (if z == 0; newarray.insert(0, x, y)). Otherwise, only y is pushed, so the first element of each group of consecutive integers is missing.
This table illustrates what's going on:
x y y == x+1
--------------------
1 3 false
3 6 false
6 7 true #=> push(7)
7 8 true #=> push(8)
8 9 true #=> push(9)
9 10 true #=> push(10)
10 12 false
12 13 true #=> push(13)
13 17 false
You could fix your code by using z as an indicator for the beginning of a group:
z = true
array.sort.each_cons(2) do |x, y|
if y == x + 1
if z
newarray.push(x, y)
z = false
else
newarray.push(y)
end
else
z = true
end
end
A better way is to use chunk_while that was introduced in Ruby 2.3:
array = [1, 3, 9, 6, 7, 10, 8, 12, 13, 17]
array
.sort #=> [1, 3, 6, 7, 8, 9, 10, 12, 13, 17]
.chunk_while { |x, y| y == x + 1 } #=> [[1], [3], [6, 7, 8, 9, 10], [12, 13], [17]]
.select { |a| a.size > 1 } #=> [[6, 7, 8, 9, 10], [12, 13]]
.flatten #=> [6, 7, 8, 9, 10, 12, 13]
Did you notice that the documentation contains a similar example?
For example, one-by-one increasing subsequence can be chunked as follows:
a = [1,2,4,9,10,11,12,15,16,19,20,21]
b = a.chunk_while {|i, j| i+1 == j }
p b.to_a #=> [[1, 2], [4], [9, 10, 11, 12], [15, 16], [19, 20, 21]]
This is my code, which works, but it's too big. I want to refactor it.
req_row = -1
req_col = -1
a.each_with_index do |row, index|
row.each_with_index do |col, i|
if col == 0
req_row = index
req_col = i
break
end
end
end
if req_col > -1 and req_row > -1
a.each_with_index do |row,index|
row.each_with_index do |col, i|
print (req_row == index or i == req_col) ? 0 : col
print " "
end
puts "\r"
end
end
Input: 2D Array
1 2 3 4
5 6 7 8
9 10 0 11
12 13 14 15
Required output:
1 2 0 4
5 6 0 8
0 0 0 0
12 13 0 15
I'm surprised the Matrix class is not used more:
a = [[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 0, 11],
[12, 13, 14, 15]]
require 'matrix'
m = Matrix.rows(a)
#=> Matrix[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 0, 11], [12, 13, 14, 15]]
r, c = m.index(0)
#=> [2, 2]
Matrix.build(m.row_count, m.column_count) {|i,j| (i==r || j==c) ? 0 : m[i,j]}.to_a
#=> [[ 1, 2, 0, 4],
# [ 5, 6, 0, 8],
# [ 0, 0, 0, 0],
# [12, 13, 0, 15]]
Note Matrix objects are immutable. To change individual elements you must create a new matrix.
A slight modification is required if you wish to do this for every zero in the matrix:
a = [[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 0, 11],
[ 0, 13, 14, 15]]
require 'set'
m = Matrix.rows(a)
#=> Matrix[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 0, 11], [0, 13, 14, 15]]
zero_rows = Set.new
zero_columns = Set.new
m.each_with_index { |e,i,j| (zero_rows << i; zero_columns << j) if e.zero? }
zero_rows
#=> #<Set: {2, 3}>
zero_columns
#=> #<Set: {2, 0}>
Matrix.build(m.row_count, m.column_count) do |i,j|
(zero_rows.include?(i) || zero_columns.include?(j)) ? 0 : m[i,j]
end.to_a
#=> [[0, 2, 0, 4],
# [0, 6, 0, 8],
# [0, 0, 0, 0],
# [0, 0, 0, 0]]
Try this code:
req_row = req_col = -1
a.each_with_index do |row, index|
req_col = row.index(0) # searching index having value 0.
if req_col
req_row = index
break
end
end
a.each_with_index do |row,index|
row.each_with_index do |col, i|
print ((req_row == index or i == req_col) ? 0 : col).to_s + " "
end
puts "\r"
end
Based on the title of your question, here's solution that searches for positions of the zero values (fixate), then actually zeros out the appropriate row and column (clear, more aligned with the contents of your question):
def fixate matrix, search=0, replace=0
rcs = []
matrix.each_with_index do |row,r|
row.each_with_index do |col,c|
rcs << [ r, c ] if col == search
end
end
rcs.each do |(row, col)|
clear matrix, row, col, replace
end
matrix
end
def clear matrix, row, col, val=0
matrix[row].map! { |_| val } # Clear rows
matrix.each { |r| r[col] = val } # Clear columns
matrix
end
Quick test:
fixate [ # [
[ 1, 2, 3, 4 ], # [ 1, 2, 0, 4 ],
[ 5, 6, 7, 8 ], # [ 5, 6, 0, 8 ],
[ 9, 10, 0, 11 ], # [ 0, 0, 0, 0 ],
[ 12, 13, 14, 15 ] # [ 12, 13, 0, 15 ]
] # ]
Here's what I came up with:
zero_rows=[]
a.map do |col|
col.each_with_index do |el, index|
zero_rows.push(index) if el==0
end
col.fill(0) if col.include?(0)
end
a.map{|col| zero_rows.each{|index| col[index]=0} }
First, use map to iterate through the columns and fill them with zeros if they contain at least one 0. but, while doing so, add the indexes which contain a 0 to the zero_rows array.
Afterwards, iterate through the array once more and set the indexes of each column that match an index in zero_rows to 0.
You may know the map method as collect. They do the same thing.
Side Note:
If an array contains multiple zero's this code will zero out every applicable column. OP's example and some other answers here will only zero out the first column in which a 0 is found. If this is the behavior you are expecting, then see #Doguita's answer.
I don't know if this code is better than the other answers. I will test it later:
ary = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 0, 11], [12, 13, 14, 15]]
col = nil
row = ary.index{ |a| col = a.index(0) }
ary.each_with_index { |a, i| i == row ? a.fill(0) : a[col] = 0 } if col
p ary
# => [[1, 2, 0, 4], [5, 6, 0, 8], [0, 0, 0, 0], [12, 13, 0, 15]]
Obs: This answer assumes there's only one 0 to search in the array
Given a BST, find all sequences of nodes starting from root that will essentially give the same binary search tree.
Given a bst, say
3
/ \
1 5
the answer should be 3,1,5 and 3,5,1.
another example
5
/ \
4 7
/ / \
1 6 10
the outputs will be
5,4,1,7,6,10
5,4,7,6,10,1
5,7,6,10,4,1
etc
The invariant here however is that the parent's index must always be lesser than its children. I am having difficulty implementing it.
I assume you want a list of all sequences which will generate the same BST.
In this answer, we will use Divide and Conquer. We will create a function findAllSequences(Node *ptr) which takes a node pointer as input and returns all the distinct sequences which will generate the subtree hanging from ptr. This function will return a Vector of Vector of int, i.e. vector<vector<int>> containing all the sequences.
The main idea for generating sequence is that root must come before all its children.
Algorithm:
Base Case 1:
If ptr is NULL, then return a vector with an empty sequence.
if (ptr == NULL) {
vector<int> seq;
vector<vector<int> > v;
v.push_back(seq);
return v;
}
Base Case 2:
If ptr is a leaf node, then return a vector with a single sequence. Its Trivial that this sequence will contain only a single element, i.e. value of that node.
if (ptr -> left == NULL && ptr -> right == NULL) {
vector<int> seq;
seq.push_back(ptr -> val);
vector<vector<int> > v;
v.push_back(seq);
return v;
}
Divide Part (this part is very simple.)
We assume that we have a function that can solve this problem, and thus we solve it for left sub tree and right sub tree.
vector<vector<int> > leftSeq = findAllSeq(ptr -> left);
vector<vector<int> > rightSeq = findAllSeq(ptr -> right);
Merging the two solutions.(The crux is in this step.)
Till now we have two set containg distinct sequences:
i. leftSeq - all sequences in this set will generate left subtree.
ii. rightSeq - all sequences in this set will generate right subtree.
Now each sequence in left subtree can be merged with each sequence of right subtree. While merging we should be careful that the relative order of elements is preserved. Also in each of the merged sequence we will add the value of current node in the beginning beacuse root must come before all children.
Pseudocode for Merge
vector<vector<int> > results
for all sequences L in leftSeq
for all sequences R in rightSeq
create a vector flags with l.size() 0's and R.size() 1's
for all permutations of flag
generate the corresponding merged sequence.
append the current node's value in beginning
add this sequence to the results.
return results.
Explanation: Let us take a sequence, say L(of size n) from the set leftSeq, and a sequence, say R(of size m) from set rightSeq.
Now these two sequences can be merged in m+nCn ways!
Proof: After merging, the new sequence will have m + n elements. As we have to maintain the relative order of elements, so firstly we will fill all n the elements from L in any of n places among total (m+n) places. After that remaining m places can be filled by elements of R. Thus we have to choose n places from (m+n) places.
To do this, lets create take a Boolean vector, say flags and fill it with n 0's and m 1's.A value of 0 represents a member from left sequence and a value of 1 represents member from right sequence. All what is left is to generate all permutations of this flags vector, which can be done with next_permutation. Now for each permutation of flags we will have a distinct merged sequence of L and R.
eg: Say L={1, 2, 3} R={4, 5}
so, n=3 and m=2
thus, we can have 3+2C3 merged sequences, i.e. 10.
1.now, Initially flags = {0 0 0 1 1}, filled with 3 0's and 2 1's
this will result into this merged sequence: 1 2 3 4 5
2.after calling nextPermutation we will have
flags = {0 0 1 0 1}
and this will generate sequence: 1 2 4 3 5
3.again after calling nextPermutation we will have
flags = {0 0 1 1 0}
ans this will generate sequence: 1 2 4 5 3and so on...
Code in C++
vector<vector<int> > findAllSeq(TreeNode *ptr)
{
if (ptr == NULL) {
vector<int> seq;
vector<vector<int> > v;
v.push_back(seq);
return v;
}
if (ptr -> left == NULL && ptr -> right == NULL) {
vector<int> seq;
seq.push_back(ptr -> val);
vector<vector<int> > v;
v.push_back(seq);
return v;
}
vector<vector<int> > results, left, right;
left = findAllSeq(ptr -> left);
right = findAllSeq(ptr -> right);
int size = left[0].size() + right[0].size() + 1;
vector<bool> flags(left[0].size(), 0);
for (int k = 0; k < right[0].size(); k++)
flags.push_back(1);
for (int i = 0; i < left.size(); i++) {
for (int j = 0; j < right.size(); j++) {
do {
vector<int> tmp(size);
tmp[0] = ptr -> val;
int l = 0, r = 0;
for (int k = 0; k < flags.size(); k++) {
tmp[k+1] = (flags[k]) ? right[j][r++] : left[i][l++];
}
results.push_back(tmp);
} while (next_permutation(flags.begin(), flags.end()));
}
}
return results;
}
Update 3rd March 2017: This solution wont work perfectly if original tree contains duplicates.
Here is a clear, concise and well-documented solution that I wrote for you in Python 3. I hope it helps you!
Code: bst_sequences.py
from binarytree import bst, Node
def weave_lists(first: list, second: list, results: list, prefix: list) -> None:
"""Recursively Weave the first list into the second list and append
it to the results list. The prefix list grows by an element with the
depth of the call stack. Ultimately, either the first or second list will
be exhausted and the base case will append a result."""
# base case
if not first or not second:
results.append(prefix + first + second)
return
# recursive case
first_head, first_tail = first[0], first[1:]
weave_lists(first_tail, second, results, prefix + [first_head])
second_head, second_tail = second[0], second[1:]
weave_lists(first, second_tail, results, prefix + [second_head])
def all_sequences(root: Node) -> list:
"""Splits the tree into three lists: prefix, left, and right."""
if root is None:
return []
answer = []
prefix = [root.value]
left = all_sequences(root.left) or [[]]
right = all_sequences(root.right) or [[]]
# At a minimum, left and right must be a list containing an empty list
# for the following nested loop
for i in range(len(left)):
for j in range(len(right)):
weaved = []
weave_lists(left[i], right[j], weaved, prefix)
answer.extend(weaved)
return answer
if __name__ == "__main__":
t = bst(2)
print(t)
solution = all_sequences(t)
for e, item in enumerate(solution):
print(f"{e:03}: {item}")
Sample Output
__4
/ \
1 5
/ \ \
0 2 6
000: [4, 1, 0, 2, 5, 6]
001: [4, 1, 0, 5, 2, 6]
002: [4, 1, 0, 5, 6, 2]
003: [4, 1, 5, 0, 2, 6]
004: [4, 1, 5, 0, 6, 2]
005: [4, 1, 5, 6, 0, 2]
006: [4, 5, 1, 0, 2, 6]
007: [4, 5, 1, 0, 6, 2]
008: [4, 5, 1, 6, 0, 2]
009: [4, 5, 6, 1, 0, 2]
010: [4, 1, 2, 0, 5, 6]
011: [4, 1, 2, 5, 0, 6]
012: [4, 1, 2, 5, 6, 0]
013: [4, 1, 5, 2, 0, 6]
014: [4, 1, 5, 2, 6, 0]
015: [4, 1, 5, 6, 2, 0]
016: [4, 5, 1, 2, 0, 6]
017: [4, 5, 1, 2, 6, 0]
018: [4, 5, 1, 6, 2, 0]
019: [4, 5, 6, 1, 2, 0]
Process finished with exit code 0
I have a much shorter solution. What do you think about it?
function printSequences(root){
let combinations = [];
function helper(node, comb, others){
comb.push(node.values);
if(node.left) others.push(node.left);
if(node.right) others.push(node.right);
if(others.length === 0){
combinations.push(comb);
return;
}else{
for(let i = 0; i<others.length; i++){
helper(others[i], comb.slice(0), others.slice(0, i).concat(others.slice(i+1, others.length)));
}
}
}
helper(root, [], []);
return combinations;
}
Note that the question is actually about topological sorting of a tree: find all the possible ways to perform topological sort. That is, we don't care about the specific way the tree was built, what's important is that elements are always added as leaves, never changing the structure of existing nodes. The constraint on the output is that nodes never precede their ancestors - treating the tree as a classic dependency graph.
But unlike topological sort for a general DAG, there's no need for reference counting here, since this is a tree - the number of references is always 1 or 0.
Here's a simple Python implementation:
def all_toposorts_tree(sources, history):
if not sources:
print(history)
return
for t in sources:
all_toposorts((sources - {t}) | {t.left, t.right} - {None}, history + [t.v])
all_toposorts_tree({root}, [])
This is question 4.9 in Cracking the Coding Interview, 6th Edition.
well here is my python code which does producing all sequences of elements/numbers for same BST.
for the logic i referred to the book cracking the coding interview by Gayle Laakmann Mcdowell
from binarytree import Node, bst, pprint
def wavelist_list(first, second, wave, prefix):
if first:
fl = len(first)
else:
fl = 0
if second:
sl = len(second)
else:
sl = 0
if fl == 0 or sl == 0:
tmp = list()
tmp.extend(prefix)
if first:
tmp.extend(first)
if second:
tmp.extend(second)
wave.append(tmp)
return
if fl:
fitem = first.pop(0)
prefix.append(fitem)
wavelist_list(first, second, wave, prefix)
prefix.pop()
first.insert(0, fitem)
if sl:
fitem = second.pop(0)
prefix.append(fitem)
wavelist_list(first, second, wave, prefix)
prefix.pop()
second.insert(0, fitem)
def allsequences(root):
result = list()
if root == None:
return result
prefix = list()
prefix.append(root.value)
leftseq = allsequences(root.left)
rightseq = allsequences(root.right)
lseq = len(leftseq)
rseq = len(rightseq)
if lseq and rseq:
for i in range(lseq):
for j in range(rseq):
wave = list()
wavelist_list(leftseq[i], rightseq[j], wave, prefix)
for k in range(len(wave)):
result.append(wave[k])
elif lseq:
for i in range(lseq):
wave = list()
wavelist_list(leftseq[i], None, wave, prefix)
for k in range(len(wave)):
result.append(wave[k])
elif rseq:
for j in range(rseq):
wave = list()
wavelist_list(None, rightseq[j], wave, prefix)
for k in range(len(wave)):
result.append(wave[k])
else:
result.append(prefix)
return result
if __name__=="__main__":
n = int(input("what is height of tree?"))
my_bst = bst(n)
pprint(my_bst)
seq = allsequences(my_bst)
print("All sequences")
for i in range(len(seq)):
print("set %d = " %(i+1), end="")
print(seq[i])
example output:
what is height of tree?3
___12
/ \
__ 6 13
/ \ \
0 11 14
\
2
All sequences
set 1 = [12, 6, 0, 2, 11, 13, 14]
set 2 = [12, 6, 0, 2, 13, 11, 14]
set 3 = [12, 6, 0, 2, 13, 14, 11]
set 4 = [12, 6, 0, 13, 2, 11, 14]
set 5 = [12, 6, 0, 13, 2, 14, 11]
set 6 = [12, 6, 0, 13, 14, 2, 11]
set 7 = [12, 6, 13, 0, 2, 11, 14]
set 8 = [12, 6, 13, 0, 2, 14, 11]
set 9 = [12, 6, 13, 0, 14, 2, 11]
set 10 = [12, 6, 13, 14, 0, 2, 11]
set 11 = [12, 13, 6, 0, 2, 11, 14]
set 12 = [12, 13, 6, 0, 2, 14, 11]
set 13 = [12, 13, 6, 0, 14, 2, 11]
set 14 = [12, 13, 6, 14, 0, 2, 11]
set 15 = [12, 13, 14, 6, 0, 2, 11]
set 16 = [12, 6, 0, 11, 2, 13, 14]
set 17 = [12, 6, 0, 11, 13, 2, 14]
set 18 = [12, 6, 0, 11, 13, 14, 2]
set 19 = [12, 6, 0, 13, 11, 2, 14]
set 20 = [12, 6, 0, 13, 11, 14, 2]
set 21 = [12, 6, 0, 13, 14, 11, 2]
set 22 = [12, 6, 13, 0, 11, 2, 14]
set 23 = [12, 6, 13, 0, 11, 14, 2]
set 24 = [12, 6, 13, 0, 14, 11, 2]
set 25 = [12, 6, 13, 14, 0, 11, 2]
set 26 = [12, 13, 6, 0, 11, 2, 14]
set 27 = [12, 13, 6, 0, 11, 14, 2]
set 28 = [12, 13, 6, 0, 14, 11, 2]
set 29 = [12, 13, 6, 14, 0, 11, 2]
set 30 = [12, 13, 14, 6, 0, 11, 2]
set 31 = [12, 6, 11, 0, 2, 13, 14]
set 32 = [12, 6, 11, 0, 13, 2, 14]
set 33 = [12, 6, 11, 0, 13, 14, 2]
set 34 = [12, 6, 11, 13, 0, 2, 14]
set 35 = [12, 6, 11, 13, 0, 14, 2]
set 36 = [12, 6, 11, 13, 14, 0, 2]
set 37 = [12, 6, 13, 11, 0, 2, 14]
set 38 = [12, 6, 13, 11, 0, 14, 2]
set 39 = [12, 6, 13, 11, 14, 0, 2]
set 40 = [12, 6, 13, 14, 11, 0, 2]
set 41 = [12, 13, 6, 11, 0, 2, 14]
set 42 = [12, 13, 6, 11, 0, 14, 2]
set 43 = [12, 13, 6, 11, 14, 0, 2]
set 44 = [12, 13, 6, 14, 11, 0, 2]
set 45 = [12, 13, 14, 6, 11, 0, 2]
here is another concise recursion based easy to understand solution:
from binarytree import Node, bst, pprint
def allsequences1(root):
if not root:
return None
lt = allsequences1(root.left)
rt = allsequences1(root.right)
ret = []
if not lt and not rt:
ret.append([root])
elif not rt:
for one in lt:
ret.append([root]+one)
elif not lt:
for two in rt:
ret.append([root]+two)
else:
for one in lt:
for two in rt:
ret.append([root]+one+two)
ret.append([root]+two+one)
return ret
if __name__=="__main__":
n = int(input("what is height of tree?"))
my_bst = bst(n)
pprint(my_bst)
seg = allsequences1(my_bst)
print("All sequences ..1")
for i in range(len(seq)):
print("set %d = " %(i+1), end="")
print(seq[i])
Let's first observe what must be be followed to create the same BST. The only sufficient rules here is insert parent before their left and right children. Because, if we can guarantee that for some node (that we are interested to insert) all parents (including grand parent) are inserted but none of it's children are inserted, than the node will find its appropriate place to be inserted.
Following this observation we can write backtrack to generate all sequence that will produce same BST.
active_list = {root}
current_order = {}
result ={{}}
backtrack():
if(len(current_order) == total_node):
result.push(current_order)
return;
for(node in active_list):
current_order.push(node.value)
if node.left :
active_list.push(node.left)
if node.right:
active_list.push(node.right)
active_list.remove(node)
backtrack()
active_list.push(node)
if node.left :
active_list.remove(node.left)
if node.right:
active_list.remove(node.right)
current_order.remove(node.val)
This is not working implementation. used just for illustration purpose.
public class Solution {
ArrayList<LinkedList<Long>> result;
/*Return the children of a node */
ArrayList<TreeNode> getChilden(TreeNode parent) {
ArrayList<TreeNode> child = new ArrayList<TreeNode>();
if(parent.left != null) child.add(parent.left);
if(parent.right != null) child.add(parent.right);
return child;
}
/*Gets all the possible Compinations*/
void getPermutations(ArrayList<TreeNode> permutations, LinkedList<Long> current) {
if(permutations.size() == 0) {
result.add(current);
return;
}
int length = permutations.size();
for(int i = 0; i < length; i++) {
TreeNode node = permutations.get(i);
permutations.remove(i);
ArrayList<TreeNode> newPossibilities = new ArrayList<TreeNode>();
newPossibilities.addAll(permutations);
newPossibilities.addAll(getChilden(node));
LinkedList<Long> newCur = new LinkedList<Long>();
newCur.addAll(current);
newCur.add(node.val);
getPermutations(newPossibilities, newCur);
permutations.add(i,node);
}
}
/*This method returns a array of arrays which will lead to a given BST*/
ArrayList<LinkedList<Long>> inputSequencesForBst(TreeNode node) {
result = new ArrayList<LinkedList<Long>>();
if(node == null)
return result;
ArrayList<TreeNode> permutations = getChilden(node);
LinkedList<Long> current = new LinkedList<Long>();
current.add(node.val);
getPermutations(permutations, current);
return result;
}
}
My solution. Works perfectly.
Here's my Python solution with plenty of explanation.
We build each array from left to right by choosing for every position one node out of a set of possible choices for that position. We add the node value to the path, and the children of the node (if any) to the list of possibilities, then recurse further. When there are no further choices we have one candidate array. To generate the rest of the the arrays, we backtrack until we can make a different choice and recurse again.
The catch is to use a suitable data structure for holding the possibilities. A list works, but the node has to be put back in the previous position while backtracking (order matters, since we have added the children of the node which must be visited AFTER the node). Insertion and deletion from a list takes linear time. A set doesn't work since it doesn't maintain order. A dict works best since Python dictionary remembers the insertion order and all operations run in constant time.
def bst_seq(root: TreeNode) -> list[list[int]]:
def _loop(choices: MutableMapping[TreeNode, bool], path: list[int], result: list[list[int]]) -> None:
if not choices:
result.append([*path])
else:
# Take a snapshot of the keys to avoid concurrent modification exception
for choice in list(choices.keys()):
del choices[choice]
children = list(filter(None, [choice.left, choice.right]))
for child in children:
choices[child] = False
path.append(choice.val)
_loop(choices, path, result)
path.pop()
choices[choice] = False
for child in children:
del choices[child]
result = []
_loop({root: False}, [], result)
return result