图算法(三):两种基本图算法的应用实例

一、隐式图搜索

作用在解答树,是一种广度优先遍历的方法。解答树,或者更一般的状态图,在某些问题中规模本身是无限的,但是通常能嗅到如“最简单”,“最短”,“最少”等字眼,这就意味着广度优先遍历

再回忆广度优先遍历的实现,由一个队列保存新发现的节点,并且有这样的距离关系:

新发现的节点与源节点的距离=其父节点与源节点的距离+1

广度优先遍历的实现:

 1 #用字典保存一个图的邻接表形式

 2 g = {'A': ['B', 'C'],

 3      'B': ['A', 'D', 'E'],

 4      'C': ['A', 'F', 'G'],

 5      'D': ['B', 'H'],

 6      'E': ['B', 'H'],

 7      'F': ['C', 'G'],

 8      'G': ['C', 'F'],

 9      'H': ['D', 'E']}

10 

11 pi = {node: 0 for node in g.keys()} #父节点 

12 d = {node: 0 for node in g.keys()} #广度遍历的层次

13 visited = {node: 0 for node in g.keys()} #访问记录

14 

15 def BFS(start):

16     visited[start] = 1

17     global q

18     q = [] #队列,保存新发现的节点

19     q.append(start)

20 

21     while len(q) > 0:

22         v = q.pop(0)

23         for w in g[v]:

24             if  visited[w] == 0:

25                 visited[w] = 1

26                 pi[w] = v

27                 d[w] = d[v] + 1 #层次=父节点+1

28 

29                 q.append(w)

30         visited[v] = 2

31 

32 def main():

33     BFS('A')

34     print pi

例一、最优程序问题

 有一台只有一个数据栈的计算机,支持ADD, SUB, MUL, DIV, DUP五种运算。设栈顶元素分别为a, b, c,五种运算的效果如下: 

ADD: a, b依次出栈,就算a+b,结果压栈

SUB: a, b依次出栈,就算b-1,结果压栈 

MUL: a, b依次出栈,就算a*b,结果压栈

DIV: a, b依次出栈,就算a/b,结果压栈

DUP: a出栈,再连续把两个a压栈

遇到一下情况,发生错误:执行DIV操作时,栈顶元素为0;执行ADD, SUB, MUL, DIV操作时栈中只有一个元素

问题:输入(a,b),设计一个最短的程序,使得执行前栈中只有元素a,执行后栈顶元素是b

分析:

  粗略看,这里有一个解答树,是一个五叉树。但是如果没有明显的叶子节点,无法回溯。有注意到最短这个关键词,因此本题用广度优先的方法。

问题的难点在于怎么表示这个状态图呢?状态图中的节点有这样几个属性:pre(历史操作),下一步操作,stack(当前的栈状态)。

  遍历过程由源节点出发,在每一个当前节点,计算下一步操作改变栈状态后,扩展下一层节点入队列。退出条件无疑是当前节点计算下一步操作后等于目标值。

程序:

import sys

import copy



name_dict = {0: "ADD", 1: "SUB", 2: "MUL", 3: "DIV", 4: "DUP"}



op_dict = {0: lambda a, b: a + b,

           1: lambda a, b: a - b,

           2: lambda a, b: a * b,

           3: lambda a, b: b / a}



class Node:

    def __init__(self):

        self.pre = [] #前缀操作

        self.op = -1 #下一次操作

        self.stack = [] #当前的栈情况



   

def op():

    n.pre.append(name_dict[n.op])



    if n.op != 4: 

        a = n.stack.pop()

        b = n.stack.pop()

        c = op_dict[n.op](a, b)

        n.stack.append(c)

    else:   

        a = n.stack.pop()

        n.stack.append(a)

        n.stack.append(a)



def main(): 

    global n1

    global n2

    global n

    global q



    for line in sys.stdin:

        line = line.rstrip()

        s = line.split(" ")

        n1 = int(s[0])

        n2 = int(s[1])



        n = Node()

        n.op = 4

        n.stack.append(n1)

                                                                                                   

        q = []  

        q.append(copy.deepcopy(n))



        while (len(q) > 0):

            n = q[0]

            q.pop(0)

            op()    

            if n.stack[-1] == n2:

                break                                                                              

                                                                                                   

            for i in range(4):

                if len(n.stack) < 2:

                    continue

                if i == 3 and n.stack[-1] == 0:

                   continue 

                n.op = i

                q.append(copy.deepcopy(n))     

                                                                                                   

            n.op = 4

            q.append(copy.deepcopy(n))

        

        for opp in n.pre:

            print opp



        print



if __name__ == '__main__':

    main()    

 

二、拓扑排序

  拓扑排序是深度优先遍历的一个例子。拓扑排序专门作用与有向无环图(DAG),可以用来描述事务图的因果关系,具体是经过拓扑排序的节点箭头指向均是向右

注意点:判断有向图是否成环,即判断是否有反向边.

例一、假设有4个变量abcd,若a<b, c<b, d<c,求在这些条件下可能的从小到大的排序

分析:这种从小到大的排序明显的方向性

#include<stdio.h>



int g[4][4];

int f[4];



int visit[4];



int counter = 3;



int dfs(int);



int main()

{

    int x;  

    g[0][1] = 1;

    g[2][1] = 1;

    g[3][2] = 1;

    g[1][3] = 1;



    for (x = 0; x < 4; x++) 

    {

        if (visit[x] == 0)

        {       

            if (dfs(x) == -1)

            {       

                printf("this is not a DAG\n");

                return -1;

            }       

        }       

    } 

    for (x = 0; x < 4; x++) 

    {

        printf("%d ", f[x]);

    }

    return 0;

}



int dfs(int v)

{

    int w;  

    visit[v] = -1;

    for (w = 0; w < 4; w++) 

    {

        if (g[v][w] == 1)

        {       

            if (visit[w] == -1)

            {       

                //排除反向边的情况

                //拓扑排序只有在有向无环图中才有意义

                return -1;

            }else if (visit[w] == 0)

            {       

                if (dfs(w) == -1)

                {       

                    return -1;

                }       

            }       

        }       

    }

    visit[v] = 1;

    f[counter] = v;

    counter--;

}

 

但是,深度优先遍历最重要的应用还是回溯与递归

你可能感兴趣的:(算法)