路径计算又两种方法,一种经过迭代,另外一种经过队列实现。算法
从起始点开始遍历,一个点周围又八个next_point,去除超出棋盘的点后,选择遍历的点知足,该点未被遍历或该点以前遍历路径所需的步数大于当前路径对于的步数。性能
将起始点push到队列中。当队列不为空时,每次循环从队列中pop一个点。若是那八个next_point知足条件:1. 点不超出棋盘;2. 该点未被遍历或该点以前遍历路径所需的步数大于当前路径对于的步数。测试
pub mod checkerboard; pub mod checkerboardq; #[cfg(test)] mod tests { use super::*; #[test] fn for_horse() { let mut board = super::checkerboard::CheckerBoard::new(); board.calculate((2,3)); println!("{}",board.get_count()); assert_eq!(board.get(&(2,3)),0); } #[test] fn for_horse_query() { let mut board = super::checkerboardq::CheckerBoardQ::new(); board.calculate((2,3)); board.print_board(); println!("{}",board.get_count()); assert_eq!(board.get(&(2,3)),0); } }
//迭代方案 #[derive(Debug)] pub struct CheckerBoard { board: Vec<Vec<isize>>,//存放路径对应步数 count:usize,//记录遍历次数 } impl CheckerBoard { // add code here pub fn new() -> CheckerBoard { let mut board:Vec<Vec<isize>> = Vec::with_capacity(10); let count:usize=0; for _ in 0..10 { board.push(vec![-1;9]); } CheckerBoard{ board, count, } } pub fn get(&self,point:&(usize,usize)) -> isize{ self.board[point.0][point.1] } pub fn set(&mut self,point:&(usize,usize),length:isize){ self.board[point.0][point.1] = length; } pub fn print_board(&self){ for i in 0..10{ println!("{:?}",self.board[i]); } } pub fn calculate(&mut self,point:(usize,usize)){ self.travel(point,-1); } fn travel(&mut self,point:(usize,usize),length:isize){ if point.0>9 || point.1>8{ return; } self.count = self.count + 1; let _length = self.get(&point); if _length>=0 && _length<=length+1{ return; } self.count = self.count+1; let _length = length+1; self.set(&point,length+1); //如下代码这么丑是为了减小bool计算次数 if point.0>1{ if point.1>1{ self.travel((point.0-1,point.1-2),_length); self.travel((point.0-1,point.1+2),_length); self.travel((point.0-2,point.1-1),_length); self.travel((point.0-2,point.1+1),_length); self.travel((point.0+1,point.1-2),_length); self.travel((point.0+2,point.1-1),_length); }else if point.1>0 { self.travel((point.0-1,point.1+2),_length); self.travel((point.0-2,point.1-1),_length); self.travel((point.0-2,point.1+1),_length); self.travel((point.0+2,point.1-1),_length); } }else if point.0>0{ if point.1>1{ self.travel((point.0-1,point.1-2),_length); self.travel((point.0-1,point.1+2),_length); self.travel((point.0+1,point.1-2),_length); self.travel((point.0+2,point.1-1),_length); }else if point.1>0{ self.travel((point.0-1,point.1+2),_length); self.travel((point.0+2,point.1-1),_length); } } self.travel((point.0+1,point.1+2),_length); self.travel((point.0+2,point.1+1),_length); } pub fn get_count(&self) -> usize{ self.count } }
#[derive(Debug)] struct Queue<T> { qdata: Vec<T>, } impl <T> Queue<T> { pub fn new() -> Self { Queue{qdata: Vec::new()} } pub fn len(&self)->usize{ self.qdata.len() } pub fn push(&mut self, item:T) { self.qdata.push(item); } pub fn pop(&mut self) -> T{ self.qdata.remove(0) } } #[derive(Debug)] pub struct CheckerBoardQ { board: Vec<Vec<isize>>, count:usize, point_query:Queue<(isize,isize)>, } impl CheckerBoardQ { // add code here pub fn new() -> CheckerBoardQ { let mut board:Vec<Vec<isize>> = Vec::with_capacity(10); let count:usize=0; let point_query:Queue<(isize,isize)> = Queue::new(); for _ in 0..10 { board.push(vec![-1;9]); } CheckerBoardQ{ board, count, point_query, } } fn set(&mut self,point:&(isize,isize),length:isize){ self.board[point.0 as usize ][point.1 as usize] = length; } pub fn get(&self,point:&(usize,usize)) -> isize{ self.board[point.0][point.1] } fn _get(&self,point:&(isize,isize)) -> isize{ self.board[point.0 as usize][point.1 as usize] } pub fn print_board(&self){ for i in 0..10{ println!("{:?}",self.board[i]); } } pub fn calculate(&mut self,point:(usize,usize)){ assert!(point.0<10 && point.1<9); let i_point = (point.0 as isize,point.1 as isize); self.set(&i_point,0); self.point_query.push(i_point); while self.point_query.len()>0 { self.count = self.count + 1; let _point = self.point_query.pop(); self.travel(_point); } } fn update_query(&mut self,length:isize,next_point:(isize,isize)){ let length = length+1; let _length = self._get(&next_point); if _length<0 || _length>length { self.set(&next_point,length); self.point_query.push(next_point); } } fn travel(&mut self,point:(isize,isize)) { //如下代码看上去简洁许多,但实际bool计算次数较上面方案多 let mut _next_point_query = vec![ (point.0-1,point.1-2),(point.0-1,point.1+2), (point.0+1,point.1-2),(point.0+1,point.1+2), (point.0-2,point.1-1),(point.0-2,point.1+1), (point.0+2,point.1-1),(point.0+2,point.1+1)].into_iter(); let length = self._get(&point); while let Some(_point) = _next_point_query.next() { if _point.0<0||_point.0>9||_point.1<0||_point.1>8{ continue; } self.update_query(length,_point); } } pub fn get_count(&self) -> usize { self.count } }
cargo test -- --nocapture --test-threads=1spa
测试结果很明显,迭代方案性能远远低于队列方案。这一点其实很是好理解,虽然,两个方案的核心判断条件相同,但迭代方案是从一个点出发,将其知足核心判断条件的路径所有遍历一遍。而队列方案每次只前进一步,就能够提早结束一大部分确定会被淘汰掉的遍历路径。设计
上述代码中设计到的rust语法很少。比较有意思的是八个next_point是否在棋盘上的判断算法,要兼顾优雅和高效。我所用的两个算法基本算是这种状况的极端。第一种代码一大坨,可是遍历一个点的平均bool计算次数为3.7,第二种明显是每一个next_point点都判断一次,为8 。code