给你一个 m x n
的字符矩阵 box
,它表示一个箱子的侧视图。箱子的每一个格子可能为:
'#'
表示石头
'*'
表示固定的障碍物
'.'
表示空位置
这个箱子被 顺时针旋转 90 度 ,由于重力原因,部分石头的位置会发生改变。每个石头会垂直掉落,直到它遇到障碍物,另一个石头或者箱子的底部。重力
不会 影响障碍物的位置,同时箱子旋转不会产生 惯性 ,也就是说石头的水平位置不会发生改变。
题目保证初始时 box
中的石头要么在一个障碍物上,要么在另一个石头上,要么在箱子的底部。
请你返回一个 __n x m
的矩阵,表示按照上述旋转后,箱子内的结果。
示例 1:
**输入:** box = [["#",".","#"]]
**输出:** [["."],
["#"],
["#"]]
示例 2:
**输入:** box = [["#",".","*","."],
["#","#","*","."]]
**输出:** [["#","."],
["#","#"],
["*","*"],
[".","."]]
示例 3:
**输入:** box = [["#","#","*",".","*","."],
["#","#","#","*",".","."],
["#","#","#",".","#","."]]
**输出:** [[".","#","#"],
[".","#","#"],
["#","#","*"],
["#","*","."],
["#",".","*"],
["#",".","."]]
提示:
m == box.length
n == box[i].length
1 <= m, n <= 500
box[i][j]
只可能是 '#'
,'*'
或者 '.'
。
方法一:用队列维护空位
提示 1
当我们将盒子顺时针旋转之后,原先的「每一行」就变成了「每一列」。
由于石头受到重力只会竖直向下掉落,因此「每一列」之间都互不影响,我们可以依次计算「每一列」的结果,即原先的「每一行」的结果。
思路与算法
由于重力向下,那么我们应当从右向左遍历原先的「每一行」。
我们使用一个队列来存放一行中的空位:
在遍历完所有的行后,我们将矩阵顺时针旋转 90 度,放入答案数组中即可。
代码
[sol1-C++]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
| class Solution { public: vector<vector<char>> rotateTheBox(vector<vector<char>>& box) { int m = box.size(); int n = box[0].size();
for (int i = 0; i < m; ++i) { deque<int> q; for (int j = n - 1; j >= 0; --j) { if (box[i][j] == '*') { q.clear(); } else if (box[i][j] == '#') { if (!q.empty()) { int pos = q.front(); q.pop_front(); box[i][pos] = '#'; box[i][j] = '.'; q.push_back(j); } } else { q.push_back(j); } } }
vector<vector<char>> ans(n, vector<char>(m)); for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { ans[j][m - i - 1] = box[i][j]; } } return ans; } };
|
[sol1-Python3]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
| class Solution: def rotateTheBox(self, box: List[List[str]]) -> List[List[str]]: m, n = len(box), len(box[0])
for i in range(m): q = deque() for j in range(n - 1, -1, -1): if box[i][j] == "*": q.clear() elif box[i][j] == "#": if q: pos = q.popleft() box[i][pos] = "#" box[i][j] = "." q.append(j) else: q.append(j)
ans = [[""] * m for _ in range(n)] for i in range(m): for j in range(n): ans[j][m - i - 1] = box[i][j] return ans
|
复杂度分析
方法二:用指针维护空位
提示 1
在遍历完某一个位置之后,如果队列不为空,那么:
提示 1 解释
如果队列不为空,那么该位置一定是空位(要么原本就是空位,要么原本有一块石头下落,该位置变成了空位),因此该位置会被加入队列成为队尾。
如果队列中的位置不连续,假设队列中没有位置 x,但有小于 x 和大于 x 的位置,当我们在此之前遍历到位置 x 时,x 没有被放入队列,说明 x 不是空位,并且那时的队列为空,这样队列中就不可能有大于 x 的位置了,这就产生了矛盾。
思路与算法
根据提示 1,我们就无需显式地维护这个队列了。
如果队列不为空,那么队尾一定为当前位置,且队列中的位置连续。因此我们只需要维护队首对应的位置即可。
代码
[sol2-C++]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
| class Solution { public: vector<vector<char>> rotateTheBox(vector<vector<char>>& box) { int m = box.size(); int n = box[0].size();
for (int i = 0; i < m; ++i) { int front_pos = n - 1; for (int j = n - 1; j >= 0; --j) { if (box[i][j] == '*') { front_pos = j - 1; } else if (box[i][j] == '#') { if (front_pos > j) { box[i][front_pos] = '#'; box[i][j] = '.'; --front_pos; } else { front_pos = j - 1; } } } }
vector<vector<char>> ans(n, vector<char>(m)); for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { ans[j][m - i - 1] = box[i][j]; } } return ans; } };
|
[sol2-Python3]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
| class Solution: def rotateTheBox(self, box: List[List[str]]) -> List[List[str]]: m, n = len(box), len(box[0])
for i in range(m): front_pos = n - 1 for j in range(n - 1, -1, -1): if box[i][j] == "*": front_pos = j - 1 elif box[i][j] == "#": if front_pos > j: box[i][front_pos] = "#" box[i][j] = "." front_pos -= 1 else: front_pos = j - 1
ans = [[""] * m for _ in range(n)] for i in range(m): for j in range(n): ans[j][m - i - 1] = box[i][j] return ans
|
复杂度分析