并查集还能这么玩。
题目
最近做了一道力扣题,leetcode-cn.com/problems/pa…
思路
leetcode-cn.com/problems/pa… 参考大佬的思路。
- 把格子中的每个顶点抽象成图中的一个顶点。
- 把两个相邻的格子之间连一条边,边的长度是两个格子数值差的绝对值
- 找一条从左上角到右下角的最短路径,其中路径的长度定义为路径上所有边的权值的最大值
方法一、二分
对最短路径长度进行二分。当我们二分枚举到长度x的时候,我们只保留所有长度小于等于x的边。然后从左上角开始进行搜索,BFS和DFS均可,只需要判断最终有没有路径到达右下角即可。如果能够到达,那么我们可以减小x,反之则增大。php代码如下:
class Solution {
/**
* @param Integer[][] $heights
* @return Integer
*/
function minimumEffortPath($heights) {
$row = count($heights);
$col = count($heights[0]);
$left = 0;
$right = 999999;
$res = 0;
$directions = [[0, 1], [0, -1], [1, 0], [-1, 0]];
while ($left < $right) {
$mid = $left + (int)(($right - $left) / 2);
$visited = array_fill(0, $row, array_fill(0, $col, 0));
$queue = new SplQueue();
$queue->enqueue([0, 0]);
$visited[0][0] = 1;
while (!$queue->isEmpty()) {
$top = $queue->dequeue();
$x = $top[0];
$y = $top[1];
foreach ($directions as $direction) {
$newX = $x + $direction[0];
$newY = $y + $direction[1];
if ($newX >= 0 && $newX < $row &&
$newY >= 0 && $newY < $col &&
$visited[$newX][$newY] == 0 &&
abs($heights[$newX][$newY] - $heights[$x][$y]) <= $mid
) {
$queue->enqueue([$newX, $newY]);
$visited[$newX][$newY] = 1;
}
}
}
if ($visited[$row - 1][$col - 1] == 1) {
$right = $mid;
// $res = $mid;
} else {
$left = $mid + 1;
}
}
return $left;
}
}
方法二、并查集
没想到这玩意还能用并查集做。
把所有的边按照长度大小从小到大依次添加到并查集,直到左上角和右下角联通即可。
class Solution {
/**
* @param Integer[][] $heights
* @return Integer
*/
function minimumEffortPath($heights)
{
$row = count($heights);
$col = count($heights[0]);
$edges = [];
for ($i = 0; $i < $row; $i++) {
for ($j = 0; $j < $col; $j++) {
$id = $i * $col + $j;
if ($i > 0) {
$edges[] = [$id - $col, $id, abs($heights[$i][$j] - $heights[$i-1][$j])];
}
if ($j > 0) {
$edges[] = [$id - 1, $id, abs($heights[$i][$j] - $heights[$i][$j-1])];
}
}
}
//按照边的长短升序
usort($edges, function ($a, $b) {
if ($a[2] > $b[2]) {
return 1;
} else {
return -1;
}
});
$uf = new UnionFind($row * $col);
foreach ($edges as $edge) {
$uf->union($edge[0], $edge[1]);
if ($uf->connection(0, $row * $col - 1)) {
return $edge[2];
}
}
return 0;
}
function minimumEffortPathAC($heights) {
$row = count($heights);
$col = count($heights[0]);
$left = 0;
$right = 999999;
$directions = [[0, 1], [0, -1], [1, 0], [-1, 0]];
while ($left < $right) {
$mid = $left + (int)(($right - $left) / 2);
$visited = array_fill(0, $row, array_fill(0, $col, 0));
$queue = new SplQueue();
$queue->enqueue([0, 0]);
$visited[0][0] = 1;
while (!$queue->isEmpty()) {
$top = $queue->dequeue();
$x = $top[0];
$y = $top[1];
foreach ($directions as $direction) {
$newX = $x + $direction[0];
$newY = $y + $direction[1];
if ($newX >= 0 && $newX < $row &&
$newY >= 0 && $newY < $col &&
$visited[$newX][$newY] == 0 &&
abs($heights[$newX][$newY] - $heights[$x][$y]) <= $mid
) {
$queue->enqueue([$newX, $newY]);
$visited[$newX][$newY] = 1;
}
}
}
if ($visited[$row - 1][$col - 1] == 1) {
$right = $mid;
// $res = $mid;
} else {
$left = $mid + 1;
}
}
return $left;
}
}
class UnionFind
{
public $parents;
public $count;
public function __construct($n)
{
for ($i = 0; $i < $n; $i++) {
$this->parents[$i] = $i;
$this->count++;
}
}
public function find($x)
{
while ($x != $this->parents[$x]) {
$this->parents[$x] = $this->parents[$this->parents[$x]];
$x = $this->parents[$x];
}
return $x;
}
public function union($x, $y)
{
$rootX = $this->find($x);
$rootY = $this->find($y);
if ($rootY == $rootX) {
return;
}
$this->parents[$rootX] = $rootY;
$this->count--;
return;
}
public function connection($x, $y)
{
$rootX = $this->find($x);
$rootY = $this->find($y);
if ($rootY == $rootX) {
return true;
}
return false;
}
}