油克小学期(四)启发式搜索(2)不可回溯搜索

实验说明:

启发式搜索它是利用问题拥有的启发信息来引导搜索,达到减少搜索范围、降低问题复杂度的目的。

 

启发式搜索算法流程设计:

 

代码实现:

```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;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北城学神

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值