一维接雨水算法题实现
https://leetcode-cn.com/problems/trapping-rain-water/
def trap_rain_water(height):
# 思路: 一个格子能不能接到雨水,能接到多少雨水,由其两边最高的"墙"决定
# 从第二个位置遍历每个格子,遍历到倒数第二个,找到每个格子两边最高的墙
# 格子高于等于墙高则无法接住雨水,如果比墙低,则能接住较低的墙减去格子高度的雨水
# 将每个格子接到的雨水加起来,遍历完成后即为总的雨水量
if not height:
return 0
rain_water_count = 0
left_max = height[0]
right_max = max(height[1:])
for index, high in enumerate(height[1: -1], 1):
if left_max < high:
left_max = high
if high == right_max:
right_max = max(height[index + 1:])
continue
if high == right_max:
right_max = max(height[index + 1:])
continue
if left_max > high and right_max > high:
lower_wall = left_max if left_max < right_max else right_max
rain_water_count += lower_wall - high
return rain_water_count
if __name__ == '__main__':
ret = trap_rain_water2([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1])
print(ret)
ret = trap_rain_water([4, 2, 0, 3, 2, 5])
print(ret)
二维接雨水算法实现
https://leetcode-cn.com/problems/trapping-rain-water-ii/submissions/
思路1,正向思路
- 最外圈格子一定接不到雨水,可将最外圈看着围墙,所有从内圈开始遍历每个格子
- 遍历到格子A时,查看其四个方向上的高度是否高于A的高度,如果第一个方向高于A,则这个方向遍历完并将高度记录下来
- 如果第二个方向比A低或高度相等,则判定第二个方向是否为边界,如果是边界则格子A定不能接到水,将格子A标记为核对完成
- 如果第二个方向比A低或高度相等,且不为边界,则从第二个方向的格子B想剩余的三个方向比较(因其中的一个方向是A,不能倒回去比较)
- 重复上诉两部,直到遍历到能确认格子A能不能接到水
- A不能接到水的,则A遍历过的足迹上,所有高于等于A的格子一定不能接到水,将之全部标记为核对完成
- A能接到水的,计算A接到的水量,A遍历过的足迹上所有第二等于A的都能接到水并计算水量
- 遍历完成所有内圈格子后,核对有没有格子接到水,如果没有则遍历完成,如果有则继续第二次遍历,后续的遍历可直接跳过核对完成的格子
class Solution(object):
def createFullStateMap(self, heightMap):
full_state_map = []
for row in heightMap:
full_state_map.append([False] * len(row))
first_row = full_state_map[0]
for i, _ in enumerate(first_row):
first_row[i] = True
last_row = full_state_map[-1]
for i, _ in enumerate(last_row):
last_row[i] = True
for row in full_state_map[1: -1]:
row[0] = row[-1] = True
self.full_state_map = full_state_map
def createWaterWalkMap(self):
self.water_walk_map = set()
def _clean_water_walk_map(self):
self.water_walk_map.clear()
def _get_four_direction_coordinates(self, i, j):
return [
(i - 1, j),
(i, j + 1),
(i + 1, j),
(i, j - 1)
]
def _set_full_state(self):
for row, col in self.water_walk_map:
if self.height_map[row][col] >= self.cur_high:
self.full_state_map[row][col] = True
def _fill_water(self):
for row, col in self.water_walk_map:
coord_high = self.height_map[row][col]
if coord_high <= self.lower_wall:
self.collected_walter += self.lower_wall - coord_high
self.height_map[row][col] = self.lower_wall
def _fill_lower_than_bord(self):
for row in range(1, self.rows_cnt - 1):
for col in range(1, self.cols_cnt - 1):
cur_high = self.height_map[row][col]
if cur_high < self.min_bord:
self.collected_walter += self.min_bord - cur_high
self.height_map[row][col] = self.min_bord
def _is_on_bord(self, i, j):
if i == 0 or i == self.rows_cnt - 1 or j == 0 or j == self.cols_cnt - 1:
return True
return False
def _real_walk(self, i, j):
if (i, j) in self.water_walk_map:
return
self.water_walk_map.add((i, j))
coord_high = self.height_map[i][j]
if coord_high > self.cur_high:
self.lower_wall = coord_high if not self.lower_wall else min(self.lower_wall, coord_high)
return
on_bord = self._is_on_bord(i, j)
if on_bord:
self.can_fill_water = False
return
four_direction_coordinates = self._get_four_direction_coordinates(i, j)
for coord in four_direction_coordinates:
self._real_walk(*coord)
def _exec_walk(self, i, j):
self.water_walk_map.add((i, j))
four_direction_coordinates = self._get_four_direction_coordinates(i, j)
for coord in four_direction_coordinates:
self._real_walk(*coord)
if not self.can_fill_water:
self._set_full_state()
return
self._fill_water()
def _get_min_bord(self):
min_bord = min(self.height_map[0] + self.height_map[-1])
for row in self.height_map[1: -1]:
min_bord = min(min_bord, row[0], row[-1])
return min_bord
def _get_max_high(self):
max_high = -1
for row in self.height_map:
max_high = max(row + [max_high])
return max_high
def _water_walk(self):
walter_full = True
self.min_bord = self._get_min_bord()
self.max_high = self._get_max_high()
self._fill_lower_than_bord()
if self.min_bord == self.max_high:
return walter_full
for i in range(1, self.rows_cnt):
for j in range(1, self.cols_cnt):
if self.full_state_map[i][j]:
continue
self.cur_high = self.height_map[i][j]
self.lower_wall = None
self.can_fill_water = True
self._clean_water_walk_map()
self._exec_walk(i, j)
if not self.can_fill_water:
continue
walter_full = False
return walter_full
def trapRainWater(self, heightMap):
"""
:type heightMap: List[List[int]]
:rtype: int
"""
self.collected_walter = 0
self.rows_cnt = len(heightMap)
self.cols_cnt = len(heightMap[0])
self.height_map = heightMap
if self.rows_cnt <= 2 or self.cols_cnt <= 2:
return self.collected_walter
self.createFullStateMap(heightMap)
self.createWaterWalkMap()
while True:
walter_full = self._water_walk()
if walter_full:
return self.collected_walter
def test_func(self):
# func = self.trapRainWater
func = foo
if callable(func):
print(self.__class__.__name__, func.__name__)
def foo():
print('hello')
if __name__ == "__main__":
solution = Solution()
lst = [
[1, 4, 3, 1, 3, 2],
[3, 2, 1, 3, 2, 4],
[2, 3, 3, 2, 3, 1]
]
# print(solution.trapRainWater(lst))
solution.test_func()
思路2,逆向思路
- 最外圈格子一定接不到雨水,将最外圈格子的高度和坐标作为一个组合放到一个数据结构C中
- 获取到C中高度最小的格子的坐标A,并从C中去掉A,因格子A在最外层,挨着他的比他矮的格子一定能接到水,且接到的水A的高度减去这个格子的高度,接完后将比较矮的格子的高度填充为A的高度
- 挨着A的比A高的格子一定接不到水
- 将挨着A的所有格子计算完毕,并将这些格子当前的高度及坐标填入到数据结构C中,且将这些格子标记为已核对
- 再次重C中拿出当前高度最小的格子A,找其周围的格子进行对比,如果某个格子已经核对过,则不再核对该格子
- 如果未核对过,则重复前面的比较过程并将格子的数据放到C中
- 一直循环到C中没有数据则遍历完成
from heapq import heappush, heappop
class Solution(object):
def createWall(self):
for i in range(self.cols_cnt):
last_row = self.rows_cnt - 1
heappush(self.heap, (self.height_map[0][i], (0, i)))
heappush(self.heap, (self.height_map[last_row][i], (last_row, i)))
self.visited.add((0, i))
self.visited.add((last_row, i))
for i in range(self.rows_cnt):
last_col = self.cols_cnt - 1
heappush(self.heap, (self.height_map[i][0], (i, 0)))
heappush(self.heap, (self.height_map[i][last_col], (i, last_col)))
self.visited.add((i, 0))
self.visited.add((i, last_col))
def _get_four_direction_coordinates(self, i, j):
return [
(i - 1, j),
(i, j + 1),
(i + 1, j),
(i, j - 1)
]
def _is_out_bord_or_visited(self, i, j):
if i <= 0 or i >= self.rows_cnt - 1 or j <= 0 or j >= self.cols_cnt - 1 or (i, j) in self.visited:
return True
return False
def _calc(self):
high, item = heappop(self.heap)
self.current_lower_wall = max(self.current_lower_wall, high)
for row, col in self._get_four_direction_coordinates(*item):
if self._is_out_bord_or_visited(row, col):
continue
cur_high = self.height_map[row][col]
if cur_high < self.current_lower_wall:
self.collected_walter += self.current_lower_wall - cur_high
self.height_map[row][col] = self.current_lower_wall
self.visited.add((row, col))
heappush(self.heap, (self.height_map[row][col], (row, col)))
def _get_min_bord(self):
min_bord = min(self.height_map[0] + self.height_map[-1])
for row in self.height_map[1: -1]:
min_bord = min(min_bord, row[0], row[-1])
return min_bord
def _get_max_high(self):
max_high = -1
for row in self.height_map:
max_high = max(row + [max_high])
return max_high
def _fill_lower_than_bord(self):
for row in range(1, self.rows_cnt - 1):
for col in range(1, self.cols_cnt - 1):
cur_high = self.height_map[row][col]
if cur_high < self.min_bord:
self.collected_walter += self.min_bord - cur_high
self.height_map[row][col] = self.min_bord
def _check_finish(self):
self.min_bord = self._get_min_bord()
self.max_high = self._get_max_high()
self._fill_lower_than_bord()
if self.min_bord == self.max_high:
return True
return False
def trapRainWater(self, heightMap):
"""
:type heightMap: List[List[int]]
:rtype: int
"""
self.collected_walter = 0
self.rows_cnt = len(heightMap)
self.cols_cnt = len(heightMap[0])
self.height_map = heightMap
self.heap = []
self.visited = set()
self.current_lower_wall = -1
if self.rows_cnt <= 2 or self.cols_cnt <= 2:
return self.collected_walter
if self._check_finish():
return self.collected_walter
self.createWall()
while self.heap:
self._calc()
return self.collected_walter
if __name__ == "__main__":
solution = Solution()
lst = [
[1, 4, 3, 1, 3, 2],
[3, 2, 1, 3, 2, 4],
[2, 3, 3, 2, 3, 1]
]
print(solution.trapRainWater(lst))