基于链表数据结构+优先队列式(LC)分支限界算法的布线问题的求解(C语言版)

布线问题

题目描述:印刷电路板将布线区域划分成 n×m 个方格阵列,要求确定连接方格阵列中的方格 a 的中点到方格 b 的中点的最短布线方案。在布线时,电路只能沿直线或直角布线,为了避免线路相交,已布了线的方格做了封锁标记,其他线路不允许穿过被封锁的方格。

效果对比

队列式分支限界算法在搜索过程扩展或考查的点数如下图所示:

6

5

4

3

4

5

6

7

8

9

5

4

3

2

3

4

5

6

7

8

4

3

2

1

2

3

-1

7

8

-3

3

2

1

-2

1

-1

-1

8

9

0

4

3

-1

1

2

3

-1

7

8

9

5

4

-1

2

3

4

5

6

7

8

6

5

-1

3

4

5

6

7

8

9

7

6

5

4

5

6

7

8

9

0

采用优先队列后的效果如下图所示,由此可见,大部分无用的分支没有被扩展就找到目标结点了。

0

0

0

0

0

5

6

7

0

0

0

0

0

0

3

4

5

6

7

0

0

0

0

1

2

3

-1

7

8

-3

0

0

1

-2

1

-1

-1

8

9

0

0

0

-1

1

2

0

-1

0

0

0

0

0

-1

0

0

0

0

0

0

0

0

0

-1

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

程序代码和运行效果截图放在了下面,其他关于题目的分析过程和说明请参照我之前发的帖子:

基于链队数据结构+队列式(FIFO)分支限界算法的布线问题的求解(C语言版)

程序代码

/* 基于链表数据结构 + 优先队列式(LC)分支限界算法的布线问题的求解(C语言版) */

#include<stdio.h>
#include<stdlib.h>

#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define OVERFLOW -1
#define PERROR -2
typedef int Status;

// 数据元素的类型,使用时需根据问题的需求定义。
typedef struct {
    int row;            // 数据域
    int col;             // 指针域
    int f;                 // 结点距离起点的距离
    int calculate;        // 预估距离目标结点的最少距离 
    int move_mode;        // 0: 起点    1:向上 2:向下 3:向左 4:向右
} ElemType;

// 结单链表点类型定义
typedef struct LNode {
    ElemType   data;    // 数据域
    struct   LNode *next;    // 指针域
} LNode;

// 链表结构体类型定义
typedef  LNode*  LinkList;

// 1、链队初始化,初始化一个只含头结点的空单链表 L
Status InitList_L (LinkList &L) {
    L = (LNode*)malloc(sizeof(struct LNode));         // 为头结点申请空间
    if(L == NULL)    return  OVERFLOW;                  //如果申请失败,返回溢出
    L->next = NULL;
    return OK;
}
// 2、构造单链表结点
LNode* MakeNode_L(ElemType e) {
    LNode *p;
    p = (LNode*)malloc(sizeof(struct LNode)); // 分配结点空间
    if (p!=NULL) {
        p->data = e;
        p->next = NULL;
    }
    return p;
}

// 3、销毁单链表
Status DestroyList_L(LinkList &L){
    LNode *q; 
    if(L==NULL) return ERROR;        // 判断链表是否存在,已销毁,不再执行销毁操作。 
    while(L!=NULL) {                // 如果是非空表,依次释放表中各结点         
        q = L;
        L = q->next;
        free(q);
    }
    return OK;
}


// 4、判断单链表是否为空
Status ListEmpty_L(LinkList L){
    if(NULL == L) return ERROR;        // 判断链表是否存在,已销毁,不再执行销毁操作。
    if(L->next == NULL){
        return TRUE;
    }
    else{
        return FALSE;
    }
}

// 5、单链表的插入 P 的后继结点操作
Status InsertAfter_L(LNode *p, LNode *q){
    if(p == NULL || q == NULL) return PERROR;  // 参数不合理
    q->next = p->next;                        // 修改q结点的指针域
    p->next = q;
    return OK;
}

// 6、头插法往单链表中的插入一个结点
Status InsertFirst_L(LinkList &L, ElemType e){
    if(L == NULL) return PERROR;              // 参数不合理
    LNode *p;
    p = MakeNode_L(e); 
    if(p == NULL)return ERROR;                // 内存溢出,结点创建不成功,不能执行插入操作
    p->next = L->next;                        // 修改q结点的指针域
    L->next = p;
    return OK;
}

// 7、删除单链表的第 i 个结点
Status ListDelete(LinkList &L,int i,ElemType &e){
    LNode *p, *q;
    p = L;
    int j = 0;
    while(p!=NULL && j<i-1){                    // 如果存在,找到第 i-1 个结点
        p = p->next;
        j++;
    }
    if(p==NULL||p->next==NULL)return ERROR;        // 如果第 i-1 个结点和第 i 个结点不存在,无法删除,返回错误 
    q = p->next;                                  // 用指针 q 指向第 i 个结点
    e = q->data;                                  // 将第 i 个结点的值赋给 e
    p->next = q->next;                          // 将 q 的后继赋值给 p
    free(q);                                    // 释放结点 q 所占空间 
    return OK;
}

// 8、单链表的查找元素操作
LNode* Search_L(LinkList L, ElemType e) {
    LNode *p;
    if(L == NULL) return NULL;
    p = L->next;
    while(p!=NULL && p->data.calculate != e.calculate) {
        p = p->next;   // 访问下一个元素
    }
    return  p;
}

// 9、在有序单链表中查找插入位置 
LNode* SearchPosition_L(LinkList L, ElemType e) {
    LNode *p, *q;
    if(L == NULL) return NULL;
    p = L;                    // p 用来保存插入位置的前一个结点 
    q = p->next;                // q 指向插入点对应的结点,即在插入点之前插入 e     
    while(q!=NULL && q->data.calculate<e.calculate) {
        p = q;                 // 寻找向后继续进行,p、q 均向后移动 
        q = p->next;           // 访问下一个元素
    }
    return  p;
}
// 10、在有序表中的正确位置插入结点 e
Status InsertNode_L(LinkList &L, ElemType e){
    if(L == NULL) return PERROR;              // 参数不合理
    LNode *p, *q;
    q = MakeNode_L(e);     
    if(p = NULL)return ERROR;                // 内存溢出,结点创建不成功,不能执行插入操作
    p = SearchPosition_L(L, e);
    q->next = p->next;                        // 修改q结点的指针域
    p->next = q;
    return OK;
}

// 11、求链表的长度
Status GetListLength_L(LinkList L) {
    int length = 0;
    LNode *p;
    if(L == NULL) return PERROR;              // 参数不合理
    if(L->next == NULL)return 0;
    p = L->next;
    while(p!=NULL){
        length++;
        p = p->next;
    }
    return length;
}

// 12、遍历单链表
Status ListTraverse_L(LinkList L, Status(*visit)(ElemType e))  {
    if(L == NULL) return PERROR;                  // 参数不合理
    if(L->next == NULL)return ERROR;              // 空表不执行操作 
    LNode *p;
    p = L->next;
    while (p != NULL) {
        visit(p->data);
        p = p->next;
    }
    return OK;
}
// 13、配合遍历,依次输出表中元素 
Status PrintList_L(ElemType e){
    printf("%d\t",e.calculate);
    return OK;
}

Status searchPath(int *paramap,int n,int m) {
    int i, j;                                        // 声明循环用变量
    ElemType e, temp;                                 // 元素类型变量
    LinkList L;                                        // 创建队列 Q
    int distance = -1;                                // 距离起点的距离,无方案时距离为 -1

    // 从地图参数数组里获取相应信息,以提高程序可读性
    int map[n][m],result[n][m];                                  // 二维地图
    int start_row, start_col, end_row, end_col;     // 地图起点和终点信息
    for(i=0; i<n; i++) {
        for(j=0; j<m; j++) {
            result[i][j] = 0;
            map[i][j] = paramap[i*m+j];                // 解析参数
            if(map[i][j]==-2) {
                start_row = i;                        // 获取地图的起点信息
                start_col = j;
            } else if(map[i][j]==-3) {
                end_row = i;                        // 获取地图的终点信息
                end_col = j;
            }
        }
    }

    InitList_L (L);                                        // 初始化队列 Q
    e.row = start_row;                                        // 从起点开始
    e.col = start_col;
    e.f = 0;
    e.calculate = abs(end_row - start_row) + abs(end_col - start_col);
    e.move_mode = 0;
    InsertFirst_L(L, e);
    while(FALSE == ListEmpty_L(L)) {                        // 队列非空执行循环
        ListDelete(L, 1, e);
        if((end_row == e.row)&&(end_col == e.col)) {            // 是目标则退出
            printf("已成功为您找到方案!\n");
            distance = e.f;
            printf("最小距离为:%d \n", distance);
            break;
        }
        // 往上扩展结点
        if(e.row>0) {
            temp.row = e.row -1;
            temp.col = e.col;
            temp.f = e.f + 1;
            temp.calculate = abs(end_row - temp.row) + abs(end_col - temp.col);
            temp.move_mode = 2;
            if(map[temp.row][temp.col] == 0) {        // 未访问过的结点
                map[temp.row][temp.col] = temp.f;
                result[temp.row][temp.col] = temp.move_mode;
                InsertNode_L(L, temp);    // 将子结点加入链表 
            } else if(map[temp.row][temp.col] == -3) {
                result[temp.row][temp.col] = temp.move_mode;
                InsertNode_L(L, temp);    // 将子结点加入链表
            }
        }
        // 往右扩展结点
        if(e.col < m-1) {
            temp.row = e.row;
            temp.col = e.col + 1;
            temp.f = e.f + 1;
            temp.calculate = abs(end_row - temp.row) +abs(end_col - temp.col);
            temp.move_mode = 3;
            if(map[temp.row][temp.col] == 0) {        // 未访问过的结点
                map[temp.row][temp.col] = temp.f;
                result[temp.row][temp.col] = temp.move_mode;
                InsertNode_L(L, temp);    // 将子结点加入链表
            } else if(map[temp.row][temp.col] == -3) {
                result[temp.row][temp.col] = temp.move_mode;
                InsertNode_L(L, temp);    // 将子结点加入链表
            }
        }
        // 往下扩展结点
        if(e.row < n-1) {
            temp.row = e.row + 1;
            temp.col = e.col;
            temp.f = e.f + 1;
            temp.calculate = abs(end_row - temp.row) +abs(end_col - temp.col);
            temp.move_mode = 1;
            if(map[temp.row][temp.col] == 0) {        // 未访问过的结点
                map[temp.row][temp.col] = temp.f;
                result[temp.row][temp.col] = temp.move_mode;
                InsertNode_L(L, temp);    // 将子结点加入链表
            } else if(map[temp.row][temp.col] == -3) {
                result[temp.row][temp.col] = temp.move_mode;
                InsertNode_L(L, temp);    // 将子结点加入链表
            }
        }
        // 往左扩展结点
        if(e.col > 0) {
            temp.row = e.row;
            temp.col = e.col - 1;
            temp.f = e.f + 1;
            temp.calculate = abs(end_row - temp.row) +abs(end_col - temp.col);
            temp.move_mode = 4;
            if(map[temp.row][temp.col] == 0) {        // 未访问过的结点
                map[temp.row][temp.col] = temp.f;
                result[temp.row][temp.col] = temp.move_mode;
                InsertNode_L(L, temp);    // 将子结点加入链表
            } else if(map[temp.row][temp.col] == -3) {
                result[temp.row][temp.col] = temp.move_mode;
                InsertNode_L(L, temp);    // 将子结点加入链表
            }
        }
    }
    if((true == ListEmpty_L(L))&&( -1 == distance)) {    // 仿制恰巧目标节点出队后队空的情况
        printf("当前地图无法到达指定目的地!\n");
        return ERROR;
    }

    int path[distance];                    // 用以存储动作方案 
    i = distance;
    path[distance-1] = 0;
    int p = e.row;
    int q = e.col;
    while(result[p][q]!= 0) {                // 导出动作序列,以生成行动方案 
        i--;
        if(result[p][q] == 1) {
            path[i] = 2;
            p--;
        } else if(result[p][q] == 2) {
            path[i] = 1;
            p++;
        } else if(result[p][q] == 3) {
            path[i] = 4;
            q--;
        } else if(result[p][q] == 4) {
            path[i] = 3;
            q++;
        }
    }
    printf("移动方案为:\n");
    for(i=0; i<distance; i++) {
        if(path[i]==1) {
            printf("第 %d 步 :向上\n", i+1);
        } else if(path[i]==2) {
            printf("第 %d 步 :向下\n", i+1);
        } else if(path[i]==3) {
            printf("第 %d 步 :向左\n", i+1);
        } else if(path[i]==4) {
            printf("第 %d 步 :向右\n", i+1);
        }
    }
    printf("\n");
    /* 输出地图填充计算分析过程 */
    printf("地图计算过程分析如下:\n");
    for(i=0; i<n; i++){                // 将方案传回
        for(j=0; j<m; j++){
            printf("%d\t", map[i][j]);
        }
        printf("\n");
    }
    // 将地图填充计算分析过程传回
    for(i=0; i<n; i++) {
        for(j=0; j<m; j++) {
            paramap[i*m+j] = map[i][j];
        }
    }
    DestroyList_L(L);
    return OK;
}

int main() {
    int i, j;
    int n = 8;                        // 声明地图方格行数
    int m = 10;                        // 声明地图方格列数
    int map[n][m];                    // 声明地图方格阵列
    int para[n*m];                     // 传参用一维数组

    for(i=0; i<n; i++) {                // 初始化地图方格阵列,0 表示距离为 0;
        for(j=0; j<m; j++) {
            map[i][j] = 0;
        }
    }
    map[3][3] = -2;            // 定义起点位置,为测试方便,写死了
    map[2][9] = -3;            // 定义终点位置,为测试方便,写死了
    // 定义障碍物位置
    map[2][6] = map[3][6] = map[4][6] = map[3][5] = map[4][2] = map[5][2] = map[6][2] = -1;

    // 将二维地图数组转换为一维数组,方便传参
    for(i=0; i<n; i++) {
        for(j=0; j<m; j++) {
            para[i*m+j] = map[i][j];
        }
    }
    searchPath(para,n,m);    
    return 0;
}

程序运行结果截图

猜你喜欢

转载自blog.csdn.net/tangguofeng/article/details/143018908