数据结构课程设计(四)校园导航

news2025/2/4 17:24:54

4 校园导航

4.1 需求规格说明

【问题描述】

一个学校平面图,至少包括10个以上的场所,每个场所带有编号、坐标、名称、类别等信息,两个场所间可以有路径相通,路长(耗时)各有不同。要求读取该校园平面图,查询各个场所信息,找出从任意场所到达另一场所的最短路径(最佳路径)。

【基本要求】

校园平面图用图数据结构表达,采用指令或菜单方式操作,实现场所查询和路径求解。

【提高要求】

编写图形用户界面程序,使用交互方式:1)绘制校园平面图,并加以存储;2)点选查询场所信息;3)点选起点和终点,显示求得的最佳路径。

【测试数据】

自行设计校园平面图,用数据文件存储,格式自定。

4.2 总体分析与设计

(1)设计思想

①存储结构

在解决校园导航的问题中,这里程序的图结构使用了殷人昆数据结构中的图结构作为模板,定义了顶点的数据类型 VertexData 结构体。指定模板的顶点数据类型为 VertexData,边上的权值类型为 float。定义使用该数据类 型的模板别名为 Edgef、Vertexf 和 Graphf。接着实现了 GraphAdjList 类,派生自 Graphf 类, 增加了相应函数方便快速查找。VertexData 结构体存储了顶点类型(可分为场所和路径,场所 拥有名称和类型而路径没有),索引,x 坐标、y 坐标,名称、类别等数据。此外,定义了结 构体 DjikstraPathsAndCost 负责存储顶点所对应最短路径的权值和所经过路径。

②主要算法思想

校园导航的核心数据结构为图结构,顶点表示校园中场所、或者路径点,边表示场所或者路径点之间的道路。每条边之间都有相应的权值,代表边的同行便利成程度。求解最短路径的核心算法为Dijkstra算法。该算法的核心思想为:

(1)指定一个起点顶点D。引进两个数组S和U。其中S的作用是记录已经求出的最短路径的顶点和经过路径以及距离顶点D的最短距离,U的作用是记录还未求出最短路径的顶点和经过路径以及距离顶点D的距离。

(2)初始化S和U集合。最初,S集合中只包含顶点D,U集合中包含除了顶点D以外的所有顶点。如果U中顶点和D不相邻,则距离为无穷大。

(3)从U集合中取出一个离顶点D距离最短的顶点K,将其加到数组S中。同时从U集合中移除顶点K。

(4)根据S集合中的路径更新U集合中每个顶点距离顶点D的最短距离和相应路径。

(5)重复(3)、(4)操作直到U集合为空。

(2)设计表示

校园导航构建的UML类图如图4.2-1所示。

4.2-2 程序UML类图

类Edgef、Vertexf和Graphf分别为Edge、Vertex、Graphlnk,均来自殷人昆数据结构中的图结构。

类CampusNavigationThread为算法的核心实现类。该类包含一个图GraphAdjList,函数LoadGraph负责读取图数据,SaveGraph负责保存平面图数据。核心算法函数为Dijkstra,接受一个起点在图中的索引位置,一个映射引用作为返回值。

类CampusNavigation为界面类,派生自QMainWindow,实现了相应的槽函数和绘制点、绘制、线等函数。核心为使用了QgraphicSence和CampusNavigationView进行显示。

类CampusNavigationView为视图类,派生自QGraphicsView,重写了其鼠标交互函数,负责获取鼠标在视图坐标系中的坐标并传递给主窗口。

(3)详细设计表示

校园导航构建的程序流程图如图4.2-2所示。

4.2-2 校园导航程序流程图

该程序的流程步骤如下所示:

1.初始化:声明一个数组dis来保存原点到各个顶点的最短距离,将起始节点的距离设为0,对于顶点s存在能直接到达的边(s,m),则把dis[m]设为w(s,m),同时把其他所有s不能直接到达的顶点的路径长度设为无穷大。创建一个空集合T,用于存储已经找到最短路径的节点。初始时,集合T只有起点S。

2.选择最近的节点:从未包含在T中的节点中,从dis数组选择最小值,即选择距离最短的节点,然后将其添加到T中。

3.更新距离:检查新添加的节点的所有邻接节点。如果通过新添加的节点到达邻接节点的距离小于比源点直接到达的距离,那么就更新这个距离。

4.重复步骤2和3:重复步骤2和3,直到集合T包含了图的所有节点。

迪杰斯特拉算法的优点是能够找到从起始节点到所有其他节点的最短路径,而且算法的时间复杂度相对较低。然而,需要注意的是,这个算法不能处理存在负权边的图。

4.3 编码

【问题1:对殷人昆数据结构中图模板的理解与应用以及修改?

【解决方式】:在阅读了殷人昆数据结构中图模板的源代码后,程序派生并修改了其图类。为了更加贴近校导航的真实场景,程序尝试定义场所点Site和路径点RoadNode,场所是具有实际意义的地点,路径点是进行同行的点。决定使用自定义性高的json数据表达校园平面图,分别存储场所、路径点以及边。在交互功能的实现中,交互过程中需要将窗口坐标转换为视图坐标,因此需要重写QgraphicView类。

【问题2】:为了提升用户体验,本导航精心设计了多种鼠标事件。然而,在某些情况下,这些事件可能会出现冲突,导致无法正常响应。

【解决方式】:在处理鼠标交互事件时,可以采用状态模式设计来解决不同状态下鼠标事件冲突的问题。状态模式设计包括编辑模式和导航模式两种状态。在编辑模式下,可以处理鼠标的点击、移动、双击事件,而在导航模式下,则可以处理鼠标的点击、移动事件,且拥有不同的响应。通过在不同的状态下处理不同的鼠标事件,可以有效地避免事件冲突的问题,用户使用更加友好。

【问题2】:QPainter绘制时点与线之间压盖,无法清除画布已经绘制的事件。

【解决方式】:首先,需要注意绘制的顺序。在绘制点与线时,需要先绘制线,再绘制点,这样可以保证点不会压在线上而导致无法清除已经绘制的事件。如果已经出现了压盖的情况,可以通过重绘事件来解决问题。重绘事件时需要注意保持绘制的顺序一致,否则可能会出现新的问题。

4.4 程序及算法分析

①使用说明

打开程序后,即可出现初始化程序样式,如图4.4-1所示。

4.4-1 初始化程序

这里可以点击“打开文件”,会弹出一个对话框选择文件路径,加载准备好的json数据。将将鼠标放置到红色个实心圆(场所点)上在左下角显示场所数据;点击“添加顶点”,在地图上点击即可添加顶点。点击后会弹出对话框选择顶点类型并输入相应数据。可以选择添加场所点或者路径点;点击“添加边”,选中两个顶点,若成功选中,则会弹出对话框输入权重;如图4.4-2所示。

4.4-2 详细操作流程

此外,程序还支持进行最短路径的查询操作,当点击“求解最短路径”时,在图上选中两个顶点,系统会自动求解两点之间的最短路径并使用蓝色线进行绘制。如图4.4-3所示。

4.4-3 求解最短路径

此外,程序还支持将输出的文件进行保存,当点击“保存”时,会弹出对话框选择保存路径,绘制好的平面图会以json格式保存,如图4.4-4所示。

4.4-4 保存文件

同时,为了考虑到界面的交互效果,这里还支持放大和缩小操作,均由QAction控件控制,如图4.4-5所示。

4.4-5 支持放大缩小

4.5 小结

本题的核心是求解最短路径,这是校园导航系统的关键所在。在众多求解最短路径的算法中,我选择了Dijkstra算法。原因在于此次题目要求的是校园导航,这是一个单源点正权图的问题,而Dijkstra算法正是针对这类问题设计的。它能够大大降低时间复杂度,提高求解效率。本导航系统以校园地图为基础,可以加载预先准备好的校园场所点和道路拐点数据。同时,用户也可以根据自己的需求编辑线路,使得系统的灵活性和拓展性得到了极大的提升。这使得我们能够真实模拟校园场景,包括各个路口的拐点。从而完成最短路径规划,为校园内的用户提供便捷、高效的导航服务。

为了提供更好的用户体验,本系统不仅具备编辑、导航功能,还注重用户交互体验的细节。多种鼠标事件的支持,对鼠标悬停、点击等行为进行细致处理,使得系统能够及时响应用户的操作,使得用户能够以更加自然、直观的方式与系统进行交互,极大地提升了用户的使用体验。多种信息展示方式,用户可以轻松获取校园场所点的详细信息,更好地了解校园的各项设施和服务。

但是,本题使用的算法需要遍历所有的节点,并在每一步都找到未访问的节点中距离最短的节点。如果节点的数量非常大,这个函数可能会比较慢。未来将采用更高效的实现,例如使用优先队列来存储未访问的节点。

4.6 附录

//实现点的插入
// 顶点数据结构
struct VertexData
{
    VertexType type = VertexType::None; // 顶点类型,默认为None
    int index = -1;                     // 数据索引
    float x = 0.0f;                            // 顶点的X坐标
    float y = 0.0f;                            // 顶点的Y坐标
    string name = "";                   // 顶点名称
    string category = "";               // 分类信息
    VertexData() {}                     // 默认构造函数
    VertexData(int) {}                  // 带参数构造函数(空实现)
    // 比较操作符重载,用于判断两个顶点数据是否相等
    bool operator==(const VertexData& other) const
    {
        return type == other.type
            && index == other.index
            && x == other.x
            && y == other.y
            && name == other.name
            && category == other.category;
    }
};
//实现线的插入
// 图的邻接表表示类,继承自Graphlnk
class GraphAdjList : public Graphf
{
public:
    GraphAdjList(int sz) :Graphf(sz) {};	//构造函数
    ~GraphAdjList() {};		//析构函数
    // 根据数据索引和类型获取顶点在邻接表中的位置
    int getVertexPosByDataIndex(int _index, VertexType type)
    {
        for (int i = 0; i < numVertices; i++)
        {
            if (NodeTable[i].data.type != type)
                continue;
            int index = NodeTable[i].data.index;
            if (index == _index)
                return i;
        }
        return -1;// 未找到对应顶点 
    }
    // 获取所有边的信息,返回一个包含起点、终点和权值的元组列表
    vector<tuple<int, int, float>> getEdges()
    {
        std::set<std::pair<int, int>> visitedEdges; // 用于跟踪已访问的边
        std::vector<std::tuple<int, int, float>> edges; // 存储边的信息
        for (int i = 0; i < numVertices; ++i)
        {
            Edgef* edge = NodeTable[i].adj; // 当前顶点的邻接边表
            while (edge != nullptr)
            {
                int u = i;              // 起点
                int v = edge->dest;     // 终点
                if (visitedEdges.find({ v, u }) == visitedEdges.end())
                { // 如果这条边未被访问过
                    visitedEdges.insert({ u, v }); // 标记为已访问
                    edges.push_back({ u, v, edge->cost });
                }
                edge = edge->link; // 遍历下一条边
            }
        }
        return edges;
    }
    // 根据索引获取指定顶点的指针
    const Vertexf* getVertex(int index)
    {
        if (index < 0 || index >= numVertices)
            return nullptr;
        return &NodeTable[index];
    }
    // 获取所有站点类型的顶点
    const vector<Vertexf*> getAllSiteVertexs()
    {
        vector<Vertexf*> vertexs;
        for (int i = 0; i < numVertices; i++)
        {
            if (NodeTable[i].data.type == VertexType::Site)
            {
                vertexs.push_back(&NodeTable[i]);
            }
        }
        return vertexs;
    }
    // 获取所有路节点类型的顶点
    const vector<Vertexf*> getAllRoadNodeVertexs()
    {
		vector<Vertexf*> vertexs;
        for (int i = 0; i < numVertices; i++)
        {
            if (NodeTable[i].data.type == VertexType::RoadNode)
            {
				vertexs.push_back(&NodeTable[i]);
			}
		}
		return vertexs;
	}
    // 获取站点数量
    int NumberOfSites()
    {
		int count = 0;
        for (int i = 0; i < numVertices; i++)
        {
			if (NodeTable[i].data.type == VertexType::Site)
				count++;
		}
		return count;
	}
    // 获取路节点数量
    int NumberOfRoadNodes()
    {
        int count = 0;
        for (int i = 0; i < numVertices; i++)
        {
			if (NodeTable[i].data.type == VertexType::RoadNode)
				count++;
		}
        return count;
    }
    // 获取图的边界矩形
    QRectF getBoundingRect()
    {
        float minX = std::numeric_limits<float>::max();
        float minY = std::numeric_limits<float>::max();
        float maxX = std::numeric_limits<float>::min();
        float maxY = std::numeric_limits<float>::min();
        for (int i = 0; i < numVertices; i++)
        {
            float x = NodeTable[i].data.x;
            float y = NodeTable[i].data.y;
            if (x < minX)
                minX = x;
            if (x > maxX)
                maxX = x;
            if (y < minY)
                minY = y;
            if (y > maxY)
                maxY = y;
        }
        return QRectF(minX, minY, maxX - minX, maxY - minY);
    }
};
//实现Djikstra算法
bool CampusNavigationThread::Dijkstra(int startVertexIndex, map<int, DjikstraPathsAndCost>& S_vertexPosAndCost)
{
	if (startVertexIndex <  0 || startVertexIndex >= mGraph->NumberOfVertices())
		return false;
    
    map<int,DjikstraPathsAndCost> U_vertexPosAndCost;
    // 初始化U集合
    for (int i = 0; i < mGraph->NumberOfVertices(); i++)
    {
        if(i==startVertexIndex)     //起点不加入U集合
			continue;
        float cost = mGraph->getWeight(startVertexIndex, i);
        DjikstraPathsAndCost pathAndCost;
        if(cost != -1)  // 顶点I和起点之间有边
        {
            pathAndCost.cost = cost;
        }
        else
        {
            pathAndCost.cost = std::numeric_limits<float>::max();
        }
        pathAndCost.pathIndex.push_back(i);
        U_vertexPosAndCost.insert(pair<int, DjikstraPathsAndCost>(i, pathAndCost));
    }
    // 初始化S集合
    DjikstraPathsAndCost startPathAndCost;
    startPathAndCost.cost = 0;
    startPathAndCost.pathIndex.push_back(startVertexIndex);
    S_vertexPosAndCost.insert(pair<int, DjikstraPathsAndCost>(startVertexIndex, startPathAndCost));
    while (!U_vertexPosAndCost.empty())
    {
        // 寻找U集合中权值最小的顶点I
        auto minPos = min_element(U_vertexPosAndCost.begin(), U_vertexPosAndCost.end(),
            [](const pair<int, DjikstraPathsAndCost>& a, const pair<int, DjikstraPathsAndCost>& b) { return a.second.cost < b.second.cost; });
        S_vertexPosAndCost.insert(*minPos);        // 将权值最小的顶点I加入S集合
        float oldCost = minPos->second.cost;     // 起点到顶点I的距离
        vector<int> oldPath = minPos->second.pathIndex;  // 起点到顶点I的路径
        // 从U集合中移除顶点I
        int minIndex = minPos->first;
        U_vertexPosAndCost.erase(minPos);
        // 更新U集合中的各顶点到起点的距离
        for (auto& K_pair : U_vertexPosAndCost)
        {
            int i = K_pair.first;
            float minCost = K_pair.second.cost;
            float newCost = mGraph->getWeight(minIndex, i);     // 顶点K和顶点I之间的距离
            if (newCost == -1)  // 顶点K和顶点I之间没有边,保持原来的距离
                continue;
            newCost += oldCost;     // 顶点K到起点的距离
            if (newCost < minCost)
            {
                K_pair.second.cost = newCost;
                K_pair.second.pathIndex = oldPath;
                K_pair.second.pathIndex.push_back(i);
            }
        }
    }
	return true;
}
//实现Djikstra算法
//将图的数据保存到JSON文件
bool CampusNavigationThread::SaveGraph(QString path)
{
    QJsonObject root;
    // 保存Sites数据
    QJsonArray sitesArray;
    
    const vector<Vertexf*> AllSites = mGraph->getAllSiteVertexs();
    for (const auto& site : AllSites)
    {
        QJsonObject siteObject;
        siteObject["Index"] = site->data.index;
        siteObject["x"] = site->data.x;
        siteObject["y"] = site->data.y;
        siteObject["Name"] = QString::fromStdString(site->data.name);
        siteObject["Category"] = QString::fromStdString(site->data.category);
        sitesArray.append(siteObject);
    }
    root["Sites"] = sitesArray;
    // 保存 RoadNodes 数据
    QJsonArray roadNodesArray;
    const vector<Vertexf*> AllRoadNodes = mGraph->getAllRoadNodeVertexs();  // 获取所有路节点的顶点数据
    for (const auto& roadNode : AllRoadNodes)
    {
        QJsonObject roadNodeObject;
        roadNodeObject["Index"] = roadNode->data.index;
        roadNodeObject["x"] = roadNode->data.x;
        roadNodeObject["y"] = roadNode->data.y;
        roadNodesArray.append(roadNodeObject);
    }
    root["RoadNodes"] = roadNodesArray;
    // 保存Edges数据
    QJsonArray edgesArray;
    vector<tuple<int, int, float>> edges = mGraph->getEdges();
    for (const auto& edge : edges)
    {
        QJsonObject edgeObject;
        edgeObject["FromType"] = QString::fromStdString(getVertexTypeAsString(mGraph->getValue(std::get<0>(edge)).type));
        edgeObject["FromIndex"] = mGraph->getValue(std::get<0>(edge)).index;
        edgeObject["ToType"] = QString::fromStdString(getVertexTypeAsString(mGraph->getValue(std::get<1>(edge)).type));
        edgeObject["ToIndex"] = mGraph->getValue(std::get<1>(edge)).index;
        edgeObject["Cost"] = std::get<2>(edge);
        edgesArray.append(edgeObject);
    }
    root["Edges"] = edgesArray;
    // 将图数据保存为JSON数据
    QFile file(path);
    if (file.open(QIODevice::WriteOnly))
    {
        QJsonDocument jsonDoc(root);
        file.write(jsonDoc.toJson());
        file.close();
        return true;
    }
    else
    {
        return false;
    }
}

项目源代码:

Data-structure-coursework/4 at main · CUGLin/Data-structure-courseworkhttps://github.com/CUGLin/Data-structure-coursework/tree/main/4

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2291903.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式知识点总结 操作系统 专题提升(四)-上下文

针对于嵌入式软件杂乱的知识点总结起来&#xff0c;提供给读者学习复习对下述内容的强化。 目录 1.上下文有哪些?怎么理解? 2.为什么会有上下文这种概念? 3.什么情况下进行用户态到内核态的切换? 4.中断上下文代码中有哪些注意事项&#xff1f; 5.请问线程需要保存哪些…

Elasticsearch基本使用详解

文章目录 Elasticsearch基本使用详解一、引言二、环境搭建1、安装 Elasticsearch2、安装 Kibana&#xff08;可选&#xff09; 三、索引操作1、创建索引2、查看索引3、删除索引 四、数据操作1、插入数据2、查询数据&#xff08;1&#xff09;简单查询&#xff08;2&#xff09;…

xxl-job 在 Java 项目的使用 以一个代驾项目中的订单模块举例

能搜到这里的最起码一定知道 xxl-job 是用来干什么的&#xff0c;我就不多啰嗦怎么下载以及它的历史了 首先我们要知道 xxl-job 这个框架的结构&#xff0c;如下图&#xff1a; xxl-job-master&#xff1a;xxl-job-admin&#xff1a;调度中心xxl-job-core&#xff1a;公共依赖…

Alibaba开发规范_异常日志之日志规约:最佳实践与常见陷阱

文章目录 引言1. 使用SLF4J日志门面规则解释代码示例正例反例 2. 日志文件的保存时间规则解释 3. 日志文件的命名规范规则解释代码示例正例反例 4. 使用占位符进行日志拼接规则解释代码示例正例反例 5. 日志级别的开关判断规则解释代码示例正例反例 6. 避免重复打印日志规则解释…

SQLAlchemy 2.0的简单使用教程

SQLAlchemy 2.0相比1.x进行了很大的更新&#xff0c;目前网上的教程不多&#xff0c;以下以链接mysql为例介绍一下基本的使用方法 环境及依赖 Python:3.8 mysql:8.3 Flask:3.0.3 SQLAlchemy:2.0.37 PyMySQL:1.1.1使用步骤 1、创建引擎&#xff0c;链接到mysql engine crea…

OpenGL学习笔记(七):Camera 摄像机(视图变换、LookAt矩阵、Camera类的实现)

文章目录 摄像机/观察空间/视图变换LookAt矩阵移动相机&#xff08;处理键盘输入&#xff09;移动速度欧拉角移动视角&#xff08;处理鼠标输入&#xff09;缩放场景&#xff08;处理滚轮输入&#xff09;Camera类 摄像机/观察空间/视图变换 在上一节变换中&#xff0c;我们讨…

『VUE』vue-quill-editor富文本编辑器添加按钮houver提示(详细图文注释)

目录 预览效果新建一个config.js存放标题编写添加提示的方法调用添加标题方法的生命周期总结 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 预览效果 新建一个config.js存放标题 export const titleConfig [{ Choice: .ql-bold…

如何使用 DeepSeek 和 Dexscreener 构建免费的 AI 加密交易机器人?

我使用DeepSeek AI和Dexscreener API构建的一个简单的 AI 加密交易机器人实现了这一目标。在本文中&#xff0c;我将逐步指导您如何构建像我一样的机器人。 DeepSeek 最近发布了R1&#xff0c;这是一种先进的 AI 模型。您可以将其视为 ChatGPT 的免费开源版本&#xff0c;但增加…

微信登录模块封装

文章目录 1.资质申请2.combinations-wx-login-starter1.目录结构2.pom.xml 引入okhttp依赖3.WxLoginProperties.java 属性配置4.WxLoginUtil.java 后端通过 code 获取 access_token的工具类5.WxLoginAutoConfiguration.java 自动配置类6.spring.factories 激活自动配置类 3.com…

SRS代码目录

代码目录&#xff1a; src/目录下核心代码&#xff1a; core&#xff1a;核心功能模块&#xff0c;包括日志、配置、错误处理等&#xff1b;protocol&#xff1a;实现RTMP、HTTP-FLV、HLS等协议的模块&#xff1b;app&#xff1a;应用层的实现&#xff0c;包括流的发布、播放…

机器学习--1.KNN机器学习入门

1、机器学习概述 1.1、什么是机器学习 机器学习&#xff08;Machine Learning&#xff09;是人工智能&#xff08;Artificial Intelligence&#xff09;领域的一个子集&#xff0c;它主要关注如何让计算机系统通过经验学习&#xff08;数据&#xff09;并自动改进性能。机器学…

Adaptive LLM Transformer²

看到了一个不错的论文https://arxiv.org/pdf/2501.06252 TRANSFORMER-SQUARED: SELF-ADAPTIVE LLMS 挺有意思的&#xff0c;是一家日本AI公司SakanaAI的论文&#xff08;我以前写过他们的不训练提升模型的能力的文章&#xff0c;感兴趣可以去翻&#xff09;它家有Lion Jones坐镇…

基于LabVIEW的Modbus-RTU设备通信失败问题分析与解决

在使用 LabVIEW 通过 Modbus-RTU 协议与工业设备进行通信时&#xff0c;可能遇到无法正常发送或接收指令的问题。常见原因包括协议参数配置错误、硬件连接问题、数据帧格式不正确等。本文以某 RGBW 控制器调光失败为例&#xff0c;提出了一种通用的排查思路&#xff0c;帮助开发…

直方图:摄影中的视觉数据指南

目录 一、直方图基础&#xff1a;揭开它的神秘面纱 二、解读直方图类型&#xff1a;亮度与色彩的密码 &#xff08;一&#xff09;亮度直方图 &#xff08;二&#xff09;RGB 直方图 三、拍摄中巧用直方图&#xff1a;优化曝光与效果 &#xff08;一&#xff09;精准判断曝…

IM 即时通讯系统-51-MPush开源实时消息推送系统

IM 开源系列 IM 即时通讯系统-41-开源 野火IM 专注于即时通讯实时音视频技术&#xff0c;提供优质可控的IMRTC能力 IM 即时通讯系统-42-基于netty实现的IM服务端,提供客户端jar包,可集成自己的登录系统 IM 即时通讯系统-43-简单的仿QQ聊天安卓APP IM 即时通讯系统-44-仿QQ即…

【Linux】从硬件到软件了解进程

个人主页~ 从硬件到软件了解进程 一、冯诺依曼体系结构二、操作系统三、操作系统进程管理1、概念2、PCB和task_struct3、查看进程4、通过系统调用fork创建进程&#xff08;1&#xff09;简述&#xff08;2&#xff09;系统调用生成子进程的过程〇提出问题①fork函数②父子进程关…

2024-我的学习成长之路

因为热爱&#xff0c;无畏山海

Kamailio 不通过 dmq 实现注册复制功能

春节期间找到一篇文章&#xff0c;需要 fg 才能看到&#xff1a; https://medium.com/tumalevich/kamailio-registration-replication-without-dmq-65e225f9a8a7 kamailio1 192.168.56.115 kamailio2 192.168.56.116 kamailio3 192.168.56.117 route[HANDLE_REPLICATION] {i…

大模型系列21-AI聊天机器人

聊天机器人 背景机器学习基础监督学习&#xff08;Supervised Learning&#xff09;概念应用场景主要问题 无监督学习&#xff08;Unsupervised Learning&#xff09;概念常见方法应用场景 强化学习&#xff08;Reinforcement Learning&#xff09;概念关键要素应用场景 模型优…

25.2.3 【洛谷】作为栈的复习不错(学习记录)

今天学习的东西不算多&#xff0c;放了一个星期假&#xff0c;感觉不少东西都没那么清楚&#xff0c;得复习一下才行。今天搞个栈题写&#xff0c;把栈复习一下&#xff0c;明天进入正轨&#xff0c;边复习边学习新东西&#xff0c;应该会有二叉树的学习等等... 【洛谷】P1449 …