实验说明:
启发式搜索它是利用问题拥有的启发信息来引导搜索,达到减少搜索范围、降低问题复杂度的目的。
启发式搜索算法流程设计:
代码实现:
```c
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 3
#define TRUE 1
#define FALSE 0
#define ZERO 0
typedef int Status;
typedef int NodeType[MAXSIZE][MAXSIZE];
typedef struct {
NodeType node;
float evaluate; //与目标不符的方格树,记录未来开销信息 h
double step; //指导当前节点为止搜索过的步数,记录历史开销信息 g
} Node;
struct NeighborNode {
Node node;
struct NeighborNode *next;
};
typedef struct NeighborNode *Neighbor;
struct ANode {
Node node;
Neighbor adjacents;
};
typedef struct ANode ANYNode;
struct GraphNode {
ANYNode node;
struct GraphNode *next;
};
typedef struct NeighborNode *Path;
struct Paths {
Path path;
struct Paths *next;
};
typedef struct Paths *PathS;
typedef struct Paths *Stack;
typedef struct GraphNode *Graph;
//判断两个九宫是否相同
Status equal(Node *node1, Node *node2) //节点(矩阵相同)
{
int i, j; //二维数组的行下标和列下标
Status flag = TRUE; //二维数组对应元素相等,九宫格相同
for (i = 0; i < MAXSIZE; i++) {
for (j = 0; j < MAXSIZE; j++) { //列下标变化
if (node1->node[i][j] != node2->node[i][j]) { //其中有一个元素不相等
flag = FALSE; //不相等
break; //结束
}
}
if (flag == FALSE) break; //九宫格不相等
}
return flag;
}
//实现对九宫格存储节点的赋值
void setvalue(Node *node1, Node *node2) //节点(矩阵赋值)
{
int i, j; //二维数组的行下标和列下标
for (i = 0; i < MAXSIZE; i++) //对应二维数组的赋值
for (j = 0; j < MAXSIZE; j++)
node1->node[i][j] = node2->node[i][j];
node1->evaluate = node2->evaluate;
node1->step = node2->step;
}
//计算两个状态的不同数字的个数,表示未来开销信息
float Evaluate(Node *node1, Node *node2) //两个状态不同的数字个数
{
int i, j; //行下标和列下标
float res = 0; //数字不同的个数
for (i = 0; i < MAXSIZE; i++) //行下标
for (j = 0; j < MAXSIZE; j++) //列下标
if (node1->node[i][j] != node2->node[i][j]) //累加不同数字的个数
res++; //返回数字的不同的个数
return res;
}
//评价所有相邻节点的启发信息
void EvaluateAdjacents(Neighbor adjacents, Node *node, Node *end) //根据当前状态和历史状态评估所有状态
{
Neighbor br = adjacents;
while (br) { //评估所有状态
br->node.evaluate = Evaluate(&(br->node), end); //与目标状态的差异
br->node.step = node->step + 1; //在当前状态深度加深
br = br->next;
}
}
//显示九宫格中的节点
void priNode(Node *node) //显示节点(矩阵)
{
int i, j; //二维数组的行下标和列下标
for (i = 0; i < MAXSIZE; i++) //按行下标显示
{
for (j = 0; j < MAXSIZE; j++)
printf("%5d", node->node[i][j]); //按列下标显示
printf("\n"); //换行显示
}
printf("Evaluate=%.2f\n", node->evaluate);
printf("Step=%.1f\n", node->step);
printf("****************************\n");
}
//确定空格的位置
Status Locate(Node *node, int *hori, int *vert, int zero) //有节点获取所有子节点
{
int i, j; //行下标和列下标
Status flag = FALSE;
for (i = 0; i < MAXSIZE; i++) { //行下标
for (j = 0; j < MAXSIZE; j++) { //列下标
if (node->node[i][j] == zero) { //空格
*hori = i; //空格所在的行和列
*vert = j;
flag = TRUE; //找到空格,结束
break;
}
}
if (flag == TRUE)
break; //找到空格,结束
}
return flag; //是否找到空格
}
//给定坐标用于交换九宫格的两个数字
void Exchange(Node *node, int hori1, int vert1, int hori2, int vert2) //位置交换
{
int tempnode;
tempnode = node->node[hori1][vert1]; //交换九宫格的数字
node->node[hori1][vert1] = node->node[hori2][vert2];
node->node[hori2][vert2] = tempnode;
}
Neighbor CopyNeighbors(Neighbor adjacents) //节点集复制
{
Neighbor copynode, lastnode, head = NULL; //没有子节点
while (adjacents) {
copynode = (Neighbor) malloc(sizeof(struct NeighborNode)); //分配节点单元
setvalue(&(copynode->node), &(adjacents->node)); //复制节点
copynode->next = NULL;
if (head == NULL) //第一个节点
head = copynode;
else
lastnode->next = copynode; //建立链接,复制子节点集
lastnode = copynode;
adjacents = adjacents->next; //下一个子节点
}
return head;
}
Neighbor ClearNeighbors(Neighbor nb) //回收子图节点空间
{
Neighbor n = nb; //临时变量
while (nb) {
n = nb;
nb = nb->next;
free(n); //回收单元
}
return nb; //返回NULL
}
Neighbor AddAnAdjacents(Neighbor nb, Node node) //添加相邻项
{
Neighbor pn, nm = NULL;
nm = (Neighbor) malloc(sizeof(struct NeighborNode));
setvalue(&(nm->node), &node);
nm->next = NULL;
if (nb == NULL) {
nb = nm;
} else {
pn = nb;
while (pn->next)
pn = pn->next;
pn->next = nm;
}
return nb;
}
//根据空格位置派生出所有相邻节点
Neighbor ExpandNodes(Node *node, int zero) //生成新的节点集合
{
Neighbor adjacents = NULL; //所有派生的节点集合
int h, v; //空格位置
Node *tempnode = (Node *) malloc(sizeof(Node)); //临时节点
if (!Locate(node, &h, &v, zero)) return adjacents; //没有找到空格位置,若找到空格则位置为h,v
if (h == 0 && v == 0) { //空格位置
setvalue(tempnode, node); //对九宫格的节点赋值
Exchange(tempnode, h, v, h + 1, v); //交换位置
adjacents = AddAnAdjacents(adjacents, *tempnode); //收集新的节点
setvalue(tempnode, node);
Exchange(tempnode, h, v, h, v + 1);
adjacents = AddAnAdjacents(adjacents, *tempnode);
} else if (h == 0 && v == 1) {
setvalue(tempnode, node);
Exchange(tempnode, h, v, h, v - 1);
adjacents = AddAnAdjacents(adjacents, *tempnode);
setvalue(tempnode, node);
Exchange(tempnode, h, v, h, v + 1);
adjacents = AddAnAdjacents(adjacents, *tempnode);
setvalue(tempnode, node);
Exchange(tempnode, h, v, h + 1, v);
adjacents = AddAnAdjacents(adjacents, *tempnode);
} else if (h == 0 && v == 2) {
setvalue(tempnode, node);
Exchange(tempnode, h, v, h, v - 1);
adjacents = AddAnAdjacents(adjacents, *tempnode);
setvalue(tempnode, node);
Exchange(tempnode, h, v, h + 1, v);
adjacents = AddAnAdjacents(adjacents, *tempnode);
} else if (h == 1 && v == 0) {
setvalue(tempnode, node);
Exchange(tempnode, h, v, h - 1, v);
adjacents = AddAnAdjacents(adjacents, *tempnode);
setvalue(tempnode, node);
Exchange(tempnode, h, v, h + 1, v);
adjacents = AddAnAdjacents(adjacents, *tempnode);
setvalue(tempnode, node);
Exchange(tempnode, h, v, h, v + 1);
adjacents = AddAnAdjacents(adjacents, *tempnode);
} else if (h == 1 && v == 1) {
setvalue(tempnode, node);
Exchange(tempnode, h, v, h - 1, v);
adjacents = AddAnAdjacents(adjacents, *tempnode);
setvalue(tempnode, node);
Exchange(tempnode, h, v, h + 1, v);
adjacents = AddAnAdjacents(adjacents, *tempnode);
setvalue(tempnode, node);
Exchange(tempnode, h, v, h, v - 1);
adjacents = AddAnAdjacents(adjacents, *tempnode);
setvalue(tempnode, node);
Exchange(tempnode, h, v, h, v + 1);
adjacents = AddAnAdjacents(adjacents, *tempnode);
} else if (h == 1 && v == 2) {
setvalue(tempnode, node);
Exchange(tempnode, h, v, h - 1, v);
adjacents = AddAnAdjacents(adjacents, *tempnode);
setvalue(tempnode, node);
Exchange(tempnode, h, v, h + 1, v);
adjacents = AddAnAdjacents(adjacents, *tempnode);
setvalue(tempnode, node);
Exchange(tempnode, h, v, h, v - 1);
adjacents = AddAnAdjacents(adjacents, *tempnode);
} else if (h == 2 && v == 0) {
setvalue(tempnode, node);
Exchange(tempnode, h, v, h - 1, v);
adjacents = AddAnAdjacents(adjacents, *tempnode);
setvalue(tempnode, node);
Exchange(tempnode, h, v, h, v + 1);
adjacents = AddAnAdjacents(adjacents, *tempnode);
} else if (h == 2 && v == 1) {
setvalue(tempnode, node);
Exchange(tempnode, h, v, h - 1, v);
adjacents = AddAnAdjacents(adjacents, *tempnode);
setvalue(tempnode, node);
Exchange(tempnode, h, v, h, v - 1);
adjacents = AddAnAdjacents(adjacents, *tempnode);
setvalue(tempnode, node);
Exchange(tempnode, h, v, h, v + 1);
adjacents = AddAnAdjacents(adjacents, *tempnode);
} else if (h == 2 && v == 2) {
setvalue(tempnode, node);
Exchange(tempnode, h, v, h - 1, v);
adjacents = AddAnAdjacents(adjacents, *tempnode);
setvalue(tempnode, node);
Exchange(tempnode, h, v, h, v - 1);
adjacents = AddAnAdjacents(adjacents, *tempnode);
}
return adjacents;
}
Path AddANodeToPath(Node *node, Path path) //节点加入路径
{
Path p;
p = (Neighbor) malloc(sizeof(struct NeighborNode)); //开辟节点空间
setvalue(&(p->node), node);
//Set(p->node,node,SetValue);
if (path == NULL) //路径上的第一个节点
p->next = NULL;
else
p->next = path; //加入到路径头部
path = p; //路径倒序加入
return path; //返回路径头部
}
Path CopyPath(Path path) //复制路径
{
Path tempath;
tempath = (Path) CopyNeighbors((Neighbor) path); //路径与兄弟集合相同
return tempath;
}
struct Paths *CopyPaths(struct Paths *paths) //复制路径集合
{
struct Paths *copynode, *lastnode, *head = NULL;
while (paths) { //路径集合不为空
copynode = (struct Paths *) malloc(sizeof(struct Paths)); //路径节点
copynode->path = CopyPath(paths->path); //复制一条路径
copynode->next = NULL; //复制路径
if (head == NULL) //第一条路径
head = copynode;
else //其他路径
lastnode->next = copynode;
lastnode = copynode; //加入路径集合
paths = paths->next;
}
return head;
}
void RevPath(Path path) //路径倒序
{
int num = 0, i;
Node *nodes;
Path p = path;
while (p) { //统计路径节点个数
p = p->next;
num++;
}
nodes = (Node *) malloc(num * sizeof(Node)); //开辟二维数组
for (i = 0, p = path; p; p = p->next, i++) //读取路径节点置于数组中
setvalue(&(nodes[i]), &(p->node));
//Set(nodes[i],p->node,SetValue);
for (i = num - 1, p = path; p; p = p->next, i--) //数组数据倒序置于路径中
setvalue(&(p->node), &(nodes[i]));
//Set(p->node,nodes[i],SetValue);
free(nodes); //回收数组空间
}
void priPath(Path path) //显示路径
{
if (path == NULL)
printf("The path is NULL!\n");
else {
while (path) {
priNode(&(path->node));
path = path->next;
}
printf("\n");
}
}
struct Paths *AddAPathToPaths(Path path, struct Paths *paths) //路径加入路径集合
{
Path copypath;
struct Paths *ps = NULL, *p;
if (path == NULL) //没有路径
return paths;
copypath = CopyPath(path); //复制路径
ps = (struct Paths *) malloc(sizeof(struct Paths)); //开辟路径集合节点
ps->path = copypath; //复制的路径置入
ps->next = NULL;
if (paths == NULL) //路径的集合为空
paths = ps; //新路径节点放置最后
else {
p = paths;
while (p->next)
p = p->next;
p->next = ps;
}
return paths;
}
Path ClearPath(Path path) //回收路径空间
{
path = (Path) ClearNeighbors((Neighbor) path);//路径与兄弟节点集合形式相同
return path;
}
struct Paths *ClearPaths(struct Paths *paths) //回收路径空间
{
struct Paths *ps = paths;
while (paths) { //所有路径
ps = paths;
ClearPath(ps->path); //回收一条路径空间
paths = paths->next; //下一条路径
free(ps);
}
return paths;
}
Stack PushAPath(Stack stack, Path path) //一条路径进栈
{
Path tempath;
Stack st;
tempath = CopyPath(path); //复制路径
st = (struct Paths *) malloc(sizeof(struct Paths)); //路径节点
st->path = tempath; //置路径于栈中
if (stack == NULL) //第一条路径
st->next = NULL;
else //已有路径
st->next = stack;
stack = st;
return stack;
}
Stack PushPaths(Stack stack, struct Paths *paths) //所有路径进栈
{
struct Paths *p, *head;
head = CopyPaths(paths); //复制路径集合
p = head;
if (p != NULL) { //逐一加入栈中
while (p->next)
p = p->next;
p->next = stack;
stack = head;
}
return stack;
}
Stack ClearStack(Stack stack) //回收栈空间
{
stack = ClearPaths((struct Paths *) stack); //堆栈与路径集合形式相同
return stack;
}
Status IsInPath(Node *node, Path path) //节点在路径中
{
Path p = path;
Status flag = FALSE; //节点是否在路径中的标识
while (p) {
if (equal(node, &(p->node)) == TRUE) {
flag = TRUE;
break;
} else
p = p->next;
}
return flag; //返回真假值
}
Neighbor DeleteNodeInPath(Neighbor adjacents, Path path) //从节点集合adjacent中删除节点在路径path中的节点
{
Neighbor n1 = adjacents, n2;
Status flag = FALSE;
while (n1) { //节点集合的每个节点
flag = IsInPath(&(n1->node), path); //节点是否在路径中
if (flag == TRUE) { //节点在路径中
if (n1 == adjacents) { //删除节点
adjacents = n1->next; //下一个节点
free(n1); //删除当前节点
n1 = adjacents; //其他节点
} else { //删除节点
n2->next = n1->next; //NULL
free(n1); //删除当前节点
n1 = n2->next; //NULL
}
} else { //节点不在路径中
n2 = n1; //下一个节点
n1 = n1->next;
}
}
return adjacents;
}
PathS FormPathsFromNodes(Neighbor adjacents, Path path, PathS paths) //将不在路径中的节点加入路径,形成路径集合
{
Path tempath;
adjacents = DeleteNodeInPath(adjacents, path); //删除构成回路的节点
while (adjacents) { //所有不构成回路的节点
tempath = CopyPath(path); //复制路径
tempath = AddANodeToPath(&(adjacents->node), tempath); //在路径中加入一个节点
paths = AddAPathToPaths(tempath, paths); //新路径加入路径集合
adjacents = adjacents->next; //下一个节点
}
return paths; //返回路径集合
}
//对九宫格最优节点的获取,弹出一个节点和路径
Stack PopANode_Heuristic_by_Mini(Stack stack, Node *node, Path *path) //启发式搜索,退出堆栈,获取节点和路径
{
Stack p = stack, p1 = NULL, p2, p3;
Path tempath; //临时路径
double ev = 1e100; //足够小
if (p == NULL) //没有堆栈
return stack;
*path = NULL; //清空路径
while (p != NULL) {
tempath = p->path; //获取路径
if (tempath->node.evaluate + tempath->node.step < ev) {
ev = tempath->node.evaluate + tempath->node.step; //更新最优启发式信息
p2 = p1; //下一个节点,评价值最优的节点
p3 = p; //当前节点
}
p1 = p; //下一个节点
p = p->next; //当前节点
}
tempath = p3->path; //当前节点路径(最优当前节点)
setvalue(node, &(tempath->node)); //节点赋值
*path = CopyPath(tempath); //复制路径,获取路径
if (p3 == stack)
stack = p3->next;
else
p2->next = p3->next;
free(p3); //回收当前路径
return stack; //返回堆栈
}
//搜索实现
Status SearchPath(Node *start, Node *end, Path *path, int zero) //判断节点是否在图中,并获取路径
{
Node node; //节点
Neighbor adjacents; //相邻节点集合
Stack stack = NULL; //线性空间
Status flag = FALSE; //搜索是否成功
Path tempath = NULL; //临时路径
struct Paths *paths = NULL; //生成新的路径集合
start->step = 0; //设置初始搜索的深度
start->evaluate = Evaluate(start, end); //将不相等数字的方格数初始化
tempath = AddANodeToPath(start, tempath); //形成路径
stack = PushAPath(stack, tempath); //路径进栈
while (stack) {
tempath = ClearPath(tempath); //清空路径
stack = PopANode_Heuristic_by_Mini(stack, &node, &tempath); //最优节点
if (equal(end, &node) == TRUE) {
flag = TRUE;
*path = CopyPath(tempath); //获取路径
break;
}
adjacents = ExpandNodes(&node, zero); //搜索下一级所有节点
EvaluateAdjacents(adjacents, &node, end); //计算启发的信息值
paths = FormPathsFromNodes(adjacents, tempath, paths); //形成路径集合
stack = PushPaths(stack, paths); //所有新的路径进栈
paths = ClearPaths(paths); //清空所有路径
}
ClearStack(stack); //清空堆栈
return flag;
}
int main() {
//Stack stack = NULL; //路径堆栈
Path path = NULL; //路径
Status flag; //是否搜索到路径
Node start = {{{1, 2, 3}, {8, 4, 5}, {7, 0, 6}}, 0, 0}, //初始状态
end = {{{1, 2, 3}, {8, 0, 4}, {7, 6, 5}}, 0, 0}; //目标状态
printf("Search from:\n");
priNode(&start);
printf("to:\n");
priNode(&end);
flag = SearchPath(&start, &end, &path, ZERO); //求解路径
printf("Path:\n");
RevPath(path); //路径倒置
priPath(path); //显示路径
printf("=============================\n");
ClearPath(path);
return 0;
}