数据结构大作业——家谱管理系统(超详细!完整代码!)
目录
设计思路:
一、项目背景
二、功能分析
查询功能流程图:
管理功能流程图:
三、设计
四、实现
代码实现:
头文件
结构体
函数声明及定义
创建家谱树头结点
绘制家谱树(打印)
建立右兄弟
建立左孩子
建立孩子结点信息
查找x的孩子
查找祖先
添加孩子
添加兄弟
中序遍历
删除结点
主函数
完整代码:
调试分析及测试结果:
进入主界面
建立家谱
生成树
查询操作
删除操作
写在最后
设计思路:
一、项目背景
家谱是一种以表谱形式,记载一个以血缘关系为主体的家族世袭繁衍和重要人物事迹的特殊图书体裁。家谱是中国特有的文化遗产,是中华民族的三大文献(国史,地志,族谱)之一,属于珍贵的人文资料,对于历史学,民俗学,人口学,社会学和经济学的深入研究,均有其不可替代的独特功能。
经历了历朝历代的连年战乱和社会动荡,历史上传世的家谱几乎丧失殆尽,许多家族的世系也因此断了线、失了传。流传至今的古代家谱,大多是明清两代纂修。在我国明清时期,出现了专门替人伪造家谱世系的“谱匠”。
本项目旨在完成一个家谱系统,并实现家谱树所需要的查找、插入、搜索和删除等相关功能。
二、功能分析
完成一个简易的家谱管理系统,主要包含了管理和查询两大功能。
首先允许用户进行家谱的创建并能简易的输出整个家谱。其次,还要具有查询某结点祖先和孩子的功能,同时为保证用户可以随时修改家谱,添加了完善孩子、完善兄弟和删除结点的功能。其中删除结点规则定义为:若有孩子,则孩子一并删去;若有兄弟,则保留兄弟。最后考虑到现实中用户中输入错误的情况,还要包括健壮性的检查。
查询功能流程图:
管理功能流程图:
三、设计
该程序具有明显的树形结构,故采用树作为数据结构。我们选择采用二叉树,每个结点包括三个域,具体为lchild,rbrother,data,分别用来存储孩子、兄弟和此结点的名称。
结点代码设计如下:
typedef struct Node
{
string data;
struct Node* lchild;//左孩子
struct Node* rbrother;//右兄弟
}SLNode;
void Initiate(SLNode** T)这个函数用来创建家谱树头结点
SLNode* Insertright(SLNode* arr, string x)这个函数用来建立右兄弟
SLNode* Insertleft(SLNode* arr, string x)这个函数用来建立左孩子
void input(SLNode* arr)这个函数用来输入结点的儿子的信息
void PrintTree(SLNode* T, int n)这个函数用来打印家谱树
void Searchchild(SLNode* T, string x,bool &flag)这个函数用来查找x的孩子,在树T中查找x是否存在,并用flag来标记
void SearchAncestor(SLNode* T, string x, bool& flag)这个函数用来查找x的祖先,在树T中查找x是否存在,并用flag来标记
void addChild(SLNode* T, string x, string t)这个函数用来添加孩子,在树T中找到x结点并把t加入其左孩子之中
void addBrother(SLNode* T, string x, string t)这个函数用来添加兄弟,在树T中找到x结点并把t加入其左孩子之中
void inOrder(SLNode* T, string x, bool& flag)这个函数中序遍历查找x是否存在,并用flag来标记
void deleteNode(SLNode* T, string x)这个函数用来解散结点x的家庭
四、实现
完善家谱功能流程图
代码实现:
头文件
#include<iostream>
#include<windows.h>
#include <stdio.h>
#include <stdlib.h>
#include<string>
结构体
typedef struct Node
{string data;struct Node* lchild;//左孩子 struct Node* rbrother;//右兄弟
}SLNode;
函数声明及定义
void Initiate(SLNode** T);
SLNode* Insertright(SLNode* arr, string x);
SLNode* Insertleft(SLNode* arr, string x);
void input(SLNode* arr);
void PrintTree(SLNode* T, int n);
void Searchchild(SLNode* T, string x);
void SearchAncestor(SLNode* T, string x);
void addChild(SLNode* T, string x, string t);
void addBrother(SLNode* T, string x, string t);
void inOrder(SLNode* T, string x, bool& flag);
void deleteNode(SLNode* T, string x);
SLNode* p;
创建家谱树头结点
void Initiate(SLNode** T)
{*T = new SLNode;(*T)->lchild = NULL;(*T)->rbrother = NULL;
}
绘制家谱树(打印)
void PrintTree(SLNode* T, int n)
{int i, j;if (T){for (i = 0; i < n; i++) cout << " " ;cout << T->data;cout << endl;//打印家谱时为左孩子n+1,整体向右推移一位,右兄弟依然是nPrintTree(T->lchild, n + 1);PrintTree(T->rbrother, n);}
}
建立右兄弟
SLNode* Insertright(SLNode* arr, string x)
{SLNode* m;if (arr == NULL) return NULL;if (arr->rbrother != NULL) arr = arr->rbrother;m = new SLNode;m->data = x;m->rbrother = arr->rbrother;m->lchild = NULL;arr->rbrother = m;return arr->rbrother;
}
建立左孩子
SLNode* Insertleft(SLNode* arr, string x)
{SLNode* m;if (arr == NULL) return NULL;else if (arr->lchild == NULL)//为什么这里要判空?//因为有可能插入多个孩子,如果已经插入一个或多个了,就需要执行else块里的右兄弟函数往下递归找到空指针在插入 {//开始创建要插入的左孩子 m = new SLNode;//malloc无法为string分配正确内存,所以用newm->data = x;m->lchild = arr->lchild;//方便下次插入孩子结点 m->rbrother = NULL;arr->lchild = m;//孩子结点插入完成 return arr->lchild;}else{Insertright(arr->lchild, x);}
}
建立孩子结点信息
void input(SLNode* arr)
{string p;if (arr == NULL) return;cout << "请输入"<<arr->data<<"所有的儿子结点, 若没有儿子或者输完所有儿子,输入#即可:" << endl;cin >> p;while (p != "#"){Insertleft(arr, p);cin >> p;}//这个建立家谱的过程,为了防止输入混乱,先递归兄弟结点,再递归孩子结点 if (arr->rbrother != NULL)input(arr->rbrother);if (arr->lchild != NULL)input(arr->lchild);/*if (arr->rbrother != NULL )input(arr->rbrother);if (arr->lchild != NULL )input(arr->lchild);}
查找x的孩子
void Searchchild(SLNode* T, string x,bool &flag)
{SLNode* p;//要用T->data和x比较,所以要保证T不为空指针if (T != NULL && T->data != x){Searchchild(T->lchild, x,flag);Searchchild(T->rbrother, x,flag);}//加入限定条件只允许T->data==x时通过if (T != NULL && T->lchild != NULL && T->data == x){cout << T->data << "结点的儿子结点为:" << T->lchild->data << endl;p = T->lchild;while (p->rbrother != NULL)//右兄弟,找到一个儿子结点后去右子树找兄弟结点{cout << p->rbrother->data << endl;p = p->rbrother;}}
}
查找祖先
void SearchAncestor(SLNode* T, string x, bool& flag)//函数为找x的祖先
{if (T != NULL && T->data != x)//如果T不为空并且data不是x {SearchAncestor(T->lchild, x, flag);//不断向下递归找左孩子 SearchAncestor(T->rbrother, x, flag);//右兄弟 }//保证p为不变的指针,指向选择的结点if (T != NULL && T->data == x)//T不为空并且已经找到了结点 {p = T;//此时的结点赋给p flag = true;//说明已经找到了 }//下面找p的祖先即可 ,T是p的祖先,可能T的左孩子就是了也可能是T的左孩子的兄弟 if (T != NULL && T->lchild != NULL && (T->lchild == p || T->lchild->rbrother == p)) {cout << "他的祖先结点为:" << T->data << endl;p = T;}
}
添加孩子
void addChild(SLNode* T, string x, string t)
{//考虑到现实生活中会有二胎三胎,这个函数的作用即为添加孩子添加孩子结点 if (T != NULL && T->data != x){addChild(T->lchild, x, t);addChild(T->rbrother, x, t);}if (T != NULL && T->data == x){p = T;Insertleft(p, t);//调用前面的插入左孩子函数进行添加 }
}
添加兄弟
void addBrother(SLNode* T, string x, string t)
{if (T != NULL && T->data != x){addBrother(T->rbrother, x, t); addBrother(T->lchild, x, t);}if (T != NULL && T->data == x){p = T;Insertright(p, t);return;}
}
中序遍历
void inOrder(SLNode* T, string x, bool& flag)//中序遍历
{if (T == NULL || flag == true) return;inOrder(T->lchild, x, flag);if (T->data == x){flag = true;//如果要完善的结点在家谱里,flag为真 return;}inOrder(T->rbrother, x, flag);
}
删除结点
void deleteNode(SLNode* T, string x)
{//先定义删除规则://如果有孩子,孩子一并删去,有兄弟则保留兄弟。if (T == NULL) return;//因为T为头结点,这里从T->lchild开始遍历if ( T->lchild != NULL && T->lchild->data == x){SLNode* p = T->lchild->lchild;free(p);//根据删除规则,孩子一并删除T->lchild = T->lchild->rbrother;//保留兄弟}if (T->rbrother != NULL && T->rbrother->data == x){SLNode* p = T->rbrother->lchild;free(p);//根据删除规则,孩子一并删除T->rbrother = T->rbrother->rbrother;//保留兄弟}deleteNode(T->lchild, x);deleteNode(T->rbrother, x);//先根再左在右,先序遍历的方式删除
}
主函数
int main()
{SLNode* T;string p;int n;Initiate(&T);do{system("color 75");cout << " " << endl;cout << " 家谱管理系统 " << endl;cout << "------------------------- 功能选项 -------------------------";cout << endl << endl;cout << " ** 1-开始建立家谱 **" << endl;cout << " ** 2-查询-家谱树 **" << endl;cout << " ** 3-查询-儿子 **" << endl;cout << " ** 4-查询-祖先 **" << endl;cout << " ** 5-完善-孩子 **" << endl;cout << " ** 6-完善-兄弟 **" << endl;cout << " ** 7-删除-结点 **" << endl;cout << " ** 0-退出系统 **" << endl;cout << endl << endl;cout << "------------------------------------------------------------";cout << endl;cout << "请选择需要的功能(数字) :";char ch;ch = getchar();switch (ch){case '1':{cout << "请输入祖先结点:" << endl;cin >> p;Insertleft(T, p);input(T->lchild); getchar(); break;};case '2':{cout << endl;PrintTree(T->lchild, 1); getchar(); break;};case '3':{bool flag_1 = false;//flag_1用来标记是否有孩子bool flag_2 = false;//flag_2用来标记家谱里是否有要查询的结点cout << "请输入要查询的结点" << endl;cin >> p;inOrder(T, p, flag_2);//inOrder(T, p, flag_1); while (!flag_2){cout << "您要查询的结点并不存在, 请重新输入:" << endl;cin >> p;inOrder(T, p, flag_2);}Searchchild(T, p,flag_1); //直接在此函数中输出getchar(); break;};case '4':{cout << "请输入要查询的结点:" << endl;cin >> p;bool flag = false;while (p == T->lchild->data)//T是头结点,祖先存储在T的lchild域里{cout << "这个结点为祖先,请重新输入:" << endl;cin >> p;}SearchAncestor(T->lchild, p, flag);while (flag == false){cout << "此结点不存在,请重新输入:" << endl;cin >> p;while (p == T->lchild->data)//T是头结点,祖先存储在T的lchild域里{cout << "这个结点为祖先,请重新输入:" << endl;cin >> p;}SearchAncestor(T->lchild, p, flag);}getchar(); break;};case '5':{bool flag = false;//flag用来标记要完善的结点是否在家谱里 cout << "请输入要完善的结点:" << endl;cin >> p;inOrder(T, p, flag);//查找结点是否在家谱里while (!flag){cout << "您要完善的结点并不存在, 请重新输入:" << endl;cin >> p;inOrder(T, p, flag);}cout << "要添加的孩子为:" << endl;string x;cin >> x;addChild(T, p, x); getchar(); break;};case '6':{bool flag = false;//flag用来标记要完善的结点是否在家谱里cout << "请输入要完善的结点:" << endl;cin >> p;inOrder(T, p, flag);//查找结点是否在家谱里while (!flag){cout << "您要完善的结点并不存在,请重新输入:" << endl;cin >> p;inOrder(T, p, flag);}cout << "要添加的兄弟为:" << endl;string x;cin >> x;addBrother(T, p, x); getchar(); break;}case '7':{bool flag = false;//标记要删除的结点是否在家谱里cout << "请输入要删除的结点:" << endl;cin >> p;inOrder(T, p, flag);//查找结点是否在家谱里while (!flag){cout << "您要删除的结点并不存在,请重新输入:" << endl;cin >> p;inOrder(T, p, flag);}deleteNode(T, p); getchar(); break;}case '0':{cout << " 感谢您的使用,下次再见!" << endl;exit(0);}default:{cout << "输入有误,请重新输入:" << endl;ch = getchar();}}} while (1);return 0;
}
完整代码:
#include<iostream>
#include<windows.h>
#include <stdio.h>
#include <stdlib.h>
#include<string>using namespace std;typedef struct Node
{string data;struct Node* lchild;//左孩子 struct Node* rbrother;//右兄弟
}SLNode;//extern SLNode* p;void Initiate(SLNode** T);
SLNode* Insertright(SLNode* arr, string x);
SLNode* Insertleft(SLNode* arr, string x);
void input(SLNode* arr);
void PrintTree(SLNode* T, int n);
void Searchchild(SLNode* T, string x);
void SearchAncestor(SLNode* T, string x);
SLNode* p;void Initiate(SLNode** T)
{*T = new SLNode;(*T)->lchild = NULL;(*T)->rbrother = NULL;
}SLNode* Insertright(SLNode* arr, string x)
{SLNode* m;if (arr == NULL) return NULL;if (arr->rbrother != NULL) arr = arr->rbrother;m = new SLNode;m->data = x;m->rbrother = arr->rbrother;m->lchild = NULL;arr->rbrother = m;return arr->rbrother;
}SLNode* Insertleft(SLNode* arr, string x)
{SLNode* m;if (arr == NULL) return NULL;else if (arr->lchild == NULL)//为什么这里要判空?//因为有可能插入多个孩子,如果已经插入一个或多个了,就需要执行else块里的右兄弟函数往下递归找到空指针在插入 {//开始创建要插入的左孩子 m = new SLNode;//malloc无法为string分配正确内存,所以用newm->data = x;m->lchild = arr->lchild;//方便下次插入孩子结点 m->rbrother = NULL;arr->lchild = m;//孩子结点插入完成 return arr->lchild;}else{Insertright(arr->lchild, x);}
}void input(SLNode* arr)
{string p;if (arr == NULL) return;cout << "请输入"<<arr->data<<"所有的儿子结点, 若没有儿子或者输完所有儿子,输入#即可:" << endl;cin >> p;while (p != "#"){Insertleft(arr, p);cin >> p;}//这个建立家谱的过程,为了防止输入混乱,先递归兄弟结点,再递归孩子结点 if (arr->rbrother != NULL)input(arr->rbrother);if (arr->lchild != NULL)input(arr->lchild);/*if (arr->rbrother != NULL )input(arr->rbrother);if (arr->lchild != NULL )input(arr->lchild);//这里是先递归兄弟节点,再递归孩子结点*/
}void PrintTree(SLNode* T, int n)
{int i, j;if (T){for (i = 0; i < n; i++) cout << " " ;cout << T->data;cout << endl;//打印家谱时为左孩子n+1,整体向右推移一位,右兄弟依然是nPrintTree(T->lchild, n + 1);PrintTree(T->rbrother, n);}
}void Searchchild(SLNode* T, string x,bool &flag)//flag用来标记是否有孩子
{SLNode* p;//要用T->data和x比较,所以要保证T不为空指针if (T != NULL && T->data != x){Searchchild(T->lchild, x,flag);Searchchild(T->rbrother, x,flag);}//加入限定条件只允许T->data==x时通过if (T != NULL && T->lchild != NULL && T->data == x){cout << T->data << "结点的儿子结点为:" << T->lchild->data << endl;p = T->lchild;while (p->rbrother != NULL)//右兄弟,找到一个儿子结点后去右子树找兄弟结点{cout << p->rbrother->data << endl;p = p->rbrother;}}
}void SearchAncestor(SLNode* T, string x, bool& flag)//函数为找x的祖先
{if (T != NULL && T->data != x)//如果T不为空并且data不是x {SearchAncestor(T->lchild, x, flag);//不断向下递归找左孩子 SearchAncestor(T->rbrother, x, flag);//右兄弟 }//保证p为不变的指针,指向选择的结点if (T != NULL && T->data == x)//T不为空并且已经找到了结点 {p = T;//此时的结点赋给p flag = true;//说明已经找到了 }//下面找p的祖先即可 ,T是p的祖先,可能T的左孩子就是了也可能是T的左孩子的兄弟 if (T != NULL && T->lchild != NULL && (T->lchild == p || T->lchild->rbrother == p)){cout << "他的祖先结点为:" << T->data << endl;p = T;}
}void addChild(SLNode* T, string x, string t)
{//考虑到现实生活中会有二胎三胎,这个函数的作用即为添加孩子添加孩子结点 if (T != NULL && T->data != x){addChild(T->lchild, x, t);addChild(T->rbrother, x, t);}if (T != NULL && T->data == x){p = T;Insertleft(p, t);//调用前面的插入左孩子函数进行添加 }
}
void addBrother(SLNode* T, string x, string t)
{if (T != NULL && T->data != x){addBrother(T->rbrother, x, t); addBrother(T->lchild, x, t);}if (T != NULL && T->data == x){p = T;Insertright(p, t);return;}
}
void inOrder(SLNode* T, string x, bool& flag)//中序遍历
{if (T == NULL || flag == true) return;inOrder(T->lchild, x, flag);if (T->data == x){flag = true;//如果要完善的结点在家谱里,flag为真 return;}inOrder(T->rbrother, x, flag);
}
void deleteNode(SLNode* T, string x)
{//先定义删除规则://如果有孩子,孩子一并删去,有兄弟则保留兄弟。if (T == NULL) return;//因为T为头结点,这里从T->lchild开始遍历if ( T->lchild != NULL && T->lchild->data == x){SLNode* p = T->lchild->lchild;free(p);//根据删除规则,孩子一并删除T->lchild = T->lchild->rbrother;//保留兄弟}if (T->rbrother != NULL && T->rbrother->data == x){SLNode* p = T->rbrother->lchild;free(p);//根据删除规则,孩子一并删除T->rbrother = T->rbrother->rbrother;//保留兄弟}deleteNode(T->lchild, x);deleteNode(T->rbrother, x);//先根再左在右,先序遍历的方式删除
}
int main()
{SLNode* T;string p;int n;Initiate(&T);do{system("color 75");cout << " "<<endl;cout << " 家谱管理系统 "<<endl;cout << "------------------------- 功能选项 -------------------------";cout << endl << endl;cout << " ** 1-开始建立家谱 **" << endl;cout << " ** 2-查询-家谱树 **" << endl;cout << " ** 3-查询-儿子 **" << endl;cout << " ** 4-查询-祖先 **" << endl;cout << " ** 5-完善-孩子 **" << endl;cout << " ** 6-完善-兄弟 **" << endl;cout << " ** 7-删除-结点 **" << endl;cout << " ** 0-退出系统 **" << endl;cout << endl << endl;cout << "------------------------------------------------------------";cout << endl;cout <<"请选择需要的功能(数字) :";char ch;ch = getchar();switch (ch){case '1':{cout << "请输入祖先结点:" << endl;cin >> p;Insertleft(T, p);input(T->lchild); getchar(); break;};case '2':{cout << endl;PrintTree(T->lchild, 1); getchar(); break;};case '3':{bool flag_1 = false;//flag_1用来标记是否有孩子bool flag_2 = false;//flag_2用来标记家谱里是否有要查询的结点cout << "请输入要查询的结点" << endl;cin >> p;inOrder(T, p, flag_2);while (!flag_2){cout << "您要查询的结点并不存在, 请重新输入:" << endl;cin >> p;inOrder(T, p, flag_2);}Searchchild(T, p,flag_1); //如果有孩子,则直接在此函数中输出if (!flag_1)//flag_1为假,即没有孩子,执行if{cout << "此结点没有儿子!" << endl;}getchar(); break;};case '4':{cout<< "请输入要查询的结点:" << endl;cin >> p;bool flag = false;while(p == T->lchild->data)//T是头结点,祖先存储在T的lchild域里{cout << "这个结点为祖先,请重新输入:" << endl;cin >> p;}SearchAncestor(T->lchild, p,flag);while (flag == false) {cout << "此结点不存在,请重新输入:" << endl;cin >> p;while(p== T->lchild->data)//T是头结点,祖先存储在T的lchild域里{cout << "这个结点为祖先,请重新输入:" << endl;cin >> p;}SearchAncestor(T->lchild, p, flag);}getchar();break;};case '5':{bool flag = false;//flag用来标记要完善的结点是否在家谱里 cout << "请输入要完善的结点:" << endl;cin >> p;inOrder(T, p, flag);//查找结点是否在家谱里while (!flag){cout << "您要完善的结点并不存在, 请重新输入:" << endl;cin >> p;inOrder(T, p, flag);}cout << "要添加的孩子为:" << endl;string x;cin >> x;addChild(T, p, x); getchar(); break;};case '6':{bool flag = false;//flag用来标记要完善的结点是否在家谱里cout << "请输入要完善的结点:" << endl;cin >> p;while (p == T->lchild->data) //T是头结点,祖先存储在T的lchild域里{cout << "这个结点为祖先,请重新输入:" << endl;cin >> p;}inOrder(T, p, flag);//查找结点是否在家谱里while (!flag){cout << "您要完善的结点并不存在,请重新输入:" << endl;cin >> p;inOrder(T, p, flag);}cout << "要添加的兄弟为:" << endl;string x;cin >> x;addBrother(T, p, x); getchar(); break;}case '7':{bool flag = false;//标记要删除的结点是否在家谱里cout << "请输入要删除的结点:" << endl;cin >> p;inOrder(T, p, flag);//查找结点是否在家谱里while (!flag){cout << "您要删除的结点并不存在,请重新输入:" << endl;cin >> p;inOrder(T, p, flag);}deleteNode(T, p); getchar(); break;}case '0':{ cout << " 感谢您的使用,下次再见!" << endl;exit(0);}default:{cout << "输入有误,请重新输入:" << endl;ch = getchar();}}} while (1);//cout << "hello world!" << endl;return 0;
}
调试分析及测试结果:
本项目以《红楼梦》贾府建立家谱树为例
进入主界面
建立家谱
生成树
查询操作
删除操作
写在最后
此次大作业是博主在为完成数据结构课程设计与团队共同完成,此项目比较简单,涉及到的知识点与数据结构都是在树之中扩展的,如果想完成课程设计大作业,此项目分数可以80+(根据学校不同),如果追求更高的成绩,也可以在此项目扩展功能,例如:增加称呼功能(对于任意两个人可以查询出互相称呼什么)、完善个人信息(完善结点结构体,增加年龄、性别、性格等属性)。若你们大作业距离答辩仅剩1-3天,此项目可以用来应急,具体源代码、答辩PPT、测试数据会统一放到一个资源里面,下面会更新链接。也可以私信博主,免费提供给大家。
获取文件可以添加博主vx好友,备注来意,联系我传送门:https://bbs.csdn.net/topics/619404381
最后特此鸣谢团队三人,@池鱼c0de
此篇终,感谢大家支持。
相关文章:
数据结构大作业——家谱管理系统(超详细!完整代码!)
目录 设计思路: 一、项目背景 二、功能分析 查询功能流程图: 管理功能流程图: 三、设计 四、实现 代码实现: 头文件 结构体 函数声明及定义 创建家谱树头结点 绘制家谱树(打印) 建立右兄弟…...
【计算机网络】课程 实验二 交换机基本配置和VLAN 间路由实现
实验二 交换机基本配置和VLAN 间路由实现 一、实验目的 1.了解交换机的管理方式。 2.掌握通过Console接口对交换机进行配置的方法。 3.掌握交换机命令行各种模式的区别,能够使用各种帮助信息以及命令进行基本的配置。 4&…...
最新MySQL面试题(2025超详细版)
2025最新超详细MySQL面试题 文章目录 2025最新超详细MySQL面试题[toc]一、 SQL 和基本操作1. SQL的执行顺序2. 如何优化MySQL查询3. 常用的聚合函数4. 数据库事务5. 事务的四大特性(ACID)6. 视图7. MySQL中使用LIMIT子句进行分页8. MySQL中使用变量和用户定义的函数9. MySQL中的…...
Unity-Mirror网络框架-从入门到精通之AdditiveScenes 示例
文章目录 前言Additive Levels和Additive ScenesAdditive Levels场景介绍Portal传送门FadeInOut特效 Additive Scenes示例介绍ZoneHandlerSceneMassage 最后 前言 在现代游戏开发中,网络功能日益成为提升游戏体验的关键组成部分。Mirror是一个用于Unity的开源网络框…...
java 转义 反斜杠 Unexpected internal error near index 1
代码: String str"a\\c"; //出现异常,Unexpected internal error near index 1 //System.out.println(str.replaceAll("\\", "c"));//以下三种都正确 System.out.println(str.replace(\\, c)); System.out.println(str.r…...
html内容过长,实现向上循环滑动效果
以下是几种实现 HTML 内容过长时向上循环滑动的常见方法,你可以根据具体需求和项目场景来选择合适的实现方式: 一、使用 CSS3 animation 实现简单的向上循环滑动(适用于简单的文本等内容滑动场景) 原理 通过 CSS3 的 keyframes…...
RAG(Retrieval-Augmented Generation,检索增强生成)流程
目录 一、知识文档的准备二、OCR转换三、分词处理四、创建向量数据库五、初始化语言聊天模型1.prompt2.检索链3.对话 完整代码 知识文档的准备:首先需要准备知识文档,这些文档可以是多种格式,如Word、TXT、PDF等。使用文档加载器或多模态模型…...
数据库系统概论期末复习
期末考试题型: 选择题 20题 20分 判断题 10题 10分 简答题 4题 20分 SQL语句: (select delete update)30分 设计题:ER图 和关系模式 ER转关系模式,注意主码,外码的标注 15分 应用题:…...
B树与B+树:数据库索引的秘密武器
想象一下,你正在构建一个超级大的图书馆,里面摆满了各种各样的书籍。B树和B树就像是两种不同的图书分类和摆放方式,它们都能帮助你快速找到想要的书籍,但各有特点。 B树就像是一个传统的图书馆摆放方式: 1. 书籍摆放&…...
数据结构-栈与队列笔记
普通的双端队列 验证图书取出顺序 class Solution {/*** 验证书籍的借阅顺序是否合法。* * param putIn 表示放入书架的书籍序列。* param takeOut 表示从书架取出的书籍序列。* return 如果书籍的借阅顺序合法,返回 true;否则返回 false。*/public boo…...
Netty中用了哪些设计模式?
大家好,我是锋哥。今天分享关于【Netty中用了哪些设计模式?】面试题。希望对大家有帮助; Netty中用了哪些设计模式? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Netty 是一个高性能的网络通信框架,广泛…...
设计模式与游戏完美开发(3)
更多内容可以浏览本人博客:https://azureblog.cn/ 😊 该文章主体内容来自《设计模式与游戏完美开发》—蔡升达 第二篇 基础系统 第五章 获取游戏服务的唯一对象——单例模式(Singleton) 游戏实现中的唯一对象 在游戏开发过程中…...
人工智能的发展领域之GPU加速计算的应用概述、架构介绍与教学过程
文章目录 一、架构介绍GPU算力平台概述优势与特点 二、注册与登录账号注册流程GPU服务器类型配置选择指南内存和存储容量网络带宽CPU配置 三、创建实例实例创建步骤镜像选择与设置 四、连接实例SSH连接方法远程桌面配置 一、架构介绍 GPU算力平台概述 一个专注于GPU加速计算的…...
【51单片机零基础-chapter5:模块化编程】
模块化编程 将以往main中泛型的代码,放在与main平级的c文件中,在h中引用. 简化main函数 将原来main中的delay抽出 然后将delay放入单独c文件,并单独开一个delay头文件,里面放置函数的声明,相当于收纳delay的c文件里面写的函数的接口. 注意,单个c文件所有用到的变量需要在该文…...
彻底学会Gradle插件版本和Gradle版本及对应关系
看完这篇,保你彻底学会Gradle插件版本和Gradle版本及对应关系,超详细超全的对应关系表 需要知道Gradle插件版本和Gradle版本的对应关系,其实就是需要知道Gradle插件版本对应所需的gradle最低版本,详细对应关系如下表格࿰…...
容器技术思想 Docker K8S
容器技术介绍 以Docker为代表的容器技术解决了程序部署运行方面的问题。在容器技术出现前,程序直接部署在物理服务器上,依赖管理复杂,包括各类运行依赖,且易变,多程序混合部署时还可能产生依赖冲突,给程序…...
在C程序中实现类似Redis的SCAN机制的LevelDB大规模key分批扫描
在C程序中实现类似Redis的SCAN机制的LevelDB大规模key分批扫描,需要充分利用LevelDB的迭代器(iterator)功能,以便能够高效地扫描和处理大量的键值对。下面是一个详细的实现指南。 环境准备 首先,确保已经安装了Level…...
多模态论文笔记——CogVLM和CogVLM2
大家好,这里是好评笔记,公主号:Goodnote,专栏文章私信限时Free。本文详细介绍多模态模型的LoRA版本——CogVLM和CogVLM2。在SD 3中使用其作为captioner基准模型的原因和优势。 文章目录 CogVLM论文背景VLMs 的任务与挑战现有方法及…...
BLDC无感控制的驱动逻辑
如何知道转子已经到达预定位置,因为我们只有知道了转子到达了预定位置之后才能进行换相,这样电机才能顺滑的运转。转子位置检测常用的有三种方式。 方式一:通过过零检测,三相相电压与电机中性点电压进行比较。过零检测的优点在于…...
分布式多机多卡训练全景指南:MPI、DeepSpeed 与 Colossal-AI 深度解析
分布式多机多卡训练技术是深度学习领域提高训练效率和加快模型收敛的重要手段。以下是几个流行的框架和工具: 1. MPI(Message Passing Interface) 概述 MPI 是一种标准化的消息传递协议,用于多机多卡之间的通信与协作,…...
Unity中 Xlua使用整理(一)
1.安装: 从GitHub上下载Xlua源码 Tencent/xLua: xLua is a lua programming solution for C# ( Unity, .Net, Mono) , it supports android, ios, windows, linux, osx, etc. (github.com) 下载Xlua压缩包,并解压将Aseet文件夹中的Xlua和Plugins文件夹复制到Unit…...
在调用 borrowObject 方法时,Apache Commons Pool 会根据连接池的配置触发一系列相关的方法
在调用 borrowObject 方法时,Apache Commons Pool 会根据连接池的配置触发一系列相关的方法 1. GrpcChannel 的概念 GrpcChannel 是 gRPC 客户端与服务器之间通信的核心组件。它是基于 HTTP/2 的连接,支持多路复用,即通过单个通道可以发送多…...
【数据结构与算法:八、排序】
第8章 排序 排序是计算机科学中最基本且最常用的操作之一。本章详细介绍了排序算法的概念、分类、每种算法的定义、图示、代码实现及其应用场景。 8.1 基本概念和排序方法概述 8.1.1 排序的基本概念 排序是指将一组无序的记录按照某种指定的顺序重新排列的过程。 排序的目…...
Unity学习笔记(六)使用状态机重构角色移动、跳跃、冲刺
前言 本文为Udemy课程The Ultimate Guide to Creating an RPG Game in Unity学习笔记 整体状态框架(简化) Player 是操作对象的类: 继承了 MonoBehaviour 用于定义游戏对象的行为,每个挂载在 Unity 游戏对象上的脚本都需要继承自 MonoBehaviour&#x…...
搭建Golang gRPC环境:protoc、protoc-gen-go 和 protoc-gen-go-grpc 工具安装教程
参考文章: 安装protoc、protoc-gen-go、protoc-gen-go-grpc-CSDN博客 一、简单介绍 本文开发环境,均为 windows 环境,mac 环境其实也类似 ~ ① 编译proto文件,相关插件 简单介绍: protoc 是编译器,用于将…...
策略模式(strategy)
一.策略模式是什么 策略模式是一种行为型对象模式,它定义了一系列算法,并将每一个算法封装起来,使它们可以相互替换。这样,算法可以独立于使用它的客户端而变化。 策略者模式的核心思想是将一系列的算法封装到一系列的策略类里…...
Centos源码安装MariaDB 基于GTID主从部署(一遍过)
MariaDB安装 安装依赖 yum install cmake ncurses ncurses-devel bison 下载源码 // 下载源码 wget https://downloads.mariadb.org/interstitial/mariadb-10.6.20/source/mariadb-10.6.20.tar.gz // 解压源码 tar xzvf mariadb-10.5.9.tar.gz 编译安装 cmake -DCMAKE_INSTA…...
如何在 VSCode 中配置 C++ 开发环境:详细教程
如何在 VSCode 中配置 C 开发环境:详细教程 在软件开发的过程中,选择一个合适的开发环境是非常重要的。Visual Studio Code(VSCode)作为一款轻量级的代码编辑器,凭借其强大的扩展性和灵活性,受到许多开发者…...
信息安全、网络安全和数据安全的区别和联系
1. 前言 有次有朋友问我 信息安全、网络安全和数据安全,这三个词平时写文档时怎么用? 我想很多人都说不清。这次我查阅了资料,尽量讲清楚这三者之间的区别和联系。 2. 信息安全 2.1 定义 信息安全是指为数据处理系统建立和采用的技术和管…...
路由组件与一般组件的区别
路由组件与一般组件的区别 1. 基本概念 1.1 路由组件 路由组件是指通过路由规则映射的组件,通常放在 pages 或 views 文件夹中。 1.2 一般组件 一般组件是指通过 import 导入后直接使用的组件,通常放在 components 文件夹中。 2. 主要区别 2.1 存…...
【微服务】4、服务保护
微服务架构与组件介绍 单体架构拆分:黑马商城早期为单体架构,后拆分为微服务架构。跨服务调用与组件使用 服务拆分后存在跨服务远程调用,如下单需查询商品信息,使用openfeign组件解决。服务间调用关系复杂,需维护服务…...
6_TypeScript 函数 --[深入浅出 TypeScript 测试]
在 TypeScript 中,函数是编程的核心组成部分之一。TypeScript 不仅继承了 JavaScript 的所有函数特性,还添加了静态类型检查和其他一些增强功能,使得函数更加安全和易于理解。以下是关于 TypeScript 函数的一些关键点和两个具体的示例&#x…...
Apifox=Postman+Swagger+Jmeter+Mock
A. 开发人员接口管理使用(Swagger 工具管理接口) B. 后端开发人员通过Postman 工具,一边开发一边测试 C. 前端开发人员需要Mock 工具提供前端调用 D. 测试人员通过(Postman、Jmeter)等工具进行接口测试 为了后台开发、前端开发、测试工程师等不同角色更加便捷管理…...
升级 Spring Boot 3 配置讲解 —— Spring Boot 3 核心源码专讲
学会这款 🔥全新设计的 Java 脚手架 ,从此面试不再怕! Spring Boot 3 是 Spring 生态中的重要里程碑,它不仅全面支持 Java 17,还引入了许多新特性,如对 GraalVM 原生镜像的支持、改进的性能优化以及更灵活的…...
接口开发完后,个人对于接下来接口优化的一些思考
优化点 入参的合法性和长度范围,必填项的检查验证 因为没有入参,所以不需要考虑。 批量思想解决N1问题 // 假设要查询100个订单及其对应的用户信息 List<Order> orders orderMapper.selectList(new QueryWrapper<>().last("limit …...
jenkins 使用 ssh-agent向windows进行部署
背景: jenkins在linux的docker环境内,应用服务部署在windows。需要使用jenkins实现自动化部署。 实现方式: jenkins上构建pipeline任务,脚本如下: 遇到问题: 1、问题:jenkins 调用部署bat脚…...
音视频入门基础:MPEG2-PS专题(6)——FFmpeg源码中,获取PS流的视频信息的实现
一、引言 通过FFmpeg命令可以获取到PS文件/PS流的视频压缩编码格式、色彩格式(像素格式)、分辨率、帧率信息: ./ffmpeg -i XXX.ps 本文以H.264为例讲述FFmpeg到底是从哪个地方获取到这些视频信息的。 二、视频压缩编码格式 (…...
如果Adobe 退出中国后怎么办
最近听说Adobe要退出中国了?那咱们的设计师们可得好好想想怎么搞到正版软件了。别急,今天教大家一个超酷的福利——Edu邮箱! Edu邮箱是什么?有什么好处? Edu邮箱就是学校给学生和老师们发的邮箱,一般结尾是.edu。有了…...
欧几里得距离在权重矩阵中的物理意义
欧几里得距离在权重矩阵中的物理意义 目录 欧几里得距离在权重矩阵中的物理意义**衡量神经元差异程度**:**反映模型变化程度**:**聚类和分组的依据**:自然语言处理中的模型更新:**神经网络聚类分组**:欧几里得距离在权重矩阵中的物理意义衡量神经元差异程度: 在神经网络中…...
玩转大语言模型——ollama导入huggingface下载的模型
ollama导入huggingface模型 前言gguf模型查找相关模型下载模型 导入Ollama配置参数文件导入模型查看导入情况 safetensfors模型下载模型下载llama.cpp配置环境并转换 前言 ollama在大语言模型的应用中十分的方便,但是也存在一定的问题,比如不能使用自己…...
Linux-----进程通讯(管道Pipe)
目录 进程不共享内存 匿名管道 通过匿名管道实现通讯 有名管道 库函数mkfifo() 案例 进程不共享内存 不同进程之间内存是不共享的。是相互独立的。 #include <stdio.h> #include <stdlib.h> #include <errno.h>int num 0;int main(int argc, char con…...
【C++11】列表初始化、右值引用和移动语义、引用折叠、完美转发
C11 一.C的发展历史二.列表初始化1.C98的{}2.C11的{}3.C11中的std::initializer_list 三.右值引用和移动语义1.左值和右值2.左值引用和右值引用3.引用延长生命周期4.左值和右值的参数匹配5.右值引用和移动语义使用场景1.左值引用使用场景2.移动构造和移动赋值3.右值引用和移动语…...
Openssl1.1.1s rpm包构建与升级
rpmbuild入门知识 openssh/ssl二进制升级 文章目录 前言一、资源准备1.下载openssh、openssl二进制包2.安装rpmbuild工具3.拷贝源码包到SOURCES目录下4.系统开启telnet,防止意外导致shh无法连接5.编译工具安装6.补充说明 二、制作 OpenSSL RPM 包1.编写 SPEC 文件2.…...
递归思想的深度理解——汉诺塔问题和青蛙跳台阶问题
递归的深度理解——汉诺塔问题and青蛙跳台阶问题 青蛙跳台阶问题汉诺塔问题 青蛙跳台阶问题 问题:一只青蛙可以一次跳一级台阶,也可以一次跳两级台阶,如果青蛙要跳n级台阶,共有多少种跳法? 解答:我们可以先…...
从数据到诊断:朴素贝叶斯算法助力肿瘤预测之路
1.案例概述 肿瘤性质的判断影响着患者的治疗方式和痊愈速度。传统的做法是医生根据数十个指标来判断肿瘤的性质,预测效果依赖于医生的个人经验而且效率较低,而通过机器学习有望能快速预测肿瘤的性质。 2.数据集 本次肿瘤预测使用的数据集共有569组样本…...
Element-UI:如何实现表格组件el-table多选场景下根据数据对某一行进行禁止被选中?
如何实现表格组件el-table多选场景下根据数据对某一行进行禁止被选中? 在使用 Element UI 的 Table 组件时,如果你想要禁用某一行的选中(特别是在多选模式下),可以通过自定义行的 selectable 属性来实现。selectable …...
Dexcap复现代码数据预处理全流程(四)——demo_clipping_3d.py
此脚本的主要功能是可视化点云数据文件(.pcd 文件),并通过键盘交互选择演示数据的起始帧和结束帧,生成片段标记文件 (clip_marks.json) 主要流程包括: 用户指定数据目录:检查目录是否存在并处理标记文件 -…...
JWT理解
前言 随着互联网的快速发展,身份验证和授权成为了许多应用的重要需求。JWT(JSON Web Token)作为一种轻量级的身份验证和授权机制,得到了广泛的应用。本文将为您详细介绍JWT的原理、结构和优点,帮助您更好地理解和应用…...
一种融合联邦学习和大模型特点的全新系统架构
一种融合联邦学习和大模型特点的全新系统架构 以下是一种融合联邦学习和大模型特点的全新系统架构设计: 分层分布式架构 底层 - 数据采集与预处理层:由大量的边缘设备和终端节点组成,如智能手机、物联网传感器等。这些设备负责采集本地数据,并在本地进行初步的数据预处理,…...
html表格table导出excel,主从表格式,带样式.自动分列
html的table导出成excel, vue模板 项目使用xlsx-js-style 源代码从https://github.com/gitbrent/xlsx-js-style/releases/tag/v1.2.0 下载 用里面的dist目录下的文件即可. 复制到vue项目的public目录下的XLSX目录下. 在index.hml中引入js脚本, 为啥要在这里引入? 是因为这里…...