在讲解Breadth-first search 算法之前,我们先简单介绍两种数据类型GraphQueue

Graph

这就是一个图,它由两部分组成:

  • 节点, 使用圆圈表示的部分
  • 边, 使用线表示的地方,通常都是有方向的线

这种数据结构可以形象的表示一个网络,而在实际解决问题的时候,我们除了找到类似网络的模拟外,还需要考虑下边两点:

  • 需要找到某条路径
  • 需要找到到达某个节点的最短路径

而如何实现这个查找的过程就用到了算法。

在项目管理专业的工程方法中,存在一个有向连接图方法,根据这个图我们就可以划出邻接矩阵,然后再求出可达矩阵,缩减矩阵等等,说这些内容,是想表达在用代码模拟图的时候,可以使用矩阵的方式来描述,但本篇中采用的是另一种方式,我们使用数组保存某个节点的neighbor节点

上边一段话会在下边的代码中进行展示:

  1. Graph.swift
  2. // MARK: - Edge
  3. public class Edge: Equatable {
  4. public var neighbor: Node
  5. public init(neighbor: Node) {
  6. self.neighbor = neighbor
  7. }
  8. }
  9. public func == (lhs: Edge, rhs: Edge) -> Bool {
  10. return lhs.neighbor == rhs.neighbor
  11. }
  12. // MARK: - Node
  13. public class Node: CustomStringConvertible, Equatable {
  14. public var neighbors: [Edge]
  15. public private(set) var label: String
  16. public var distance: Int?
  17. public var visited: Bool
  18. public init(label: String) {
  19. self.label = label
  20. neighbors = []
  21. visited = false
  22. }
  23. public var description: String {
  24. if let distance = distance {
  25. return "Node(label: \(label), distance: \(distance))"
  26. }
  27. return "Node(label: \(label), distance: infinity)"
  28. }
  29. public var hasDistance: Bool {
  30. return distance != nil
  31. }
  32. public func remove(edge: Edge) {
  33. neighbors.remove(at: neighbors.index { $0 === edge }!)
  34. }
  35. }
  36. public func == (lhs: Node, rhs: Node) -> Bool {
  37. return lhs.label == rhs.label && lhs.neighbors == rhs.neighbors
  38. }
  39. // MARK: - Graph
  40. public class Graph: CustomStringConvertible, Equatable {
  41. public private(set) var nodes: [Node]
  42. public init() {
  43. self.nodes = []
  44. }
  45. public func addNode(_ label: String) -> Node {
  46. let node = Node(label: label)
  47. nodes.append(node)
  48. return node
  49. }
  50. public func addEdge(_ source: Node, neighbor: Node) {
  51. let edge = Edge(neighbor: neighbor)
  52. source.neighbors.append(edge)
  53. }
  54. public var description: String {
  55. var description = ""
  56. for node in nodes {
  57. if !node.neighbors.isEmpty {
  58. description += "[node: \(node.label) edges: \(node.neighbors.map { $0.neighbor.label})]"
  59. }
  60. }
  61. return description
  62. }
  63. public func findNodeWithLabel(_ label: String) -> Node {
  64. return nodes.filter { $0.label == label }.first!
  65. }
  66. public func duplicate() -> Graph {
  67. let duplicated = Graph()
  68. for node in nodes {
  69. _ = duplicated.addNode(node.label)
  70. }
  71. for node in nodes {
  72. for edge in node.neighbors {
  73. let source = duplicated.findNodeWithLabel(node.label)
  74. let neighbour = duplicated.findNodeWithLabel(edge.neighbor.label)
  75. duplicated.addEdge(source, neighbor: neighbour)
  76. }
  77. }
  78. return duplicated
  79. }
  80. }
  81. public func == (lhs: Graph, rhs: Graph) -> Bool {
  82. return lhs.nodes == rhs.nodes
  83. }

Queue

队列同样是一种数据结构,它遵循FIFO的原则,因为Swift没有现成的这个数据结构,因此我们手动实现一个。

值得指出的是,为了提高性能,我们针对在数组中读取数据做了优化。比如,当在数组中取出第一个值时,如果不做优化,那么这一步的消耗为O(n),我们采取的解决方法就是把该位置先置为nil,然后设置一个阈值,当达到阈值时,在对数组做进不去的处理。

这一部分的代码相当简单

  1. Queue.swift
  2. public struct Queue<T> {
  3. fileprivate var array = [T?]()
  4. fileprivate var head = 0
  5. public init() {
  6. }
  7. public var isEmpty: Bool {
  8. return count == 0
  9. }
  10. public var count: Int {
  11. return array.count - head
  12. }
  13. public mutating func enqueue(_ element: T) {
  14. array.append(element)
  15. }
  16. public mutating func dequeue() -> T? {
  17. guard head < array.count, let element = array[head] else { return nil }
  18. array[head] = nil
  19. head += 1
  20. let percentage = Double(head) / Double(array.count)
  21. if array.count > 50 && percentage > 0.25 {
  22. array.removeFirst(head)
  23. head = 0
  24. }
  25. return element
  26. }
  27. public var front: T? {
  28. if isEmpty {
  29. return nil
  30. } else {
  31. return array[head]
  32. }
  33. }
  34. }

Breadth-first search

其实这个算法的思想也很简单,我们已源点为中心,一层一层的往外查找,在遍历到某一层的某个节点时,如果该节点是我们要找的数据,那么就退出循环,如果没找到,那么就把该节点的neighbor节点加入到队列中,这就是该算法的核心原理。

打破循环的条件需要根据实际情况来设定。


  1. //: Playground - noun: a place where people can play
  2. import UIKit
  3. import Foundation
  4. var str = "Hello, playground"
  5. func breadthFirstSearch(_ graph: Graph, source: Node) -> [String] {
  6. /// 创建一个队列并把源Node放入这个队列中
  7. var queue = Queue<Node>()
  8. queue.enqueue(source)
  9. /// 创建一个数组用于存放结果
  10. var nodesResult = [source.label]
  11. /// 设置Node的visited为true,因为我们会把这个当做一个开关
  12. source.visited = true
  13. /// 开始遍历
  14. while let node = queue.dequeue() {
  15. for edge in node.neighbors {
  16. let neighborNode = edge.neighbor
  17. if !neighborNode.visited {
  18. queue.enqueue(neighborNode)
  19. neighborNode.visited = true
  20. nodesResult.append(neighborNode.label)
  21. }
  22. }
  23. }
  24. return nodesResult
  25. }
  26. let graph = Graph()
  27. let nodeA = graph.addNode("a")
  28. let nodeB = graph.addNode("b")
  29. let nodeC = graph.addNode("c")
  30. let nodeD = graph.addNode("d")
  31. let nodeE = graph.addNode("e")
  32. let nodeF = graph.addNode("f")
  33. let nodeG = graph.addNode("g")
  34. let nodeH = graph.addNode("h")
  35. graph.addEdge(nodeA, neighbor: nodeB)
  36. graph.addEdge(nodeA, neighbor: nodeC)
  37. graph.addEdge(nodeB, neighbor: nodeD)
  38. graph.addEdge(nodeB, neighbor: nodeE)
  39. graph.addEdge(nodeC, neighbor: nodeF)
  40. graph.addEdge(nodeC, neighbor: nodeG)
  41. graph.addEdge(nodeE, neighbor: nodeH)
  42. graph.addEdge(nodeE, neighbor: nodeF)
  43. graph.addEdge(nodeF, neighbor: nodeG)
  44. let nodesExplored = breadthFirstSearch(graph, source: nodeA)
  45. print(nodesExplored)

总结

实现的代码不是重点,重要的是理解这些思想,在实际情况中能够得出解决的方法。当然跟实现的语言也没有关系。

使用playground时,command + 1可以看到Source文件夹,把单独的类放进去就可以加载进来了。上边的内容来自这个网站swift-algorithm-club

Breadth-first search 算法(Swift版)的更多相关文章

  1. Dijkstra算法(Swift版)

    原理 我们知道,使用Breadth-first search算法能够找到到达某个目标的最短路径,但这个算法没考虑weight,因此我们再为每个edge添加了权重后,我们就需要使用Dijkstra算法来 ...

  2. 算法与数据结构(四) 图的物理存储结构与深搜、广搜(Swift版)

    开门见山,本篇博客就介绍图相关的东西.图其实就是树结构的升级版.上篇博客我们聊了树的一种,在后边的博客中我们还会介绍其他类型的树,比如红黑树,B树等等,以及这些树结构的应用.本篇博客我们就讲图的存储结 ...

  3. 快速排序OC、Swift版源码

    前言: 你要问我学学算法在工作当中有什么用,说实话,当达不到那个地步的时候,可能我们不能直接的感觉到它的用处!你就抱着这样一个心态,当一些APP中涉及到算法的时候我不想给其他人画界面!公司的项目也是暂 ...

  4. 广度优先搜索(Breadth First Search, BFS)

    广度优先搜索(Breadth First Search, BFS) BFS算法实现的一般思路为: // BFS void BFS(int s){ queue<int> q; // 定义一个 ...

  5. 【数据结构与算法Python版学习笔记】图——词梯问题 广度优先搜索 BFS

    词梯Word Ladder问题 要求是相邻两个单词之间差异只能是1个字母,如FOOL变SAGE: FOOL >> POOL >> POLL >> POLE > ...

  6. Swift版iOS游戏框架Sprite Kit基础教程下册

    Swift版iOS游戏框架Sprite Kit基础教程下册 试读下载地址:http://pan.baidu.com/s/1qWBdV0C 介绍:本教程是国内唯一的Swift版的Spritekit教程. ...

  7. 从vector容器中查找一个子串:search()算法

    如果要从vector容器中查找是否存在一个子串序列,就像从一个字符串中查找子串那样,次数find()与find_if()算法就不起作用了,需要采用search()算法:例子: #include &qu ...

  8. Swift版音乐播放器(简化版),swift音乐播放器

    这几天闲着也是闲着,学习一下Swift的,于是到开源社区Download了个OC版的音乐播放器,练练手,在这里发扬开源精神, 希望对大家有帮助! 这个DEMO里,使用到了 AudioPlayer(对音 ...

  9. iOS可视化动态绘制八种排序过程(Swift版)

    前面几篇博客都是关于排序的,在之前陆陆续续发布的博客中,我们先后介绍了冒泡排序.选择排序.插入排序.希尔排序.堆排序.归并排序以及快速排序.俗话说的好,做事儿要善始善终,本篇博客就算是对之前那几篇博客 ...

随机推荐

  1. Zabbix(二) : Zabbix Server端配置文件说明

    Zabbix Server端配置文件说明 # This is a configuration file for Zabbix Server process # To get more informat ...

  2. PS 软件操作应用处理——粒子化任务效果

      前  言 JRedu 上次分享中,给大家介绍了一些图片的处理方法,主要是通过滤镜里的功能,把图片处理成素描效果或者水彩画效果,营造出不同的氛围. PS是一款非常强大的软件,包含了非常多的功能,合成 ...

  3. Response.Write输出导致页面变形和页面白屏解决办法

    方法一:此方法应该是微软官方推荐的方法,但弹出时会造成页面白屏.Page.RegisterStartupScript("TestEvent", "<script&g ...

  4. MongoDB学习教程(3)-常用命令

    1.MongoDB 条件操作符 描述 条件操作符用于比较两个表达式并从mongoDB集合中获取数据. 在本章节中,我们将讨论如何在MongoDB中使用条件操作符. MongoDB中条件操作符有: (& ...

  5. RewriteMap(apache)

    最近在工作中发现一个陌生的语法,apache服务器站点rewrite配置文件里的,开始还以为是apache的一种新语法,以这个词网上搜索,没搜到相关文章,跟老同事请教了一下,说这个是RewriteMa ...

  6. var let const 的区别

    Var let const 的区别 1.Var 定义的变量存在变量提升,而了let和const不存在变量提升.即在定义的变量代码上使用该变量,var的会输出undefined,而let的会报错. 2. ...

  7. [解读REST] 1.REST的起源

    0. 世界上第一个网站 1990年12月20日,这一天对于现在的互联网来说意义非凡.欧洲核子研究组织(CREN)的科学家Tim Berners-Lee在一台NeXT电脑上启动了世界上的第一个网站(当然 ...

  8. DevOps之基础设施

    唠叨话 关于德语关我屁事的知识点,仅提供精华汇总,具体知识点细节,参考教程网址,如需帮助,请留言. <信息技术(IT )> 关于IT信息技术的基础设施,知识与技能的层次(知道.理解.运用) ...

  9. app启动页问题

    今天自己做的小作品准备提交,就差一个启动页,各种百度,各种搜,结果还好最后终于出来了,和大家分享一下,这个过程中遇到的各种小问题.(注XCode版本为7.2) 1.启动页一般都是图片,因为苹果有4,4 ...

  10. Linux-Nand Flash驱动(分析MTD层并制作NAND驱动)

    1.本节使用的nand flash型号为K9F2G08U0M,它的命令如下: 1.1我们以上图的read id(读ID)为例,它的时序图如下: 首先需要使能CE片选 1)使能CLE 2)发送0X90命 ...