使用Scala中的不可变值查找路径是不是存在于图形中
Posted
技术标签:
【中文标题】使用Scala中的不可变值查找路径是不是存在于图形中【英文标题】:Find if Path Exists in Graph using immutable values in Scala使用Scala中的不可变值查找路径是否存在于图形中 【发布时间】:2021-10-27 20:04:19 【问题描述】:如何使用 immutables 而不是 var 编写以下代码?
问题:
有一个具有 n 个顶点的双向图,其中每个顶点标记为从 0 到 n - 1(包括)。图中的边表示为一个二维整数数组边,其中每个边[i] = [ui, vi] 表示顶点 ui 和顶点 vi 之间的双向边。每个顶点对最多由一条边连接,并且没有一个顶点与自己有一条边。
您想确定是否存在从顶点开始到顶点结束的有效路径。
给定边和整数 n、start 和 end,如果从 start 到 end 存在有效路径,则返回 true,否则返回 false。
示例 1:
输入:n = 3,边 = [[0,1],[1,2],[2,0]],开始 = 0,结束 = 2 输出:真 解释:从顶点0到顶点2有两条路径:
0 → 1 → 2 0 → 2 列表项解决方案
package com.example
object Solution
var visited: Seq[Int] = Nil
def validPath(n: Int, edges: Array[Array[Int]], start: Int, end: Int) : Boolean =
if(edges.length == 0)
return true
val finalMap = edges.foldLeft(Map.empty[Int, Seq[Int]]) case(result, edge) =>
val keyVal = result.getOrElse(edge(0) , Nil) :+ edge(1)
val updatedMap = (result + (edge(0)-> keyVal ))
val keyVal1 = updatedMap.getOrElse(edge(1) , Nil) :+ edge(0)
(updatedMap + (edge(1)-> keyVal1 ))
helper(finalMap , end, start)
def helper(map:Map[Int, Seq[Int]], end: Int, start: Int): Boolean =
println(visited)
if(visited.contains(start))
false
else
val resultList = map.get(start)
resultList match
case Some(l) => if (l.contains(end))
true
else
l.foldLeft(false) (a , b) =>
visited = (visited :+ start).distinct
a || helper(map, end, b)
case None => false
o
def main(args: Array[String]): Unit =
// val input = Array(Array(3,12),Array(26,84),Array(10,43),Array(68,47),Array(33,10),Array(87,35),Array(41,96),Array(70,92),Array(38,31),Array(88,59),Array(7,30),Array(89,26),Array(95,25),Array(66,28),Array(14,24),Array(86,11),Array(83,65),Array(14,4),Array(67,7),Array(89,45),Array(52,73),Array(47,85),Array(86,53),Array(68,81),Array(43,68),Array(87,78),Array(94,49),Array(70,21),Array(11,82),Array(60,93),Array(22,32),Array(69,99),Array(7,1),Array(41,46),Array(73,94),Array(98,52),Array(68,0),Array(69,89),Array(37,72),Array(25,50),Array(72,78),Array(96,60),Array(73,95),Array(7,69),Array(97,19),Array(46,75),Array(8,38),Array(19,36),Array(64,41),Array(61,78),Array(97,14),Array(54,28),Array(6,18),Array(25,32),Array(34,77),Array(58,60),Array(17,63),Array(98,87),Array(13,76),Array(58,53),Array(81,74),Array(29,6),Array(37,5),Array(65,63),Array(89,56),Array(61,18),Array(23,34),Array(76,29),Array(73,76),Array(11,63),Array(98,0),Array(54,14),Array(63,7),Array(87,32),Array(79,57),Array(72,0),Array(94,16),Array(85,16),Array(12,91),Array(14,17),Array(30,45),Array(42,41),Array(82,69),Array(24,28),Array(31,59),Array(11,88),Array(41,89),Array(48,12),Array(92,76),Array(84,64),Array(19,64),Array(21,32),Array(30,19),Array(47,43),Array(45,27),Array(31,17),Array(53,36),Array(88,3),Array(83,7),Array(27,48),Array(13,6),Array(14,40),Array(90,28),Array(80,85),Array(29,79),Array(10,50),Array(56,86),Array(82,88),Array(11,99),Array(37,55),Array(62,2),Array(55,92),Array(51,53),Array(9,40),Array(65,97),Array(25,57),Array(7,96),Array(86,1),Array(39,93),Array(45,86),Array(40,90),Array(58,75),Array(99,86),Array(82,45),Array(5,81),Array(89,91),Array(15,83),Array(93,38),Array(3,93),Array(71,28),Array(11,97),Array(74,47),Array(64,96),Array(88,96),Array(4,99),Array(88,26),Array(0,55),Array(36,75),Array(26,24),Array(84,88),Array(58,40),Array(77,72),Array(58,48),Array(50,92),Array(62,68),Array(70,49),Array(41,71),Array(68,6),Array(64,91),Array(50,81),Array(35,44),Array(91,48),Array(21,37),Array(62,98),Array(64,26),Array(63,51),Array(77,55),Array(25,13),Array(60,41),Array(87,79),Array(75,17),Array(61,95),Array(30,82),Array(47,79),Array(28,7),Array(92,95),Array(91,59),Array(94,85),Array(24,65),Array(91,31),Array(3,9),Array(59,58),Array(70,43),Array(95,13),Array(30,96),Array(51,9),Array(16,70),Array(29,94),Array(37,22),Array(35,79),Array(14,90),Array(75,9),Array(2,57),Array(81,80),Array(61,87),Array(69,88),Array(98,79),Array(18,70),Array(82,19),Array(36,27),Array(49,62),Array(67,75),Array(62,77),Array(83,96),Array(92,37),Array(95,22),Array(46,97),Array(35,0),Array(44,79),Array(82,89),Array(68,94),Array(96,31),Array(92,34),Array(25,0),Array(46,36),Array(38,84),Array(21,0),Array(0,80),Array(72,44),Array(56,97),Array(86,26),Array(94,57),Array(25,6),Array(81,13),Array(66,63),Array(57,5),Array(72,49),Array(46,86),Array(95,16),Array(95,37),Array(14,89),Array(44,22),Array(60,39),Array(37,47),Array(58,86),Array(89,96),Array(38,83),Array(51,91),Array(72,70),Array(14,82),Array(60,30),Array(58,39),Array(57,22),Array(95,70),Array(44,76),Array(5,68),Array(15,69),Array(33,61),Array(81,32),Array(21,68),Array(73,20),Array(22,72),Array(83,8),Array(15,54),Array(93,42),Array(68,95),Array(55,72),Array(33,92),Array(5,49),Array(17,96),Array(44,77),Array(24,53),Array(2,98),Array(33,81),Array(32,43),Array(20,16),Array(67,84),Array(98,35),Array(58,11),Array(72,5),Array(3,59),Array(78,79),Array(6,0),Array(26,71),Array(96,97),Array(18,92),Array(1,36),Array(78,0),Array(63,15),Array(20,43),Array(32,73),Array(37,76),Array(73,16),Array(76,23),Array(50,44),Array(68,2),Array(14,86),Array(69,65),Array(95,98),Array(53,64),Array(6,76),Array(7,11),Array(14,84),Array(62,50),Array(83,58),Array(78,92),Array(37,0),Array(13,55),Array(12,86),Array(11,59),Array(41,86),Array(27,26),Array(94,43),Array(20,78),Array(0,73),Array(58,90),Array(69,36),Array(62,34),Array(65,26),Array(32,85))
val input = Array(Array(0,4))
val result = Solution.validPath(5, input,0,4)
println(result)
【问题讨论】:
【参考方案1】:我认为这是可行的(非常有限的测试),并且没有 var
或可变集合。
它不会进行深度优先搜索,因为您的问题已被标记,而是来自似乎不是必需的问题描述。
import scala.collection.immutable.IntMap
def validPath(edgCnt: Int
,edges: Array[Array[Int]]
,start: Int, end: Int): Boolean =
val nxtSet =
edges.head
.indices
.map(x => IntMap.from(edges.groupBy(_(x)).map
case (k,v) => k -> v.flatten.toSet))
.reduce(_.unionWith(_, (k,a,b) => (a++b) - k))
def loop(from:Set[Int], to:Set[Int], open:Set[Int]):Boolean =
from.exists(to) || to.nonEmpty &&
loop(to, from.flatMap(nxtSet).filter(open), open diff to)
loop(Set(start), Set(end), Set.range(0,edgCnt) - start)
我在这里使用IntMap
只是因为它有方便的unionWith()
方法。不知道为什么其他系列没有。
【讨论】:
这回答了我的问题。 DFS 不是我只是在寻找不可变实现的要求【参考方案2】:我个人更喜欢将图建模为 Map[Node, NonEmptyList[Node]]
,其中键值对代表边。
使用该表示搜索路径非常简单:
import cats.data.NonEmptyList
type Graph[A] = Map[A, NonEmptyList[A]]
object findPath
private sealed trait SearchState
private final case object Initial extends SearchState
private final case object FoundStart extends SearchState
private final case object FoundEnd extends SearchState
def apply[A](graph: Graph[A])(start: A, end: A): Boolean =
def newState(currentState: SearchState, nextElem: A): SearchState =
(currentState, nextElem) match
case (Initial, `start`) => FoundStart
case (FoundStart, `end`) => FoundEnd
case _ => currentState
@annotation.tailrec
def loop(remainingSteps: List[(SearchState, A, Set[A])]): Boolean =
remainingSteps match
case (currentState, nextElem, visited) :: tail =>
newState(currentState, nextElem) match
case FoundEnd =>
true
case newState =>
val newVisited = visited + nextElem
val newSteps =
graph
.get(key = nextElem)
.fold(ifEmpty = List.empty[A])(_.toList)
.collect
case a if (!newVisited.contains(a)) =>
(newState, a, newVisited)
loop(newSteps reverse_::: tail)
case Nil =>
false
loop(
remainingSteps = graph.keysIterator.map(a => (Initial, a, Set.empty[A])).toList
)
如何从当前输入创建这样的Map
留给读者练习
可以看到运行here的代码。
【讨论】:
【参考方案3】:这是一个不可变的解决方案,也满足应用 bfs
object Solution
def validPath(n: Int, edges: Array[Array[Int]], start: Int, end: Int): Boolean =
if(start == end) true
else if(edges.isEmpty) false
else bfs(build(edges), start, end)
import scala.collection.immutable.Queue
def bfs(map: Map[Int, List[Int]], start: Int, end: Int): Boolean =
def go(queue: Queue[Int], seen: Set[Int]): (Queue[Int], Set[Int]) =
if (queue.isEmpty) (queue, seen)
else
val (node, remQueue) = queue.dequeue
val updateSet = seen + node
val (q, s) = map(node).foldLeft(remQueue, updateSet)
case ((q, s), n) =>
val q2 = if (!s.contains(n)) q.enqueue(n) else q
(q2, s)
go(q, s)
val (_, seen) = go(Queue.empty[Int].enqueue(start), Set.empty[Int])
if(seen.contains(start) && seen.contains(end)) true else false
def build(edges: Array[Array[Int]]): Map[Int, List[Int]] =
edges.foldLeft(Map[Int, List[Int]]()) (acc, edge) =>
appendMap(appendMap(acc, edge(1), edge(0)), edge(0), edge(1))
def appendMap(map: Map[Int, List[Int]], k: Int, v: Int): Map[Int, List[Int]] =
map.get(k) match
case Some(list) =>
val r = list :+ v
map + (k -> r)
case None => map + (k -> List(v))
它也传递 leetcode :)
【讨论】:
以上是关于使用Scala中的不可变值查找路径是不是存在于图形中的主要内容,如果未能解决你的问题,请参考以下文章
对于更新不依赖于先前值的不可变集合,是不是有任何理由更喜欢 Interlocked 而不是 volatile?