数据结构 · 图

本文介绍了鸿蒙乾坤矩阵型图算法的三代版本,包括基础编辑(点、边操作、图型切换)、辅助功能(查找、查重、孤立点判断)和进阶功能(最小生成树、广搜深搜、最短路径)。讨论了存在的问题和正在研发的SPFA等技术。
摘要由CSDN通过智能技术生成

当前主要版本

第一代· 无向矩阵型(鸿蒙·乾坤Day67)(DEV-CPP)测试版本

        部分高级功能缺失,是探索性模板,由于宏观设计不合理,已经停止维护

第二代·综合矩阵型(鸿蒙·乾坤Day107)(VSCode)集成版本

        可切换【有向/无向】,更优化的功能
        当前问题:矩阵太小,大了程序会崩;存在部分bug
        正在研究:SPFA等拓展

VSC对switch的缩进怎么这么恶心

第三代·邻接表·动态内存型(寒假开发)旗舰版本(已经咕咕)

主要功能·二代

        一、基础编辑

                1.创建·清空 全图

                2.点:新建·删除·修改(名称)·探测(邻接情况)【a到a bug】

                3.边:新建·删除·修改(权值)【负权值 bug】

                4.切换:有向图/无向图

                5.更迭:启动点(用于启发BFS/DFS)

        二、辅助功能

                1.Find:输入【点·名称】,返回在点表中的【位置】

                2.Same:【点·名称】查重(寒假开发)

                3.判断【孤立点】【存在回环】?(寒假开发)

        三、进阶功能

                1.最小生成树(无向图时启动):Prim/Kruskal(寒假·后者有bug·需要并查集知识)

                2.广搜/深搜:BDS/DFS(寒假)有向图更迭启动点后出现bug

                3.最短路径:Dijkstra/Floyd【显示路径】

储备功能·三代旗舰

        1.删除点(动态内存维护malloc free)

        2.对vector容器、STL库queue stack等应用

        3.SPFA、拓扑排序、关键路径

        4.更好的视图

测试截图

感谢网上大佬帮我搭建的VSC环境!C++和Python~之前好像有发现网友开发的
一键搭建VSC C++环境工具包,我寒假一定要给他找回来(工具包被我删了)

 

T2型-矩阵·综合(先进版本,正在开发)

#include <iostream>
#include <queue>
#include <stack>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <math.h>
#include <malloc.h>
#include <string.h>
#include <vector>
//#include <bits/stdc++.h>
#define maxx 0x3f3f3f3f
#define minn (1 << 31)
#define max 100 //过大会导致函数崩溃,可能是内存过载导致
using namespace std;

//基本数据部署
bool v[max];
bool Direction = true; //默认为有向图
int dis[max], from[max];
bool use[max];
int A[max][max], cost;    //存当前的最短路径长度
int Path_Floyd[max][max]; //存递归需要的回溯路径

//结构体数据部署
typedef struct G //【综合·有向图·矩阵】
{
    char name[max][27]; //顶点名称
    int map[max][max];  //边·矩阵
    int edge, node, start;
};
typedef struct T //【Prim算法·辅助】
{
    int minDis, parent;
};
typedef struct kus
{ //克鲁斯卡尔辅助数组
    int begin, end;
    int weight;
};
vector<kus> KU; //【科研突破!矢量容器】
G a;

//辅助功能
bool pd1(kus a, kus b) // sort判断【边权值,从小到大】小的往前走
{
    return a.weight < b.weight;
}
bool init(G &a) //初始化
{
    a.edge = 0;
    a.node = 0;
    memset(a.name, 0, sizeof(a.name));
    int x, y, z;
    for (x = 0; x < max; x++)
        for (y = 0; y < max; y++)
            a.map[x][y] = maxx;
    a.start = 0;
    return true;
}
bool find(G a, char t[], int &e) //结点位置追踪
{
    int x, y, z;
    for (x = 0; x < a.node; x++)
    { //对x位置进行检查
        z = 1;
        for (y = 0; y < strlen(a.name[x]); y++) //对各个字母进行检查
        {
            if (t[y] != a.name[x][y])
            {
                z = 0;
                break;
            }
        }
        if (z == 1)
        {
            e = x;
            return true;
        }
    }
    printf("结点名称异常!(%s)请重新输入\n", t);
    e = -1;
    return false;
}
bool same(G a, char t[]) //结点名称查重
{                        //寒假处置
    return false;
}
void path(G a, int now) //迪杰斯特拉算法路径1
{
    if (from[now] == -1)
        return;
    path(a, from[now]);
    printf("%s->", a.name[from[now]]);
}
void init_Floyd(int z, G a)
{
    int x, y;
    for (x = 0; x < z; x++)
        for (y = 0; y < z; y++)
        {
            A[x][y] = a.map[x][y];
            Path_Floyd[x][y] = -1;
        }
}
void find_path_Floyd(int x, int y, G a)
{
    // printf("追踪!\n");
    if (Path_Floyd[x][y] == -1)
    {
        printf("(%s-->%s) ", a.name[x], a.name[y]);
        cost += a.map[x][y];
    }

    else
    {
        int mid = Path_Floyd[x][y];
        find_path_Floyd(x, mid, a);
        find_path_Floyd(mid, y, a);
    }
}

//地图编辑
bool crate(G &a) //创建图
{
    int x, y, z, z1, z2;
    char t1[27], t2[27];

    printf("1.宏观部署!\n  n个点m条边:");
    scanf("%d%d", &a.node, &a.edge);

    printf("2.基础部署·点!\n");
    for (x = 0; x < a.node; x++)
    {
        printf("  输入第%d点名称:", x + 1);
        scanf("%s", &t1);
        if (same(a, t1))
            x--;
        else
            strcpy(a.name[x], t1);
    }

    printf("3.基础部署·边!\n");
    for (x = 1; x <= a.edge; x++)
    {
        z1 = z2 = 0;
        memset(t1, 0, sizeof(t1));
        memset(t2, 0, sizeof(t2));
        printf("  %3d:输入【两点名称】+【权值】:", x);
        scanf("%s%s%d", &t1, &t2, &y);
        if (!find(a, t1, z1) || !find(a, t2, z2))
        {
            x--;
            continue;
        }
        if (a.map[z1][z2] != maxx)
            printf("覆盖为");
        printf("    map[%d][%d]=%d\n", z1, z2, y);
        if (Direction)
            a.map[z1][z2] = y;
        else //无向图
        {
            kus b; //为克鲁斯卡尔算法部署边
            b.begin = z1;
            b.end = z2;
            b.weight = y;
            KU.push_back(b);   //塞进容器
            a.map[z1][z2] = y; //矩阵部署边
            a.map[z2][z1] = y;
        }
    }

    printf("4.最终部署·启动点!\n");
    do
    {
        scanf("%s", &t1);
    } while (!find(a, t1, z));
    printf("部署成功!  %s - a.name[%d]\n", a.name[z], z);
    a.start = z;
    return true;
}
bool change_node(G &a)
{
    int x, y, z;
    char t[27];
    printf("输入您想修改的结点名称:");
    do
    {
        scanf("%s", &t);
    } while (!find(a, t, z));
    printf("输入新节点名称:");
    scanf("%s", &t);
    memset(a.name[z], 0, sizeof(a.name[z]));
    strcpy(a.name[z], t);
    return true;
}
bool change_edge(G &a)
{
    int x, y, z1, z2;
    char t1[27], t2[27];
    printf("输入两个结点以确定一条边:\n");
    printf("   起点名称:");
    do
    {
        scanf("%s", &t1);
    } while (!find(a, t1, z1));
    printf("   终点名称:");
    do
    {
        scanf("%s", &t2);
    } while (!find(a, t2, z2));
    printf("   部署权值(输入0为删除):");
    scanf("%d", &x);
    if (x == 0)
        x = maxx;
    printf("map[%d][%d]=%d\n", z1, z2, x);
    if (Direction)
        a.map[z1][z2] = x;
    else
    {
        a.map[z1][z2] = x;
        a.map[z2][z1] = x;
    }
    return true;
}
bool add_node(G &a)
{
    if (max == a.node)
    {
        printf("已饱和!添加失败。");
        return false;
    }
    printf("输入新节点名称:");
    scanf("%s", &a.name[a.node]);
    printf("第%d个结点[%s],已部署至a.name[%d]!\n", a.node + 1, a.name[a.node], a.node);
    a.node++;
    return true;
}
void search(G a)
{
    printf("输入您想探测的点名称:");
    int x, y, z;
    char t[27];
    scanf("%s", &t);
    if (find(a, t, z))
    {
        printf("此点【起点】,可以【前往】的地点:\n");
        y = 0;
        for (x = 0; x < a.node; x++)
            if (a.map[z][x] != maxx)
                printf("%d:%s\n", ++y, a.name[x]);
        printf("\n");

        printf("此点为【终点】,可以由以下点【抵达】:\n");
        y = 0;
        for (x = 0; x < a.node; x++)
            if (a.map[x][z] != maxx)
                printf("%d:%s\n", ++y, a.name[x]);
        printf("\n");
    }
}

//高级功能
bool dfs2(G a, int w)
{
    printf("%3s ", a.name[w]);
    v[w] = true;
    for (int x = 0; x < a.node; x++)
        if (a.map[w][x] != maxx && !v[x])
            dfs2(a, x);
    return true;
}
bool DFS(G a)
{
    int x, y, z;
    for (x = 0; x < a.node; x++)
        v[x] = false; //初始化标记未访问过
    // printf("%d",a.node);
    for (x = 0; x < a.node; x++)
    {
        if (!v[x])
            dfs2(a, a.start);
    }

    return true;
}
bool BFS(G a, int w)
{
    queue<int> Q;
    int x, y, z;
    for (x = 0; x < a.node; x++)
        v[x] = false;
    printf("%3s ", a.name[w]);
    v[w] = true;
    Q.push(w); //当前点入队,准备对下属层的点进行遍历
    while (!Q.empty())
    { //遍历完成才会跳出,全跳出技术
        y = Q.front();
        Q.pop();                     //出队赋值,开始遍历
        for (x = 0; x < a.node; x++) //以Y为依托,遍历进攻
        {
            if (a.map[y][x] != maxx && !v[x])
            {
                printf("%3s ", a.name[x]);
                v[x] = true;
                Q.push(x); //入队,等会从这里开始打印
            }
        }
    }
    return true;
}
bool Prim(G a) //树集合,邻接边选小入,扩增点
{
    bool n[max]; //标记·是否处于【最小生成树中】
    T we[max];
    //辅助·【minDis】·与【生成树】邻接的最小边    【parent】·这个点最小值的【更迭】来自于谁【两点确定一条线】
    int sum = 0, x, y, k, min;
    // xy临时数据,sum是对边权值进行累加,min负责筛选更新后点
    for (x = 0; x < a.node; x++)
    {
        n[x] = false;
        we[x].parent = a.start;
        we[x].minDis = a.map[a.start][x];
    }
    n[a.start] = true;

    for (x = 1; x < a.node; x++)
    {
        min = maxx;
        for (y = 0; y < a.node; y++) //【物色·下一个加入点】
        {
            if (n[y] == false && we[y].minDis < min) //【没被选过+数值更小】
            {                                        //如果【此点可选】,且【记录min】更小
                min = we[y].minDis;
                k = y; //更新min所处位置
            }          //更新min的值  为了筛选 下一个【加入树の点】
        }
        printf("<%s,%s>  ", a.name[k], a.name[we[k].parent]);
        sum += we[k].minDis;
        n[k] = true;
        for (y = 0; y < a.node; y++)
        {
            if (n[y] == false && a.map[k][y] < we[y].minDis)
            {
                we[y].parent = k;
                we[y].minDis = a.map[k][y];
            }
        }
        // minDis表维护完成
    }
    printf("All_Cost=%d\n", sum);
    return true;
}
bool Kruskal(G a) //边排序,选小入,去回环
{                 //有bug,技术能力不够,暂时不修复,寒假处置
    sort(KU.begin(), KU.end(), pd1);
    int loop[max], sum = 0, n = 0; //统计边的权值·个数
    int x, y, z1, z2;
    for (x = 0; x < a.node; x++)
        loop[x] = x;

    for (x = 0; x < a.node; x++)
    {
        if (n + 1 == a.node)
            break;
        z1 = loop[KU[x].begin];
        z2 = loop[KU[x].end];
        if (z1 != z2)
        {
            printf("<%s,%s> ", a.name[KU[x].begin], a.name[KU[x].end]);
            sum += KU[x].weight;
            for (y = 0; x < a.node; y++)
            {
                if (loop[y] == z2)
                    loop[y] = z1;
            }
            n++;
        }
    }
    printf("All_Cost=%d\n", sum);
    //可能要用到并查集,现阶段技术不够
    return true;
}
bool Dijkstra(G a)
{

    int n = a.node, min;
    int k, x, y, z, now = a.start;
    for (x = 0; x < n; x++)
    {
        use[x] = false;
        dis[x] = a.map[now][x];
        if (dis[x] != maxx)
            from[x] = now;
        else
            from[x] = -1;
    }
    use[now] = true;
    dis[now] = 0;
    //初始化结束

    for (x = 1; x < n; x++) //战略指挥!执行n-1次
    {
        min = maxx;
        for (y = 0; y < n; y++)          //寻找最小点k
            if (!use[y] && dis[y] < min) //没用过且dis更小
            {
                k = y;
                min = dis[y];
            }
        use[k] = true; //标记k,作为下一次攻击启动点

        //更新从源点触发到其他个点的最短路径
        for (y = 0; y < n; y++)
        {
            if (!use[y] && dis[k] + a.map[k][y] < dis[y])
            //【bug修复】:maxx+6就变成负数了,会判定为最小值
            { //因为int (1<<31)-1再加就变成负数溢出
                //而0x3f3f3f3f差不多是max的一半,常规输入不会溢出
                dis[y] = dis[k] + a.map[k][y];
                printf("k=%d y=%d  dis[y]=%d\n", k, y, dis[y]);
                from[y] = k; //标记攻击来源点
            }
        }
    }

    //打印模块
    printf("目标点\t最短路径值\t最短路径\n");
    for (x = 0; x < a.node; x++)
    {
        if (x != now)
        {
            printf("  %s\t        %d\t", a.name[x], dis[x]);
            path(a, x);
            printf("%s\n", a.name[x]);
        }
    }
    return true;
}
bool Floyd(G a)
{
    init_Floyd(a.node, a); //初始化!path部署-1,map部署给A
    int x, y, z;
    for (z = 0; z < a.node; z++) //选【中间点】,尝试插入
    {
        for (x = 0; x < a.node; x++)
        {
            for (y = 0; y < a.node; y++)
            {
                if (x == y || z == x || z == y)
                    continue;

                if (A[x][y] > A[x][z] + A[z][y])
                {
                    A[x][y] = A[x][z] + A[z][y];
                    Path_Floyd[x][y] = z;
                }
            }
            // cout << endl;
        }
        // printf("\n\n");
    }
    /*
    关于path=-1的意义:说明有直接的边相连
    */
    return true;
}

//视觉模块
bool map(G a)
{
    int x, y;
    printf("    ");
    for (x = 0; x < a.node; x++)
        printf(" %3s", a.name[x]);
    printf("\n");
    for (x = 0; x < a.node; x++)
    {
        printf(" %3s", a.name[x]);
        for (y = 0; y < a.node; y++)
        {
            if (a.map[x][y] == maxx)
                printf("   0");
            else
                printf(" %3d", a.map[x][y]);
        }
        printf("\n");
    }
    return true;
}
bool menu(G a, int &x) //菜单
{
    system("cls");
    if (a.node != 0)
    {
        printf("图·矩阵汇报\n");
        map(a);
        printf("\n");
        printf("DFS:");
        DFS(a);
        printf("\n");
        printf("BFS:");
        BFS(a, a.start);
        printf("\n");
        if (!Direction)
        {
            printf("最小生成树·Prim:");
            Prim(a);
            printf("\n");
            printf("最小生成树·Kruskal:");
            // Kruskal(a);
            printf("\n");
        }
    }
    // DFS
    // BFS
    //最小生成树  Prim算法(依边选点)
    //最小生成树  Kruskal算法(依点选边)·不产生回路
    if (Direction)
        printf("\n\n《有向图·矩阵·综合实现》  鸿蒙纪元·乾坤Day107\n");
    else
        printf("\n\n《无向图·矩阵·综合实现》  鸿蒙纪元·乾坤Day107\n");
    printf("1-创建图   2-修改·添加·删除边   3-修改点名称\n");
    printf("4-最短路径Dijkstra             5-最短路径Floyd \n");
    printf("6-初始化                       7-添加新点\n");
    printf("2745-退出                  5257-有/无 向图切换     \n");
    printf("2333-启动点更迭                8-点·信息探测\n");
    //删除模块对矩阵不友好,时间有限,实践算法有限,决定抛弃
    printf("输入您的选择:");
    scanf("%d", &x);
    if (x == 2745)
        return false;
    if (x == 5257)
        Direction = !Direction;
    return true;
}

/*测试数据T1(顺推探测·测试最短路径)
1 8 11
a b c d e f g h
a h 1  a b 2  b c 3  c d 4
h f 5  b f 6  b e 7  e c 8
d g 9  f g 10  h d 11
a

*/
/*测试数据T2(树型结构·测试遍历D/BFS)
1 19 18
a b c d e f g h i j k l m n o p q r s
a b 1  a c 1  a d 1  d e 1  c q 1  d m 1
d n 1  d o 1  d p 1  q r 1  r s 1  e l 1
e f 1  f k 1  f g 1  g h 1  h i 1  g j 1
a

*/
/*测试数据T3(无向图·测试生成树)
5257
1 8 11
a b c d e f g h
a h 1  a b 2   b c 3  c d 4
h f 5  b f 6   b e 7  e c 8
d g 9  f g 10  h d 11
a

*/
/*测试数据T4(佛洛依德异常情况测试)
1 4 8
0 1 2 3
0 1 5  2 0 3  0 3 7  1 3 1
1 2 4  2 1 3  2 3 2  3 2 1
0
*/

int main()
{
    int x, y, z;
    init(a);
    char t[27], tt[27];
    while (menu(a, x))
    {
        switch (x)
        {
        case 1:
        {
            crate(a);
            break;
        }
        case 2:
        {
            change_edge(a);
            break;
        }
        case 3:
        {
            change_node(a);
            break;
        }
        case 4:
        {
            Dijkstra(a);
            break;
        }
        case 5:
        {
            Floyd(a);
            printf("Path情况汇报:\n");
            for (int s1 = 0; s1 < a.node; s1++)
            {
                for (int s2 = 0; s2 < a.node; s2++)
                {
                    printf("%3d ", Path_Floyd[s1][s2]);
                }
                cout << endl;
            }
            cout << endl;
            printf("Floyd计算完成!\n");
            do
            {
                printf("输入需要查询【最短路径】の两点名称:");
                scanf("%s%s", &t, &tt);
            } while (!find(a, t, y) || !find(a, tt, z));
            printf("<%s,%d>  ===>  <%s,%d>追踪启动!\n", a.name[y], y, a.name[z], z);
            cost = 0;

            find_path_Floyd(y, z, a);
            printf("总长度:%d\n", cost);
            break;
        }
        case 6:
        {
            init(a);
            break;
        }
        case 7:
        {
            add_node(a);
            break;
        }
        case 8:
        {
            search(a);
            break;
        }
        case 5257:
        {
            printf("更迭进化!");
            break;
        }
        case 2333:
        {
            printf("启动点更迭!输入新启动点名称:");
            do
            {
                scanf("%s", &t);
            } while (!find(a, t, z));
            printf("部署成功!  %s - a.name[%d]\n", a.name[z], z);
            a.start = z;
            break;
        }
        default:
            printf("输入异常!");
        }

        system("pause");
    }
    system("pause");
    return 0;
}

T1型-无向矩阵(已停止维护)

测试数据 T2基础图形

(孤立点:德玛西亚   启动点:a

1 9 11 a b c d e f g h 德玛西亚 a h 1 a b 2 b c 3 c d 4 h f 5 f b 6 e b 7 c e 8 g d 9 f g 10 h d 11 a 

测试数据 T3·三叉树

(无·孤立点 启动点:a)(图没了懒得画
1 12 11 a b c d e f g h i j k L a b 1 a c 1 a d 1 b e 1 b f 1 f g 1 f h 1 d i 1 i j 1 i k 1 i L 1 a    

#include <iostream>
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stack>
#include <queue>
#include <stdlib.h>
#include <algorithm>
#define max 2745
#define maxx (1 << 31) - 1
using namespace std; //以0为INFINITY,不知道为什么memset对infinity会赋值为-1
typedef struct G
{						// Graph
	char node[max][27]; //点·符号
	int n, e;			//点、边·个数
};
typedef struct FZ
{			 // Prim算法所用数据结构
	int x;	 //最小边在集合U(最小边在当前子树顶点集合中的那个顶点的下标)
	int min; //最小边上的权值
};
int map[max][max] = {}, nmax = 0, star = 0, alone = 0; //矩阵·权值记录;启动点·用于启动DFS和 拓扑排序;存在几个孤立点
int n[max][max];									   //《佛洛依德·路径矩阵》
bool v[max], LT;									   // DFS访问标志
//综合图·三叉树·连通图
// 1 9 11 a b c d e f g h 德玛西亚 a h 1 a b 2 b c 3 c d 4 h f 5 f b 6 e b 7 c e 8 g d 9 f g 10 h d 11 a
// 1 12 11 a b c d e f g h i j k L a b 1 a c 1 a d 1 b e 1 b f 1 f g 1 f h 1 d i 1 i j 1 i k 1 i L 1 a
// 1 8 11 a b c d e f g h a h 1 a b 2 b c 3 c d 4 h f 5 f b 6 e b 7 c e 8 g d 9 f g 10 h d 11 a
//【正在开发!鸿蒙纪元·乾坤Day62--Day】
//《佛洛依德路径·Kruskal》

//《判断辅助·功能区》
bool pd1(int t1, int t2, int t)
{ //《佛洛依德·矩阵更迭判断》
	if ((t1 != 0 && t2 != 0) && ((t1 + t2 < t && t != 0) || (t == 0)))
		return true;
	else
		return false;
}
bool pd2(G a, int x)
{ //《判断·是否删除点》
	if (a.node[x][0] == '\0')
		return true;
	else
		return false;
}
bool loop(G a)
{ //《判断·存在回路?》
	return true;
}
bool empty(G a)
{ //《判断·空图》
	if (a.n == 0 && a.e == 0)
		return true;
	else
		return false;
}
bool find(G a, char t[], int &x)
{					 //《寻找·依结点名称·返回char中的存储位置》
	int y, z, s = 0; // 0没找到
	for (y = 1; y <= a.n; y++)
	{
		s = 1; //默认找到
		for (z = 0; z <= strlen(a.node[y]); z++)
			if (a.node[y][z] != t[z])
			{		   //出现不同
				s = 0; //标记不同
				break;
			}
		if (s == 1)
		{ //发现位置!
			x = y;
			return true;
		}
	}
	printf("结点名称异常!(%s)请重新输入\n", t);
	return false;
}
bool wrong(char a[])
{ //《异常输入阻断》
	char b[20] = "Teloy_SXH";
	for (int x = 0; x < strlen(a); x++)
		if (b[x] != a[x])
			return false;
	return true;
}

//《强化算法·功能区》
bool dfs(G a, int w)
{ //《DFS·递归执行》
	if (!v[w])
		printf("%s ", a.node[w]);
	v[w] = true;
	for (int x = 1; x <= nmax; x++)
		if (map[w][x] != 0 && !v[x] && !pd2(a, x)) //有通路且没走过且空
			dfs(a, x);
	return true;
}
bool DFS(G a, int w)
{ //《DFS·指挥模块》
	for (int x = 1; x <= nmax; x++)
		v[x] = false; //初始化,都没访问过
	for (int x = 1; x <= nmax; x++)
		if (!v[x] && a.node[x][0] != '\0') //没访问过,且不是空点
			dfs(a, w);
	return true;
}
void BFS(G a, int w)
{				  //《BFS·直接执行》
	queue<int> Q; //对启动点单独处理
	int n = 1;
	for (int x = 1; x <= nmax; x++)
		v[x] = false;		  //初始化,都没访问过
	printf("%s ", a.node[w]); //打印
	v[w] = true;			  //标记访问
	Q.push(w);				  //入队!
	while (!Q.empty())
	{
		int y = Q.front();
		Q.pop(); //出队,赋值于y
		for (int x = 1; x <= nmax; x++)
		{ //遍历x是y下层所有点
			if (map[y][x] != 0 && !v[x] && !pd2(a, x))
			{
				printf("%s ", a.node[x]);
				v[x] = true;
				n++;
				Q.push(x);
			}
		} // 1 12 11 a b c d e f g h i j k L a b 1 a c 1 a d 1 b e 1 b f 1 f g 1 f h 1 d i 1 i j 1 i k 1 i L 1 a
	}
	if (n == a.n) //连通图
		LT = true;
	else
		LT = false;
}
bool Dijkstra(G a, int w1, int w2)
{ //《迪杰斯特拉·最短路径》· eloge
	printf("矩阵流,无法使用<迪杰斯特拉>最短路径探测算法!\n");
	return false;
}
bool Floyd(G a, int w1, int w2)
{													 //《佛洛依德·最短路径》·n^3·
	printf("最短路径探测!<佛洛依德>算法(Floyd)\n"); //意识:从w1到w2
	int m[nmax + 1][nmax + 1], x, y, z, t1, t2, t3;
	for (x = 1; x <= nmax; x++)
		for (y = 1; y <= nmax; y++)
		{
			m[x][y] = map[x][y];
			//迭代图·复制·初始化
		}
	//《初始化·完成》

	//《算法启动》
	for (x = 1; x <= nmax; x++)
	{ //遍历n次·插入点
		if (pd2(a, x))
			continue;
		for (y = 1; y <= nmax; y++)
		{ //出发点
			if (pd2(a, y) || y == x)
				continue;
			for (z = 1; z <= nmax; z++)
			{ //终点
				if (pd2(a, z) || z == x || z == y)
					continue;
				//思想,从y到z,插入x是否可行(条件保障·点存在·三点不重复)
				if (pd1(m[y][x], m[x][z], m[y][z])) //没路但是可以连接,或者,有更短路且直连不是0
					m[y][z] = m[y][x] + m[x][z];	//插入x作为中转
			}
		}
	}
	//《算法·结束》

	if (m[w1][w2] == 0)
	{
		printf("两点间不存在路径!\n");
		return false;
	}
	else
	{
		printf("最短路径长度:%d\n路径追踪:", m[w1][w2]);
		printf("\n\n");
		return true;
	}
}
bool Prim(G a)
{ //《普利姆·最小生成树》
	FZ T[max];
	int x, y, z;
	int i, j, k; //辅助数组T   上边存的是他老爹是谁,下面一个存的是最小边的权值
	int go = star - 1, sum = 0, min;
	int gg[a.n][a.n] = {};
	for (x = 0; x < a.n; x++)
	{ //《生成·新体系》·对角线全=0·0区改maxx
		for (y = 0; y < a.n; y++)
			if (map[x + 1][y + 1] == 0 && x != y)
				gg[x][y] = maxx;
			else
				gg[x][y] = map[x + 1][y + 1];
	}
	//《初始化·完成》

	//《算法·启动》
	for (x = 0; x < nmax; x++)
	{ //《初步·启动点加入生成树》
		T[x].x = go;
		T[x].min = gg[go][x];
	}
	for (x = 1; x < nmax; x++)
	{ //《探索!剩余n-1个点!》
		if (pd2(a, x + 1))
			continue;
		min = maxx;
		for (y = 0; y < nmax; y++)
		{ //【寻找·辐射点·权值最小边】辐射点·与已生成的树直接连接的点
			if (pd2(a, y + 1))
				continue;
			if (T[y].min != 0 && T[y].min < min)
			{ //可选且权值更小(进入生成树的点会标注0!!)
				min = T[y].min;
				k = y; //记录 权值 地址
			}
		}
		printf("<%s,%s>", a.node[k + 1], a.node[T[k].x + 1]); //进入·打印
		sum += T[k].min;
		z++;
		for (y = 0; y < nmax; y++)
		{ //《加入·更迭辅助区》
			if (pd2(a, y))
				continue;
			if (T[y].min != 0 && gg[k][y] < T[y].min)
			{				//目标点不在树内 且 (由新加入点到目标点)地图路径更短
				T[y].x = k; //更迭辅助区
				T[y].min = gg[k][y];
			}
		} //叠加!边的权值
	}
	printf("总权值:%d\n", sum);
}
bool Kruskal(G a)
{ //《克鲁斯卡尔·最小生成树》·eloge
	printf("\n");
} //自适应选择,对比算法复杂度
bool top_sort(G a, int w)
{ //《拓扑排序》 (有向图)
	printf("无向图不实现!");
	return true;
}

//《基础控制·功能区》
bool init(G &a)
{ //《图·初始化》
	a.n = 0;
	a.e = 0;
	nmax = 0;
	star = 0; //最大矩阵长度,防止删点导致矩阵打印异常
	memset(a.node, '\0', sizeof(a.node));
	memset(map, 0, sizeof(map)); //初始化0,默认不存在路径
	return true;
}
bool mapp(G a)
{ //《打印·矩阵·结点对应情况》
	int x, y;
	printf("\n边·矩阵情况\n");
	for (x = 1; x <= nmax; x++)
	{
		for (y = 1; y <= nmax; y++)
			printf("%-3d ", map[x][y]);
		printf("\n");
	}
	printf("\n点·node情况\n");
	for (x = 1; x <= nmax; x++)
	{
		printf("%d\t", x);
		if (a.node[x][0] == '\0')
			printf("被删除的点(空位)\n");
		else
			printf("%s\n", a.node[x]);
	}
}
bool go_star(G a)
{ //《部署·启动点》
	int x;
	char t[20];
	do
	{
		printf("输入启动点:");
		memset(t, '\0', sizeof(t));
		scanf("%s", &t);
	} while (!find(a, t, x));
	star = x;
	printf("部署启动点<%s>于node[%d]!", a.node[x], x);
	return true;
}
bool create(G &a)
{ //《创建·全图》 清空后重建会出现构建bug
	if (!empty(a))
		return false;
	printf("几个点?几条边?");
	scanf("%d%d", &a.n, &a.e);
	if (a.e > a.n * (a.n - 1) / 2)
	{
		init(a);
		printf("无向图·边数溢出!\n");
		return false;
	}
	if (a.n < 1)
	{
		init(a);
		printf("无向图·结点非法!\n");
		return false;
	}
	int x, y, z, s;
	printf("输入结点名称:\n");
	for (x = 1; x <= a.n; x++)
	{
		printf("%d号结点:", x); //注释化!读入情况查看
		scanf("%s", a.node[x]);
	}
	/*printf("\n读入完成,汇报情况如下:\n"); //4 4 a b c d a b 1 a c 2 a d 3 b c 4
	for(x=1;x<=a.n;x++)
		printf("%d\t%s\n",x,a.node[x]);*/
	printf("\n\n");
	char t1[20] = {}, t2[20] = {};
	printf("部署无向边(俩结点名称·空格隔开+边权)\n");
	for (x = 1; x <= a.e; x++)
	{
		do
		{
			memset(t1, '\0', sizeof(t1));
			memset(t2, '\0', sizeof(t2));
			scanf("%s%s%d", &t1, &t2, &s);
		} while (!find(a, t1, y) || !find(a, t2, z) || map[y][z] != 0 || s == 0);
		//结点名称异常导致没找到,或者已经存在边!=1,或者边=0
		printf("      %s位于%d  %s位于%d  建立无向边权值=%d\n", t1, y, t2, z, s);
		map[y][z] = s;
		map[z][y] = s; //地图权值赋予
	}
	go_star(a);
	return true;
}
bool del1(G &a)
{ //《删除·点》
	printf("输入结点名称:");
	int x, y, z;
	char t1[20] = {};
	do
	{
		memset(t1, '\0', sizeof(t1));
		scanf("%s", &t1);
		if (wrong(t1))
			return false;
	} while (!find(a, t1, z));
	// 1 9 11 a b c d e f g h 德玛西亚 a h 1 a b 2 b c 3 c d 4 h f 5 f b 6 e b 7 c e 8 g d 9 f g 10 h d 11 a
	//结点名称异常导致没找到
	printf("%s位于%d,单点移除!\n", t1, z);
	memset(a.node[z], '\0', sizeof(a.node[z])); //全部置零
	for (x = 1; x <= a.n; x++)
	{
		if (map[x][z] != 0 && map[z][x] != 0)
		{ //行列全部置空,表示移除所有边
			map[x][z] = 0;
			map[z][x] = 0;
			a.e--;
		}
	}
	a.n--;
	return true; //结点数-1
}
bool del2(G &a)
{ //《删除·边》
	printf("输入俩结点名称:");
	int x, y, z;
	char t1[20] = {}, t2[20] = {};
	do
	{ // 1 9 11 a b c d e f g h 德玛西亚 a h 1 a b 2 b c 3 c d 4 h f 5 f b 6 e b 7 c e 8 g d 9 f g 10 h d 11 a
		memset(t1, '\0', sizeof(t1));
		memset(t2, '\0', sizeof(t2));
		scanf("%s%s", &t1, &t2);
		if (wrong(t1))
			return false;
	} while (!find(a, t1, y) || !find(a, t2, z));
	//结点名称异常导致没找到
	printf("%s位于%d  %s位于%d,两点间直连边删除!\n", t1, y, t2, z);
	map[y][z] = 0;
	map[z][y] = 0; //地图权值赋予
	a.e--;
	return true;
}
bool insert_edge(G &a)
{ //《插入·边》
	int n, s, x, y, z;
	char t1[20] = {}, t2[20];
	printf("输入修改条数:");
	scanf("%d", &n);
	printf("输入无向边(俩结点名称·空格隔开+边权)\n");
	for (x = 1; x <= n; x++)
	{
		do
		{
			memset(t1, '\0', sizeof(t1));
			memset(t2, '\0', sizeof(t2));
			printf("%d号边:", x);
			scanf("%s%s%d", &t1, &t2, &s);
		} while (!find(a, t1, y) || !find(a, t2, z) || s == 0);
		//结点名称异常导致没找到,或者已经存在边!=1,或者边=0
		printf("      %s位于%d  %s位于%d  建立无向边权值=%d\n", t1, y, t2, z, s);
		map[y][z] = s;
		map[z][y] = s; //地图权值赋予
	}
	return true;
}
bool insert_node(G &a)
{ //《插入·点》
	int x = 1, y, z;
	while (1)
	{
		if (strlen(a.node[x]) != 0)
			x++; // 1 4 4 a b c d a b 1 b c 4 a c 2 a d 3
		else
			break;
	}
	printf("探查到插入点:%d\n", x);
	printf("输入结点名称:");
	scanf("%s", a.node[x]);
	a.n++;
	return true;
}
bool trace_1(G a)
{ //《探测·单点》
	char t1[20] = {};
	int x, y, z = 0;
	printf("输入单点名称(必须存在):");
	do
	{
		memset(t1, '\0', sizeof(t1));
		scanf("%s", &t1);
	} while (!find(a, t1, y));
	printf("追踪到结点%s,存储区位于node<%d>\n", t1, y);
	for (x = 1; x <= nmax; x++)
		if (map[x][y] != 0 && !pd2(a, x))
		{ //不是被删除的点 且 路径存在
			printf("  连接结点%s,权值%d\n", a.node[x], map[x][y]);
			z++;
		}
	printf("共有%d条边;\n", z);
	return true;
}
bool trace_2(G a)
{ //《探测·双点》
	char t1[20] = {}, t2[20] = {};
	int x, y, z;
	printf("输入俩点名称(必须存在·空格隔开):");
	do
	{
		memset(t1, '\0', sizeof(t1));
		memset(t2, '\0', sizeof(t2));
		scanf("%s%s", &t1, &t2); // 1 8 11 a b c d e f g h a h 1 a b 2 b c 3 c d 4 h f 5 f b 6 e b 7 c e 8 g d 9 f g 10 h d 11
	} while (!find(a, t1, y) || !find(a, t2, z));
	printf("追踪到结点%s,存储区位于node<%d>\n追踪到结点%s,存储区位于a.node<%d>\n", t1, y, t2, z);
	printf("选择最短路径探测方式(Dijkstra/Floyd):");
	scanf("%d", &x);
	if (x == 1)
		Dijkstra(a, y, z);
	if (x == 2)
		Floyd(a, y, z);
	return true;
}
bool menu(int &x, G a)
{ //《菜单·图情况汇报》
	system("cls");
	float z;
	int y;
	alone = 0; // printf("%d\n",maxx);
	if (a.n > nmax)
		nmax = a.n;
	printf("【当前图·汇报】");
	if (!empty(a))
	{
		printf(":node=%d,edge=%d,", a.n, a.e);
		if (loop(a))
			printf("存在回路");
		else
			printf("不存在回路");
		if (!alone)
			printf(",是连通图");
		else
			printf(",非连通图,有%d个孤立点", alone);
		if (star != 0)
			printf(",启动结点<%s>于%d\n", a.node[star], star);
		else
			printf(",尚未设置启动结点\n");
		printf("\nDFS:");
		DFS(a, star);
		printf("\nBFS:");
		BFS(a, star);
		printf("\ntop_sort:");
		top_sort(a, star);
		z = a.e * log(a.e);
		y = (int)z;
		printf("\n最小生成树,node<%d> edge<%d>\n", a.n * a.n, y);
		if (LT)
		{
			printf("Kruskal:");
			Kruskal(a);
			printf("\nPrim:");
			Prim(a);
		}
		else
			printf("不是连通图,无法生成最小生成树!DFS、BFS不完全!!\n");
		mapp(a);
	}
	else
		printf("这是个空图!");

	printf("\n\n");
	printf("《最短路径  ·双点》:Dijkstra/Floyd算法(邻接表)(矩阵流)\n");
	printf("《最小生成树·全图》:Kruskal /Prim 算法(eloge) (n&2)【自适应】\n");
	printf("《拓扑排序  ·全图》\n\n");
	printf("1-创建      2-删除·点   3.删除·边   0-初始化\n");
	printf("4-启点更迭  5-探测·点   6-探测·双(最短路径)\n");
	printf("7-插入或修改边           8-插入点\n");
	printf("您选择:");
	scanf("%d", &x);
}

//『寒月竹影江浸灭,孤雁剑与暮风随』 影月·暮风
//『悠婉流莺花底滑,银霞笛曲荷风晚』 夏栀·荷风
int main()
{
	G a;
	int x, y, z;
	init(a);
	char t[20] = {};
	while (1)
	{
		menu(x, a);
		switch (x)
		{
		case 0:
		{ //《初始化》
			init(a);
			break;
		}
		case 1:
		{ //《创建》
			create(a);
			break;
		}
		case 2:
		{ //《删除·点》
			del1(a);
			break;
		}
		case 3:
		{ //《删除·边》
			del2(a);
			break;
		}
		case 4:
		{ //《启动更迭》
			go_star(a);
			break;
		}
		case 5:
		{ //《探测·单点》
			trace_1(a);
			break;
		}
		case 6:
		{ //《探测·双点》
			trace_2(a);
			break;
		}
		case 7:
		{ //《覆盖·单边》  	//兼顾 插入/修改功能
			insert_edge(a);
			break;
		}
		case 8:
		{ //《插入·单点》
			insert_node(a);
			break;
		}
		default:
		{ //输入异常
			printf("读入异常!\n");
			break;
		}
		}
		printf("\n");
		system("pause");
	}
	return 0;
}




 

Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

影月丶暮风

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

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

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

打赏作者

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

抵扣说明:

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

余额充值