zoukankan      html  css  js  c++  java
  • 2-25《啊哈算法》第5章,图的遍历原理。(2-26)

    第一节,深度和广度优先指啥

     针对图的遍历而言的。

     常用的储存方法是图的二维数组e,图的邻接矩阵储存法。

     还有很多种储存方法,如邻接表。 

      

    深度优先遍历的主要思想:沿着图的某一分支遍历直到末端,然后回朔,然后再沿着另一条进行同样的遍历,直到所有顶点都被访问过为止。

    广度优先遍历的主要思想:首先以一个未被访问过的顶点作为起始顶点,访问其所有相邻的顶点,然后对每个相邻的顶点,再访问它们相邻的未被访问过的顶点,直到所有顶点都被访问过,遍历结束。 

    无论是深度优先变量还是广度优先遍历,本节的题目都会产生图的生成树。

    什么是生成树?

    如果一个无向图不包含回路,就是一棵树。 

    # 深度优先遍历 
    # 直接输入 图的邻接矩阵。
    $e = [
      nil,
      [nil,0, 1, 1, 99, 1],
      [nil,1, 0, 99, 1, 99],
      [nil,1, 99, 0, 99, 1],
      [nil,99, 1, 99, 0, 99],
      [nil,1, 99, 1, 99, 0]
    ]
    # 代表每个点都没有被访问,如果访问则改为1.
    $book = [nil,0,0,0,0,0]
    $sum = 0
    $n = 5
    def dfs(current)
    # 输出经过的点。
      p current
    # 每访问一个点,sum加1
      $sum += 1
    # 边界,如果所有点都访问到了,退出。
      if $sum == $n
        return
      end
    # 尝试current和各个点比较,是否连接,并判断i点是否已经访问过了
      i=1
      for i in 1..5 do
        if $e[current][i] == 1 && $book[i] == 0
          $book[i] = 1
          dfs(i) #从顶点i出发继续遍历。
        end
      end
    end
    $book[1] = 1 #第1个点已经访问
    dfs(1)

     广度优先遍历

    # 直接输入 图的邻接矩阵。
    $e = [
      [0, 1, 1, 99, 1],
      [1, 0, 99, 1, 99],
      [1, 99, 0, 99, 1],
      [99, 1, 99, 0, 99],
      [1, 99, 1, 99, 0]
    ]
    # 标记走访记录,0未走访,1已走访
    $book = [0,0,0,0,0]
    # queue队列
    x = []
    head = 0 #指针
    tail = 1
    # 当前点,第一个点矩阵中是0
    head = 0
    # 第一个点入队列
    x << head
    # 第一个点 设置为已访问
    $book[head] = 1
    while head < tail
      i = 0
      while i <= 4
        if $e[head][i] == 1 && $book[i] == 0
          $book[i] = 1
          x << i
          tail += 1
        end
        i += 1
      end
      head += 1
    end

    #排列输出搜索顺序。 

    x.each do |n|
      p n + 1
    end

    第二节, 城市地图 -图的深度优先遍历

     图分为,有向图,无方图,边也就分有无。

     和一个有向边连接的点分起始点和终点。和一个点相关的边分为出边和入边。

    ❌:设立一个二维数组的时候,用双层嵌套。但要⚠️数据声明结构的问题 

    1. 在循环前,先声明数组
    2. 在第一个循环后,第一行代码,是给二层数组声明,之后才能在内层循环中赋值。 

    问题:从任意1个城市到另一个城市的最短路径?hint:用深度优先搜索 

    # # 矩阵5*5 手输入数据。
    # # 城市5个,n = 5.
    # $map = []
    # i = j = 1
    # n = 5
    # for i in 1..n do
    #   # 声明二层结构是数组。
    #   $map[i] = []
    #   # 给二层数组赋值。
    #   for j in 1..n do
    #     if i == j
    #       $map[i][j] = 0
    #     else
    #       $map[i][j] = 999
    #     end
    #   end
    # end
    #
    # # 城市间的道路m条
    # m = 8
    # for i in 1..m do
    #   print "输入3个数,a,b,r: "
    #   a = gets.to_i
    #   b = gets.to_i
    #   r = gets.to_i
    #   $map[a][b] = r
    # end
    #
    # $map.each do |n|
    #   p n
    # end

     代码:有向图。

    $map = [
      nil,
      [nil, 0, 2, 999, 999, 10],
      [nil, 999, 0, 3, 999, 7],
      [nil, 4, 999, 0, 4, 999],
      [nil, 999, 999, 999, 0, 5],
      [nil, 999, 999, 3, 999, 0]
    ]
    # 记录是否走过。
    $book = [nil,0,0,0,0,0]
    # 储存最小路径
    $min = 999999
    # current为当前城市,destination目的地, sum为路径和

    def dfs(current, destination,sum)

      # 如果当前的路径所花费大于$min,则没必要再继续尝试下去了,马上返回
      if sum > $min
        return
      end

      # border 到达终点是结束的标志。

      if current == destination
        p "当前:#{sum}"
        if sum < $min
          $min = sum
        end
        p "最小:#{$min}"
        return
      end
      i = 1
      for i in 1..5 do
        if $map[current][i] != 999 && $map[current][i] != 0 && $book[i] == 0
          $book[i] = 1
          sum += $map[current][i]
          dfs(i, destination,sum)
          #回溯的时候,之前sum加上的路径还要减掉,

          #或者直接在方法参数中写: dfs(i, destionation,sum + $map[current][i])

          sum -= $map[current][i]
          # 之前一步探索完毕之后,取消对城市i的标记
          $book[i] = 0
        end
      end
    end
    $book[1] = 1
    dfs(1,0)

     第三节  广度优先遍历,求最少转机次数。

    广度优先适合所有边的权重一样的时候。 


    问:求任意2个城市直接的最少转机次数 ?

    答:

    $map =[
      nil,
      [nil, 0, 1, 1, 999, 999],
      [nil, 1, 0, 1, 1, 999],
      [nil, 1, 1, 0, 1, 1],
      [nil, 999, 1, 1, 0, 1],
      [nil, 999, 999, 1, 1, 0]
    ]
    # 队列queue初始化,x记录经过城市号,声明指针赋值。
    x = []
    head = 0
    tail = 1
    # 标记城市的变量
    book = [nil,0,0,0,0,0]
    book[1] = 1
    # 开始城市是start,加入队列x, 目的地destination

    print "请输入出发城市编号:(1-5) " 

    start = gets.to_i

    print "请输入目的地城市编号:(1-5) " 

    destination = gets.to_i 

    x << start
    # 记录转机数
    step = []
    step[head] = 0
    # 当队列不为空的时候♻️
    while head < tail
      # cur 是当前城市
      cur= head + 1
      i = 1
      for i in 1..5 do
        if $map[cur][i] != 0 && $map[cur][i] != 999 && book[i] == 0
          # 途经的城市标记下。从1直接到3,标记3。再从2到3就不需要录入队列了,因为没必要增加转机次数。
          book[i] = 1
          x << i
          # 记录cur当前城市是第几次转机。
          step[tail] = step[head] + 1
          tail += 1
          if x.last == destination
            break
          end
        end
      end
      # 外层再写一遍,是为了省去再循环。
      if x.last == destination
        break
      end
      head += 1
    end
    p "需要#{step.last}次转机"
  • 相关阅读:
    Regular Expression Basic
    Getting http address from text file by awk script
    日报、周报,项目进度汇报有意义吗?
    目不转睛地盯着电脑屏幕,认真找Bug的你
    这组朋友圈,得罪了半个互联网圈!
    2021年,让你看透世界的8个底层逻辑
    再见,胡阿姨!再见,共享单车!
    @所有人,2021新年快乐,每个人都了不起!
    为了实现而工程,大道至简第五章读后感
    Java第四次上课博文动手动脑
  • 原文地址:https://www.cnblogs.com/chentianwei/p/8470544.html
Copyright © 2011-2022 走看看