有没有办法将基于矩阵的图形表示转换为 OCaml 中的邻接表之类的东西?

如何解决有没有办法将基于矩阵的图形表示转换为 OCaml 中的邻接表之类的东西?

通常,在编程面试中,您会遇到一个问题,要求您使用诸如 problem on LeetCode 之类的 DFS 或 BFS 遍历图形的二维矩阵表示。不幸的是,当您遇到节点补丁时,循环遍历元素并运行 dfs 的典型算法很难在功能上实现。我想知道是否有一种简单的方法可以将 2D 矩阵转换为 OCaml 中的邻接列表表示,以便函数算法可以获得更有效的解决方案。

解决方法

不幸的是,在遇到节点补丁时循环元素和运行 dfs 的典型算法很难在功能上实现。

相反,它们实施起来非常容易和自然。让我们展示一下。 LeetCode 的输入表示为,


grid = [
  ["1","1","0"],["1","0",["0","0"]
]

直接映射到 OCaml 为

let grid = [
  ["1";"1";"1";"1";"0"];
  ["1";"1";"0";"1";"0"];
  ["1";"1";"0";"0";"0"];
  ["0";"0";"0";"0";"0"]
]

唯一的语法区别是您必须使用 ; 而不是 , 作为分隔符。

OCaml 中的迭代,以及一般的函数式语言,都是使用迭代器来表达的。迭代器是一个高阶函数,它接受一个容器和另一个函数,并将这个函数应用于该容器的每个元素。因此,迭代不需要特殊的语言结构,例如 forwhile1

迭代器大致可以分为两组:文件夹映射器。文件夹将函数应用于每个元素并累积结果。映射器创建一个新的数据结构,其中每个元素都是原始元素的映射(转换)。不映射每个元素的映射器,即产生可能小于输入的输出数据结构,称为过滤器。最后,每个映射器都可以(通常是)使用文件夹实现,因此文件夹是最通用的迭代形式。

现在,当我们掌握了这些知识后,我们可以查看 List 以了解 iterators 提供了什么。由于我们需要找到孤立岛的数量,因此输入表示不是最优的,我们需要将其转换为更合适的东西。从图论的角度来看,一个岛是 a connected component,我们需要将我们的土地划分为一组岛屿。显然,我们的输入数据结构不是很适合,因为它不允许我们有效地查询一个元素是否连接到其他元素。单链表中的所有查询都是线性的,因为它们必须从第一个元素到最后一个元素。因此,我们需要找到一种更好的数据结构来表示我们的世界地理信息。

由于我们只对 is_land 感兴趣,因此有效的表示是一组位置,其中每个位置都表示为 x 和 y 坐标,

type pos = {x : int; y : int}

让我们为仓位类型定义一个模块,并在其中添加一些有用的功能,

module Pos = struct
  type t = pos
  let compare = compare   (* use structural compare *)
  let zero = {x=0; y=0}
  let north p = {p with y = p.y - 1}
  let south p = {p with y = p.y + 1}
  let east p = {p with x = p.x + 1}
  let west p = {p with x = p.x - 1}
end

最后,我们准备将世界定义为一组位置,

module World = Set.Make(Pos)

现在我们可以迭代我们的矩阵并创建一个世界,

let is_land = function "1" -> true | _ -> false

let make_world input : World.t =
  snd @@
  List.fold_left (fun (pos,map) ->
      List.fold_left (fun ({x;y},map) piece ->
          if is_land piece
          then ({x=x+1; y},World.add {x;y} map)
          else ({x=x+1; y},map))
        ({x=1; y = pos.y + 1},map))
    ({Pos.zero with x = 1},World.empty)
    input

了解我们如何使用 List 迭代器对矩阵执行迭代。

接下来,我们将实现 union-find 算法将世界划分为一组岛屿。让我们自上而下地开发它。 union-find 算法将一组元素划分为一组不相交的集合(通俗地称为 quotient set)。我们的初始集合是 World.t,它是所有土地位置的集合。对于每个位置,我们需要找到该位置 is_connected 所在的岛屿列表。我们现在需要精确定义 is_connected 的含义。就我们世界的几何形状而言,位于 pos 的一块土地与一个岛屿 island 相连,如果它属于 island 或者它的任何邻居属于 island {1}},其中 neighborspos 是,

let neighbors pos = [
  Pos.north pos;
  Pos.south pos;
  Pos.east pos;
  Pos.west pos;
]

所以 is_connected 是使用 List.exists 迭代器定义的,

let is_connected pos island =
  List.exists (fun x -> World.mem x island)
    (pos :: neighbors pos)

现在,我们可以编写一个函数,将岛的商集划分为一块土地所属的岛集和不与该块连接的岛集。使用 List.partition 迭代器很容易实现,

let find_islands pos =
  List.partition (is_connected pos)

如果一个元素属于几个岛,那么就意味着它是一个连接元素,一个链接,连接了在我们发现这个元素之前认为是断开的几个岛。我们需要一个将一个岛的几个部分连接成一个岛的函数。同样,我们可以使用 List.fold_left

let union = List.fold_left World.union World.empty

现在,我们拥有了所有必要的构建元素来找到我们的主要算法,

let islands world =
  World.fold (fun pos islands ->
      let found,islands = find_islands pos islands in
      World.add pos (union found) :: islands)
    world []

让我们重申它的实现。对于我们世界的每一部分,我们将我们最初的一组岛屿(以空集开始)划分为这部分所属和不属于的岛屿。然后我们union找到找到的岛屿,并将当前碎片添加到新形成的岛屿,并将该岛屿添加到岛屿集合中。

注意,在函数式编程语言中实现 union-find 是多么简单!

岛屿的数量显然是我们分区的基数,例如,

let number_of_islands world = List.length (islands world)

最后,solve 函数接受指定形式的输入并返回岛屿的数量,定义为,

let solve input = number_of_islands (make_world input)

我们来玩玩吧,

# solve [
  ["1";"1";"1";"0";"0"];
  ["1";"1";"0";"1";"0"];
  ["1";"1";"0";"0";"0"];
  ["0";"0";"0";"0";"1"]
];;
          - : int = 3
# solve [
  ["1";"1";"1";"1";"0"];
  ["1";"1";"0";"1";"0"];
  ["1";"1";"0";"0";"0"];
  ["0";"0";"0";"0";"1"]
];;
          - : int = 2
# solve [
  ["1";"1";"1";"1";"0"];
  ["1";"1";"0";"1";"1"];
  ["1";"1";"0";"0";"1"];
  ["0";"0";"0";"0";"1"]
];;
          - : int = 1
# 

看起来不错!但是如果它从一开始就不起作用怎么办?我们需要调试它。在函数式编程中,调试很容易,因为您可以独立调试每个小函数。但为此,您需要能够打印您的数据,而我们的 World.t 是一种抽象数据类型,打印为 <abstr>。为了能够打印它,我们需要定义一些打印机,例如,

let pp_pos ppf {x; y} = Format.fprintf ppf "(%d,%d)" x y
let pp_comma ppf () = Format.fprintf ppf ","
let pp_positions ppf world =
  Format.pp_print_list ~pp_sep:pp_comma pp_pos ppf
    (World.elements world)
let pp_world ppf world =
  Format.fprintf ppf "{%a}" pp_positions world

现在我们可以安装它(我假设您正在使用 ocamlutop 解释器运行这个程序),现在我们可以看到我们的算法如何将我们的世界划分为岛屿,>

# #install_printer pp_world;;

# islands @@ make_world [
  ["1";"1";"1";"0";"0"];
  ["1";"1";"0";"1";"0"];
  ["1";"1";"0";"0";"0"];
  ["0";"0";"0";"0";"1"]
];;
          - : World.t list =
[{(5,4)}; {(4,2)}; {(1,1),(1,2),3),(2,(3,1)}]

1) 我们仍然在 OCaml 中使用它们,但很少使用。

,

我猜你会喜欢这样的:

let to_adjacency_list m =
  (* Create an array of the size of the matrix with empty lists as values *)
  let ml = Array.init (Array.length m) (fun _ -> []) in
  (* For each cell (i,j) that contains true,append j to the list at index i *)
  Array.iteri
    (fun i a -> Array.iteri (fun j v -> if v then ml.(i) <- j :: ml.(i)) a)
    m;
  (* Reverse all the created lists for readability and convert the array to a list *)
  Array.to_list (Array.map List.rev ml)

如果我将其应用于矩阵:

# let m =
  [|
    [| true; true; true; true; false |];
    [| true; true; false; true; false |];
    [| true; true; false; false; false |];
    [| false; false; false; false; false |];
  |] in to_adjacency_list m;;

- : int list list = [[0; 1; 2; 3]; [0; 1; 3]; [0; 1]; []]

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res