github.com/qiuhoude/go-web@v0.0.0-20220223060959-ab545e78f20d/algorithm/datastructures/graph/a_star.go (about) 1 package graph 2 3 import ( 4 "fmt" 5 "github.com/qiuhoude/go-web/algorithm/datastructures/queue" 6 ) 7 8 /* 9 a*算法 10 h(i): 启发函数(heuristic function),是到目标点的曼哈顿距离(Manhattan distance) 11 曼哈顿距离: 指两点之间横纵坐标的距离之和 h() = abs(x1-x2) + abs(y1-y2) 12 g(i): 起点到该点的距离 13 f(i) = h(i) + g(i) 14 */ 15 16 type point struct { 17 f int // f(i) = h(i) + g(i) 18 g int //起点到该点的距离 19 x, y int //坐标 m[y][x] 20 parent *point // 父节点 21 } 22 23 func (p *point) String() string { 24 return fmt.Sprintf("(%v,%v,%v)", p.x, p.y, p.g) 25 } 26 27 func newPoint(x, y, g, f int, p *point) *point { 28 return &point{ 29 x: x, 30 y: y, 31 g: g, 32 f: f, 33 parent: p, 34 } 35 } 36 37 func comparePoint(v1, v2 interface{}) int { 38 p1 := v1.(*point) 39 p2 := v2.(*point) 40 if p1.f > p2.f { 41 return 1 42 } else if p1.f < p2.f { 43 return -1 44 } 45 return 0 46 } 47 48 func equalPoint(v1, v2 interface{}) bool { 49 p1 := v1.(*point) 50 p2 := v2.(*point) 51 return p1.x == p2.x && p1.y == p2.y 52 } 53 54 // 曼哈顿距离 55 func hManhattan(x1, y1, x2, y2 int) int { 56 return abs(x1-x2) + abs(y1-y2) 57 } 58 59 func abs(a int) int { 60 if a < 0 { 61 return -a 62 } 63 return a 64 } 65 66 func xyToId(x, y, width int) int { 67 return y*width + x 68 } 69 70 const ( 71 positive = 10 // 上下左右距离是 10 72 opposite = 14 // 对角是距离是 14 73 ) 74 75 func checkToXyAvail(x, y int, m [][]uint) bool { 76 high := len(m) 77 width := len(m[0]) 78 if y < 0 || y >= high || x < 0 || x >= width { // 超过边界 79 return false 80 } 81 if m[y][x] == 1 { // 障碍物不能走 82 return false 83 } 84 return true 85 } 86 87 func AstarSearch(sx, sy, tx, ty int, m [][]uint) { 88 // 都是以左上为原点 89 high := len(m) 90 width := len(m[0]) 91 92 visited := make(map[int]bool) // 相当于有些教程中的 close表 93 que := queue.NewPriorityQueue(comparePoint) // open表 94 visited[xyToId(sx, sy, width)] = true 95 que.Enqueue(newPoint(sx, sy, 0, 0, nil)) // 加入起点坐标 96 var findPoint *point 97 out: 98 for !que.IsEmpty() { 99 curP := que.Dequeue().(*point) 100 // 逐个遍历 上下左右 左上右上 左下右下 101 for i := -1; i <= 1; i++ { // y轴 102 for j := -1; j <= 1; j++ { // x轴 103 afY := curP.y + i // 计算后的 x, y 104 afX := curP.x + j 105 if !checkToXyAvail(afX, afY, m) || (i == 0 && j == 0) { 106 continue 107 } 108 // 有障碍物斜边是否能跨越 109 if i == -1 && j == -1 { // 左上 110 if !checkToXyAvail(curP.x-1, curP.y, m) || !checkToXyAvail(curP.x, curP.y-1, m) { 111 continue 112 } 113 } else if i == 1 && j == -1 { // 左下 114 if !checkToXyAvail(curP.x-1, curP.y, m) || !checkToXyAvail(curP.x, curP.y+1, m) { 115 continue 116 } 117 } else if i == -1 && j == 1 { // 右上 118 if !checkToXyAvail(curP.x+1, curP.y, m) || !checkToXyAvail(curP.x, curP.y-1, m) { 119 continue 120 } 121 } else if i == 1 && j == 1 { //右下 122 if !checkToXyAvail(curP.x+1, curP.y, m) || !checkToXyAvail(curP.x, curP.y+1, m) { 123 continue 124 } 125 } 126 127 isOpposite := abs(i) == 1 && abs(j) == 1 // 是否为对角 128 h := hManhattan(afX, afY, tx, ty) // 曼哈顿距离 129 g := 0 130 if isOpposite { 131 g = curP.g + opposite // 对角+14 132 } else { 133 g = curP.g + positive 134 } 135 f := h + g //f(i) = h(i) + g(i) 136 id := xyToId(afX, afY, width) 137 np := newPoint(afX, afY, f, g, curP) 138 if visited[id] { 139 p2 := que.Remove(np, equalPoint) 140 if p2 != nil { 141 np2 := p2.(*point) 142 np2.g = g 143 np2.f = f 144 // 此处不要更新父节点值 145 que.Enqueue(np2) 146 } 147 } else { 148 que.Enqueue(np) 149 visited[id] = true // 设置已访问 150 } 151 if afX == tx && afY == ty { 152 findPoint = np 153 break out 154 } 155 } 156 } 157 } 158 159 if findPoint != nil { 160 m2 := make([][]uint, high) 161 for i := range m2 { 162 m2[i] = make([]uint, width) 163 copy(m2[i], m[i]) 164 } 165 cur := findPoint 166 var i uint = 2 167 for ; cur != nil; cur = cur.parent { 168 m2[cur.y][cur.x] = i 169 i++ 170 } 171 printMap(m2) 172 } else { 173 fmt.Println("不可达") 174 } 175 } 176 177 func printMap(m [][]uint) { 178 //fmt.Printf("%4d", -1) 179 //for i := 0; i < len(m[0]); i++ { 180 // fmt.Printf("%4d", i) 181 //} 182 //fmt.Println() 183 for i := range m { 184 //fmt.Printf("%4d", i) 185 for j := range m[i] { 186 if j != 0 { 187 } 188 fmt.Printf("%4d", m[i][j]) 189 } 190 fmt.Println() 191 } 192 }