深度和广度节点搜索,对应的单线程与多线程版本

节点操作接口

public interface Puzzle<P,M>{
    //获取起始节点
    P initialPosition();
    //是否为目标节点
    boolean isGoal(P position);
    //合法的移动路径
    Set<M> legalMoves(P position);
    //移动之后的节点位置
    P move(P position,M move)
}

节点

static class Node<P,M>{
    //节点
    final P pos;
    //移动操作
    final M move;
    //前节点
    fina Node<P,M> prev;
    //获取从该节点到起始节点的移动操作列表
    public List<M> asMoveList(){
        List<M> solution = new LinkedList<M>();
        for(Node<P,M> n=this;n.move != null;n=n.prev){
            //因为移动是从起点开始,所以将移动操作放在列表的开头
            solution.add(0,n.move);
        }
        return solution;
    }
}

串行程序,深度优先

//该程序并不寻找最优,找到就停止
public class SequentialPuzzleSolver<P,M>{
    private final Puzzle<P,M> puzzle;
    //存放已经搜索过的节点
    private final Set<P> seen = new HashSet<P>();

    public List<M> solve(){
        P pos = puzzle.initialPosition();
        return search(new Node<P,M>(pos,null,null));
    }

    private List<M> search(Node<P,M> node){
        if(!seen.contains(node.pos)){
            seen.add(node.pos);
            if(puzzle.isGoal(node.pos)) //目标节点
                return node.asMoveList();
            //遍历所有合法的路径
            for(Move m:puzzle.legalMoves(node.pos)){
                P pos = puzzle.move(node.pos,m);
                Node<P,M> child = new Node<P,M>(pos,move,node);
                List<M> result = search(child);
                if(result != null)
                    return result;
            }
        }
        //没找着
        return null;
    }
}

多线程广度搜索

//该广度搜索也是找到路径就返回,并不在意是否是最优节点。
//同时在找到后会关闭executor,需要将拒绝执行处理器设置为“抛弃已提交任务”
//在未找到解决方案时停止(将solution值设置为null)
public class ConcurrentPuzzleSolver<P,M>{
    private final Puzzle<P,M> puzzle;
    private final ExecutorService exec;
    //为了线程安全,故将已经搜索过的路径放入concurrentMap,并采用putIfAbsent方法验证是否已搜索
    private final ConcurrentMap<P,Boolean> seen;
    //存放结果的闭锁对象
    final ValueLatch<Node<P,M>> solution = new ValueLatch<Node<P,M>>();
    //任务开启数
    private final AtomicInteger taskCount = new AtomicInteger(0);

    public List<M> solve() throws InterruptedException{
        try{
            P p = puzzle.initialPosition();
            exec.execute(newTask(p,null,null) );
            //getValue方法为阻塞实现,使用的是countDownLatch的await()方法
            Node<P,M> solnNode = solution.getValue();
            return (solunNode == null) ? null:solunNode.asMoveList();
        }finally{
            //将线程池guanbi
            exec.shutdown();
        }
    } 

    protected Runnable newTask(P p,M m,Node<P,M> n){
        return new SolverTask(p,m,n);
    }

    //搜索任务
    class SolverTask extends Node<P,M> implements Runnable{

        SolverTask(P p,M m,Node<P,M> n){
            super(p,m,n);
            //增加计数
            taskCount.incrementAndGet();
        }

        public void run(){
            try{
                if(solution.isSet() || seen.putIfAbsent(pos,true) != null)
                    return;//已经找到或者已经搜索过
                if(puzzle.isGoal(pos))
                        solution.setValue(this);//设置为已找到
                else
                    for(M m:puzzle.legalMoves(pos))
                        exec.execute(newTask(puzzle.move(pos,m),m,this));
                }finally{
                    //线程开启的任务数是0
                    if(taskCount.decrementAndGet() == 0)
                        solution.setValue(null);
                }
        }
    }

    //利用闭锁机制来检查程序是否找到结果,即阻塞的并携带结果的闭锁
    public class ValueLatch<T>{
        private synchronized T value = null;
        //设置为1表示只要找到一个解答就停止
        private final CountDownLatch done = new CountDownLatch(1);
        public boolean isSet(){  //是否完成
            return (done.getCount() == 0);
        }
        public synchronized void setValue(T newValue){
            if(!isSet()){
                value = newValue;
                done.countDown();  //完成
            }
        }
    public T getValues throws InterruptedException{
        done.await(); //阻塞
        synchronized(this){
            return value;
        }
    }
    }
}

该多线程版本可以添加等待时间限制,比如在getValue()中的await方法添加时间限制。
同时,该版本在关闭Executor时,要避免RejectedExecutionException,需要将拒绝执行处理器设置为“抛弃已提交的任务”

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值