图结构邻接表表示法的C语言实现代码
图结构邻接表表示法的C语言实现代码,图结构相关知识可以参考“图结构
https://www.cnblogs.com/value-code/articles/9206581.html”这篇文章。刚进入程序会默认从inputArc.csv读取边数据,从inputVex.csv读取节点数据。如果没有读到数据则会打开比网页。可以通过配置文件(config.ini)来更改程序的输入输出文件等。
操作说明(方便手机九宫格键盘操作):
1:查找 | 2:向上 | 3:上关联
4:向左 | 5:确认 | 6:向右
7:删除 | 8:向下 | 9:下关联
,:修改 | 0:退出 | Ctrl+E:进入管理界面
查看代码
/*
* 文件名称: graph.c
* 摘要: 数据结构图
* 当前版本:
* 历史版本:
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <time.h>
#include <stdarg.h>
#include <assert.h>
// 取消注释禁用 assert()
// #define NDEBUG
#define CTRL_KEY(k) ((k) & 0x1f)
#define MAX_VERTEX_NUM 80000 //存储节点的上限
#define Length_limit 6000 //限制单次输入字符串最大限制
#define MAX_ARC_NUM 1000000 //限制最大链接数量
#define max_num 128
#define readSize 1024
typedef struct dual_Node {
int element;
struct dual_Node *pripor, *next;
}dualNode;
typedef struct {
dualNode *top, *bottom;
}links;
typedef struct {
char *data[3];
links *nextArc;
links *priporArc;
}VNode;
typedef struct {
int id[MAX_VERTEX_NUM];
VNode *vertices[MAX_VERTEX_NUM];
int vexnum;
int arcnum;
int endnum;
}ALGraph;
signed int getInt(void);
links *initLinks(void);
int pushStart(links *linkPoint, int value);
int popStart(links *linkPoint);
int popEnd(links *linkPoint);
int lengthLinks(links *linkPoint);
int delElem(links *linkPoint, int value);
int searchElem(links *linkPoint, int value);
void destroyLinks(links **linkPoint);
char* dynamicStr(links *head);
ALGraph* createGraph(void);
void destroyGraph(ALGraph **graph);
int binarySearch(ALGraph *graph, char *str);
int binaryInsert(ALGraph *graph, char *value);
int insertVex(ALGraph *graph, char *value);
int insertArc(ALGraph *graph, int source, int dest);
void deleteArc(ALGraph *graph, int source, int dest);
void deleteVex(ALGraph *graph, int del);
int dispGraph(ALGraph *graph);
int readALG(ALGraph *graph, char *filename, int flag);
void saveCSV(char *str, FILE *fp);
int saveALG(ALGraph *graph, char *filename, int flag);
void Next(char *T, int *next);
int KMP(ALGraph *graph, char *T);
char* getKeyValu(char *filename, char *section, char *key);
int utf8ToUnicode(char *utf8, unsigned long *unicode);
int writeLog(const char *format, ...);
int main(void)
{
ALGraph *digraph = createGraph();
int opt = '5';
char *menuList[] = {"Ctrl_E退出", "读取文件", "保存文件"};
char fileName[512] = {0};
if( digraph != NULL ){
strcpy(fileName, getKeyValu("config.ini", "[file]", "inputVex.csv"));
readALG(digraph, fileName, 0);
strcpy(fileName, getKeyValu("config.ini", "[file]", "inputArc.csv"));
readALG(digraph, fileName, 1);
if( digraph->vexnum == 0 )
system("start https://www.cnblogs.com/pangea/p/14858443.html");
do {
switch( opt ){
case '1':
strcpy(fileName, getKeyValu("config.ini", "[file]", "addArc.csv"));
readALG(digraph, fileName, 1);
strcpy(fileName, getKeyValu("config.ini", "[file]", "addVex.csv"));
readALG(digraph, fileName, 0);
break;
case '2':
strcpy(fileName, getKeyValu("config.ini", "[file]", "outputArc.csv"));
saveALG(digraph, fileName, 1);
strcpy(fileName, getKeyValu("config.ini", "[file]", "outputVex.csv"));
saveALG(digraph, fileName, 0);
break;
default :
opt = dispGraph(digraph);
break;
}
if( opt == '0' )
break;
srand(time(0));
sprintf(fileName, "%d", rand()%1350);
strcpy(fileName, getKeyValu("soup.ini", "[soup]", fileName));
printf("%s\n>", fileName);
printf("总节点: %d\n总连接: %d\n", digraph->vexnum, digraph->arcnum);
for(int i=0; i < 3; i++) {
printf("%d、%s\n",i, menuList[i]);
}
fprintf(stdout, ">");
opt = getInt();
}while( opt != CTRL_KEY('e') );
destroyGraph(&digraph);
}
return 0;
}
/*
* 函数介绍: 从 ini 文件中读取配置信息
* 参数:
* 返回值:
* 备注: 以 # 开头的为注释行,每行长度不得超过 512 个字符
*/
char* getKeyValu(char *filename, char *section, char *key)
{
char line[512] = {0};
char sectname[512] = {0};
char *skey = NULL, *valu = NULL;
char seps[] = "=";
bool flag = false, find = false;
FILE *fp = fopen(filename, "r");
if( fp == NULL ){
return key;
}
if( strchr(section, '[') == NULL){
strcpy(sectname, "[");
strcat(sectname, section);
strcat(sectname, "]");
} else {
strcpy(sectname, section);
}
while( fgets(line, 512, fp) != NULL ){
if( strcmp(line, "\n") == 0 ) { // 跳过空行
continue;
}
if( (valu = strchr(line, '\r')) != NULL ){
*valu = '\0';
} else if( (valu = strchr(line, '\n')) != NULL ){
*valu = '\0';
} else { // 跳过超过长度的行
while( fgets(line, 512, fp) != NULL ){
if( (valu = strchr(line, '\r')) != NULL ){
break;
} else if( (valu = strchr(line, '\n')) != NULL ){
break;
}
}
continue;
}
if( line[0] == '#' ) { // 跳过注释
continue;
}
if( line[0] == '[' ){ // 以 [ 开头的行默认为“节”
if( find == true ) {
break;
} else if( strcmp(sectname, line) == 0 ){
find = true;
flag = true;
}
}
if( flag ){
skey = strtok(line, seps);
if( strcmp(skey, key) == 0 ){
fclose(fp);
return strtok(NULL, "\0");
}
}
}
fclose(fp);
return key;
}
/*
* 函数介绍: 限制只接收一个字符的输入
* 参数:
* 返回值: 返回一个字符的数值
* 备注: 用于检测输入的合法性
*/
int getInt(void)
{
int opt = 0;
do {
opt = getchar();
if( opt == '\n' ){
break;
}
if( getchar() == '\n' ){
break;
}
while( getchar() != '\n' );
printf("只能输入一个字符!\n");
}while( 1 );
return opt;
}
/*
* 函数介绍: 初始化双链表
* 参数:
* 返回值: 成功时,返回指向新分配内存的指针;失败时,返回空指针 NULL。
* 备注:
*/
links *initLinks(void)
{
links *node = (links *)malloc(sizeof(links));
if( node != NULL ){
node->top = (dualNode *)malloc(sizeof(dualNode));
if( node->top != NULL ){
node->top->pripor = node->top;
node->top->next = node->top;
node->bottom = node->top;
node->bottom->element = 0;
} else {
free(node);
node = NULL;
}
}
return node;
}
/*
* 函数介绍: 从头部插入数值
* 参数: link 指针,int 类型的元素
* 返回值: 成功时,返回0;失败时,返时1。
* 备注: 当没有更多的内存可分配时失败
*/
int pushStart(links *linkPoint, int value)
{
assert( linkPoint != NULL );
if( linkPoint->bottom->next == linkPoint->top ){
dualNode *newElement = (dualNode *)malloc(sizeof(dualNode));
if( newElement != NULL) {
linkPoint->bottom->next = newElement;
newElement->pripor = linkPoint->bottom;
newElement->next = linkPoint->top;
linkPoint->top->pripor = newElement;
} else {
writeLog("%s, %s, %d:内存错误\n", __FILE__, __func__, __LINE__);
return 1;
}
}
linkPoint->top = linkPoint->top->pripor;
linkPoint->top->element = value;
linkPoint->bottom->element++;
return 0;
}
/*
* 函数介绍: 从头部弹出元素值
* 参数: link 指针
* 返回值: 链表为空时返回 -1
* 备注: 不检测链表中的值是否为 -1
*/
int popStart(links *linkPoint)
{
assert( linkPoint != NULL );
int resoult = -1;
if( linkPoint->top != linkPoint->bottom ){
resoult = linkPoint->top->element;
linkPoint->top = linkPoint->top->next;
linkPoint->bottom->element--;
}
return resoult;
}
/*
* 函数介绍: 从尾部弹出元素值
* 参数: link 指针
* 返回值: 链表为空时返回 -1
* 备注: 不检测链表中的值是否为 -1
*/
int popEnd(links *linkPoint)
{
assert( linkPoint != NULL );
int resoult = -1;
if( linkPoint->top != linkPoint->bottom ){
resoult = linkPoint->bottom->pripor->element;
linkPoint->bottom = linkPoint->bottom->pripor;
linkPoint->bottom->element = linkPoint->bottom->next->element - 1;
}
return resoult;
}
/*
* 函数介绍: 计算链表的长度
* 参数: link 指针
* 返回值: 返回链表的长度
* 备注:
*/
int lengthLinks(links *linkPoint)
{
assert( linkPoint );
int length = 0;
for( dualNode *point = linkPoint->top; point != linkPoint->bottom; length++ ) {
point = point->next;
}
assert( length == linkPoint->bottom->element );
return length;
}
/*
* 函数介绍: 删除链表中第一个value值
* 参数: link 指针,要删除的值
* 返回值: 删除成功返回 0,没有删除返回 -1。
* 备注:
*/
int delElem(links *linkPoint, int value)
{
assert( linkPoint != NULL );
int flag = -1;
dualNode *tmp = linkPoint->top;
while( tmp != linkPoint->bottom ){
if( tmp->element == value ){
if( tmp == linkPoint->top ){
linkPoint->top = linkPoint->top->next;
} else {
tmp->pripor->next = tmp->next;
tmp->next->pripor = tmp->pripor;
linkPoint->top->pripor->next = tmp;
tmp->pripor = linkPoint->top->pripor;
linkPoint->top->pripor = tmp;
tmp->next = linkPoint->top;
}
linkPoint->bottom->element--;
flag = 0;
}
tmp = tmp->next;
}
return flag;
}
/*
* 函数介绍: 查找链表中是否存在值value
* 参数:
* 返回值: 返回value在链表中的第几位,0代表未找到value值
* 备注:
*/
int searchElem(links *linkPoint, int value)
{
assert( linkPoint != NULL);
int num = 0;
dualNode *tmp = linkPoint->top;
while( tmp != linkPoint->bottom ){
num++;
if( tmp->element == value )
return num;
tmp = tmp->next;
}
return 0;
}
/*
* 函数介绍: 销毁链表
* 参数:
* 返回值:
* 备注:
*/
void destroyLinks(links **linkPoint)
{
if( *linkPoint != NULL ){
(*linkPoint)->bottom = (*linkPoint)->top->next;
(*linkPoint)->top->next = NULL;
do {
(*linkPoint)->top = (*linkPoint)->bottom;
(*linkPoint)->bottom = (*linkPoint)->bottom->next;
free((*linkPoint)->top);
}while( (*linkPoint)->bottom != NULL );
free(*linkPoint);
*linkPoint = NULL;
}
}
/*
* 函数介绍: 输入长度不超过Length_limit字节的字符串
* 参数:
* 返回值: 成功时,返回指向新分配内存的指针;失败时,返回空指针。
* 备注:
*/
char* dynamicStr(links *head)
{
int Bound = 0;
int ch = getchar();
head->top = head->bottom;
head->bottom->element = 0;
while( ch != '\n' && ch != '\0' && Bound <= Length_limit ){
if( pushStart(head, ch) == 1 ){ // 没有内存可以分配了
head->top = head->bottom;
head->bottom->element = 0;
return NULL;
}
Bound++;
ch=getchar();
}
if( ch != '\n') { // 清空输入缓存
while( getchar() != '\n' );
}
char *str = (char *)malloc((Bound+1) * sizeof(char));
if( str != NULL ){
for(int i=0; i < Bound; i++) {
str[i] = popEnd(head);
}
str[Bound] = '\0';
} else {
head->top = head->bottom;
head->bottom->element = 0;
}
return str;
}
/*
* 函数介绍: 初始化图存储结构
* 参数: 空
* 返回值: 成功时,返回已初始化的 ALGraph 指针;失败时,返回空指针。
* 备注:
*/
ALGraph* createGraph(void)
{
ALGraph *graph = (ALGraph *)malloc(sizeof(ALGraph));
if( graph != NULL ){
for( int i=0; i < MAX_VERTEX_NUM; i++ ){
graph->vertices[i] = NULL;
graph->id[i] = 0;
}
graph->vexnum = 0;
graph->arcnum = 0;
graph->endnum = MAX_VERTEX_NUM;
}
return graph;
}
/*
* 函数介绍: 销毁图
* 参数:
* 返回值:
* 备注: 释放图结构中所有申请的内存
*/
void destroyGraph(ALGraph **graph)
{
if(*graph != NULL) {
for( int i=0; i < (*graph)->vexnum; i++ ) {
destroyLinks(&(*graph)->vertices[(*graph)->id[i]]->nextArc);
destroyLinks(&(*graph)->vertices[(*graph)->id[i]]->priporArc);
for( int j=0; j < 3; j++ ){
free((*graph)->vertices[(*graph)->id[i]]->data[j]);
}
free((*graph)->vertices[(*graph)->id[i]]);
}
free(*graph);
*graph = NULL;
}
}
/*
* 函数介绍: 精确查找元素在图中的位置,二分查找
* 参数:
* 返回值: 找到,返回元素在图中的索引位置;找不到,返回-1。
* 备注:
*/
int binarySearch(ALGraph *graph, char *str)
{
int low = 0;
int mid = 0;
int high = 0;
if( graph->vexnum > 0 ){
high = graph->vexnum - 1;
while( low <= high ){
mid = low + (high - low)/2;
if( strcmp(graph->vertices[graph->id[mid]]->data[0], str) == 0 )
return mid;
if( strcmp(graph->vertices[graph->id[mid]]->data[0], str) < 0 ){
low = mid + 1;
} else {
high = mid - 1;
}
}
}
return -1;
}
/*
* 函数介绍: 二分插入
* 参数:
* 返回值:
* 备注:
*/
int binaryInsert(ALGraph *graph, char *value)
{
int left = 0;
int mid = 0;
int right = 0;
right = graph->vexnum - 1;
while( left < right ){
mid = left + (right - left)/2;
if( strcmp(graph->vertices[graph->id[mid]]->data[0], value) < 0 ){
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
/*
* 函数介绍: 在图中添加新节点
* 参数:
* 返回值: 成功,返回元素在图中的索引位置。
* 备注: -1出错,MAX_VERTEX_NUM已达到图存储极限
*/
int insertVex(ALGraph *graph, char *value)
{
if( graph == NULL || value == NULL || *value == '\0' )
return -1;
if( graph->vexnum >= MAX_VERTEX_NUM )
return MAX_VERTEX_NUM;
int source = 0;
int status = binarySearch(graph, value);
if( status == -1 ){
if( graph->endnum < MAX_VERTEX_NUM ){
status = graph->id[graph->endnum];
graph->endnum++;
} else {
status = graph->vexnum;
}
graph->vertices[status] = (VNode *)malloc(sizeof(VNode));
assert(graph->vertices[status]);
graph->vertices[status]->data[0] = value;
graph->vertices[status]->data[1] = NULL;
graph->vertices[status]->data[2] = NULL;
graph->vertices[status]->nextArc = initLinks();
graph->vertices[status]->priporArc = initLinks();
graph->vexnum++;
source = binaryInsert(graph, value);
for(int i = graph->vexnum - 1; i > source; i--) {
graph->id[i] = graph->id[i-1];
}
graph->id[source] = status;
status = source;
value = NULL;
}
return status;
}
/*
* 函数介绍: 向图中添加一条弧
* 参数:
* 返回值: 成功,返回0;失败,返回-1
* 备注: 当达到极限时将不再新增,以此来限制程序对存储空间的使用。
*/
int insertArc(ALGraph *graph, int source, int dest)
{
assert( graph != NULL);
if( graph->arcnum > MAX_ARC_NUM || source == -1 || dest == -1 || source == dest )
return -1;
if( searchElem(graph->vertices[graph->id[source]]->nextArc, graph->id[dest]) == 0 ){
pushStart(graph->vertices[graph->id[source]]->nextArc, graph->id[dest]);
pushStart(graph->vertices[graph->id[dest]]->priporArc, graph->id[source]);
graph->arcnum++;
}
return 0;
}
/*
* 函数介绍: 删除图中的一条弧
* 参数:
* 返回值:
* 备注:
*/
void deleteArc(ALGraph *graph, int source, int dest)
{
assert( graph );
if( graph->vertices[source] != NULL && graph->vertices[dest] != NULL ){
if( delElem(graph->vertices[source]->nextArc, dest) == 0 ){
delElem(graph->vertices[dest]->priporArc, source);
graph->arcnum--;
}
}
}
/*
* 函数介绍: 删除指定顶点
* 参数:
* 返回值:
* 备注: 释放该节点下申请的内存
*/
void deleteVex(ALGraph *graph, int del)
{
assert( graph );
if( del != -1 ){
int source = graph->id[del], dest=0;
while( lengthLinks(graph->vertices[source]->nextArc) != 0 ){
dest = graph->vertices[source]->nextArc->top->element;
deleteArc(graph, source, dest);
}
destroyLinks(&graph->vertices[source]->nextArc);
while( lengthLinks(graph->vertices[source]->priporArc) != 0 ){
dest = graph->vertices[source]->priporArc->top->element;
deleteArc(graph, dest, source);
}
destroyLinks(&graph->vertices[source]->priporArc);
for( int i=0; i < 3; i++ ){
free(graph->vertices[source]->data[i]);
}
free(graph->vertices[source]);
graph->vertices[source] = NULL;
for( int i = del; i < graph->vexnum - 1; i++ )
graph->id[i] = graph->id[i+1];
graph->vexnum--;
graph->endnum--;
graph->id[graph->endnum] = source;
}
}
/*
* 函数介绍: 显示图中顶点
* 参数:
* 返回值:
* 备注:
*/
int dispGraph(ALGraph *graph)
{
enum
{
UP = '2',
DOWN = '8',
LEFT = '4',
RIGHT = '6',
SELECT = '5',
PREVIOUS = '3',
NEXT = '9',
FIND= '1',
CHANGE = ',',
DELETE = '7',
EXIT = '0',
DISP = '.'
};
char *utf8 = NULL;
unsigned long unicode = 0;
int source = 0, dest = 0, current = 0;
char string[max_num] = {0};
char fmt[10] = {0};
char *str = NULL;
dualNode *temp = NULL;
links *point = NULL;
dualNode *vernier = NULL;
int ch = FIND, loop = 0, lens = 0;
links *head = initLinks();
if( graph->vexnum == 0 ){
printf("首个节点>");
sprintf(fmt, "%%%ds", max_num - 1);
do {
memset(string, 0, sizeof(string));
}while( scanf(fmt, string) != 1);
while( getchar() != '\n' );
str = (char *)malloc(strlen(string) + 1);
strcpy(str, string);
current = insertVex(graph, str);
for( int j=1; j < 3; j++ ){
printf("第 %d 项 >", j);
graph->vertices[graph->id[current]]->data[j] = dynamicStr(head);
}
}
srand(time(NULL));
current = rand()%graph->vexnum;
point = graph->vertices[graph->id[current]]->priporArc;
vernier = point->top;
do {
printf("———————————————————\n");
printf("%s;%s", graph->vertices[graph->id[current]]->data[0], \
graph->vertices[graph->id[current]]->data[1]);
if( vernier != point->bottom ){
printf("\n%s;%s", graph->vertices[vernier->element]->data[0], \
graph->vertices[vernier->element]->data[1]);
}
printf("\n———————————————————");
temp = graph->vertices[graph->id[current]]->priporArc->top;
loop = graph->vertices[graph->id[current]]->priporArc->bottom->element;
if( loop > 0 )
printf("\n%d:", loop);
while( loop > 0 ){
if( temp == vernier ){
fprintf(stdout, "%s《", graph->vertices[temp->element]->data[0]);
} else if( vernier != graph->vertices[graph->id[current]]->priporArc->bottom
&& temp->element == vernier->element ){
fprintf(stdout, "%s〈", graph->vertices[temp->element]->data[0]);
} else {
fprintf(stdout, "%s", graph->vertices[temp->element]->data[0]);
if( loop > 1 )
printf(",");
}
temp = temp->next;
loop--;
}
printf("\n【%s】", graph->vertices[graph->id[current]]->data[0]);
utf8 = graph->vertices[graph->id[current]]->data[0];
loop = strlen(utf8);
lens = 0;
do {
utf8 = utf8 + lens;
lens = utf8ToUnicode(utf8, &unicode);
loop = loop - lens;
printf(" %lx", unicode);
}while( loop > 0 && lens != 0 && *utf8 != '\0' );
temp = graph->vertices[graph->id[current]]->nextArc->top;
loop=graph->vertices[graph->id[current]]->nextArc->bottom->element;
if( loop > 0 )
printf("\n%d:", loop);
while( loop > 0 ){
if( temp == vernier ){
fprintf(stdout, "%s《", graph->vertices[temp->element]->data[0]);
} else if( vernier != graph->vertices[graph->id[current]]->priporArc->bottom
&& temp->element == vernier->element ){
fprintf(stdout, "%s〈", graph->vertices[temp->element]->data[0]);
} else {
fprintf(stdout, "%s", graph->vertices[temp->element]->data[0]);
if( loop > 1 )
printf(",");
}
temp = temp->next;
loop--;
}
printf("\n>");
ch = getInt();
switch( ch ){
case UP:
point = graph->vertices[graph->id[current]]->priporArc;
vernier = point->top;
break;
case DOWN:
point = graph->vertices[graph->id[current]]->nextArc;
vernier = point->top;
break;
case LEFT:
if( vernier == point->top ){
vernier = point->bottom->pripor;
} else {
vernier = vernier->pripor;
}
break;
case RIGHT:
if( vernier->next == point->bottom ){
vernier = point->top;
} else {
vernier = vernier->next;
}
break;
case SELECT:
if( vernier != point->bottom ){
current = binarySearch(graph, graph->vertices[vernier->element]->data[0]);
point = graph->vertices[graph->id[current]]->priporArc;
vernier = point->bottom;
}
break;
case PREVIOUS:
fprintf(stdout, "反向链接:");
sprintf(fmt, "%%%ds", max_num - 1);
do {
memset(string, 0, sizeof(string));
}while( scanf(fmt, string) != 1 );
while( getchar() != '\n' );
dest = binarySearch(graph, string);
if( dest == -1 ){
if( graph->vexnum >= MAX_VERTEX_NUM ){
printf("已达存储上限,无法新增\n");
} else {
printf("按'%c'增加:%s\n>", SELECT, string);
if( getInt() == SELECT ){
source = graph->id[current];
str = (char *)malloc(strlen(string) + 1);
strcpy(str, string);
dest = insertVex(graph, str);
for( int j=1; j < 3; j++ ){
printf("第 %d 项: ", j);
str = dynamicStr(head);
if( *str != '\0' ){
graph->vertices[graph->id[dest]]->data[j] = str;
} else {
free(str);
}
str = NULL;
}
current = binarySearch(graph, graph->vertices[source]->data[0]);
insertArc(graph, dest, current);
}
}
} else {
insertArc(graph, dest, current);
}
break;
case NEXT: fprintf(stdout, "插入链接:");
sprintf(fmt, "%%%ds", max_num - 1);
do {
memset(string, 0, sizeof(string));
}while( scanf(fmt, string) != 1 );
while( getchar() != '\n' );
dest = binarySearch(graph, string);
if( dest == -1 ){
if( graph->vexnum >= MAX_VERTEX_NUM ){
printf("已达存储上限,无法新增\n");
} else {
printf("按'%c'增加:%s\n>", SELECT, string);
if( getInt() == SELECT ){
source = graph->id[current];
str = (char *)malloc(strlen(string) + 1);
strcpy(str, string);
dest = insertVex(graph, str);
for( int j=1; j < 3; j++ ){
printf("第 %d 项: ", j);
str = dynamicStr(head);
if( *str != '\0' ){
graph->vertices[graph->id[dest]]->data[j] = str;
} else {
free(str);
}
str = NULL;
}
current = binarySearch(graph, graph->vertices[source]->data[0]);
insertArc(graph, current, dest);
}
}
} else {
insertArc(graph, current, dest);
}
break;
case FIND:
fprintf(stdout, "查找:");
sprintf(fmt, "%%%ds", max_num - 1);
do {
memset(string, 0, sizeof(string));
}while( scanf(fmt, string) != 1 );
while( getchar() != '\n' );
source = binarySearch(graph, string);
if( source != -1 ){
current = source;
point = graph->vertices[graph->id[current]]->priporArc;
vernier = point->top;
} else {
source = KMP(graph, string);
if( source != MAX_VERTEX_NUM ){
current = source;
point = graph->vertices[graph->id[current]]->priporArc;
vernier = point->top;
}
}
break;
case DELETE:
if( point->bottom != vernier ){
temp = vernier->next;
if( point == graph->vertices[graph->id[current]]->priporArc ){
deleteArc(graph, vernier->element, graph->id[current]);
} else if( point == graph->vertices[graph->id[current]]->nextArc ){
deleteArc(graph, graph->id[current], vernier->element);
}
if( temp != point->bottom ){
vernier = temp;
} else if( temp->element != 0 ){
vernier = temp->pripor;
} else if( point == graph->vertices[graph->id[current]]->priporArc ){
point = graph->vertices[graph->id[current]]->nextArc;
vernier = point->top;
} else {
point = graph->vertices[graph->id[current]]->priporArc;
vernier = point->top;
}
} else {
printf("按'%c'删除:%s\n>", SELECT, graph->vertices[graph->id[current]]->data[0]);
if( getInt() == SELECT && graph->vexnum > 1 ){
deleteVex(graph, current);
current = 0;
point = graph->vertices[graph->id[current]]->priporArc;
vernier = point->top;
}
}
break;
case CHANGE:
printf("修改\n");
printf("%s\n>", graph->vertices[graph->id[current]]->data[0]);
str = dynamicStr(head);
if( binarySearch(graph, str) == -1 && *str != '\0' ){
free(graph->vertices[graph->id[current]]->data[0]);
graph->vertices[graph->id[current]]->data[0] = str;
for( int j=1; j < 3; j++ ){
printf("%s\n>", graph->vertices[graph->id[current]]->data[j]);
str = dynamicStr(head);
if( *str != '\0' ){
free(graph->vertices[graph->id[current]]->data[j]);
graph->vertices[graph->id[current]]->data[j] = str;
} else {
free(str);
}
}
for( ; current < graph->vexnum-1; current++ ){
if( strcmp(graph->vertices[graph->id[current]]->data[0], \
graph->vertices[graph->id[current+1]]->data[0]) > 0 ){
source = graph->id[current];
graph->id[current] = graph->id[current+1];
graph->id[current+1] = source;
} else {
break;
}
}
for( ; current > 0; current-- ){
if( strcmp(graph->vertices[graph->id[current]]->data[0], \
graph->vertices[graph->id[current-1]]->data[0]) < 0 ){
source = graph->id[current];
graph->id[current] = graph->id[current-1];
graph->id[current-1] = source;
} else {
break;
}
}
} else {
if( *str == '\0' ){
free(str);
for( int j=1; j < 3; j++ ){
printf("%s\n>", graph->vertices[graph->id[current]]->data[j]);
str = dynamicStr(head);
if( *str != '\0' ){
free(graph->vertices[graph->id[current]]->data[j]);
graph->vertices[graph->id[current]]->data[j] = str;
} else {
free(str);
}
}
} else {
free(str);
}
}
break;
case DISP:
for( int i=0; i < 3; i++) {
if( graph->vertices[graph->id[current]]->data[i] != NULL)
printf("%s\n", graph->vertices[graph->id[current]]->data[i]);
}
while( getchar() != '\n' );
break;
default :
if( ch == CTRL_KEY('e') ){
destroyLinks(&head);
return ch;
}
break;
}
}while( ch != EXIT );
destroyLinks(&head);
return '0';
}
/*
* 函数介绍: 读取csv文件格式文件到图结构
* 参数:
* 返回值: 成功:返回0;失败,返回EXIT_FAILURE。
* 备注: 使用fgetc对文件读写时,文件越大读取速度越慢。需改进
*/
int readALG(ALGraph *graph, char *filename, int flag)
{
links *head = initLinks();
if( head == NULL ){
writeLog("%s, %s, %d:内存错误\n", __FILE__, __func__, __LINE__);
return EXIT_FAILURE;
}
FILE *fp = fopen(filename, "r");
if( fp == NULL ){
writeLog("%s, %s, %d:%s文件丢失\n", __FILE__, __func__, __LINE__, filename);
return EXIT_FAILURE;
}
char *str = NULL, *topStr = NULL;
bool endOfField = true, endOfLine = false, quotationMarks = false, formatError = false, memoryError = false;
dualNode *temp = NULL, *del = NULL;
int fieldCount = 0, topNum = 0, strLength = 1, c = 0;
while( (c = fgetc(fp)) != EOF ){
if( isblank(c) != 0 ){
continue; //跳过字段开始的空白字符
}
endOfField = true; //读取了一个字段
endOfLine = false; //读取了一行
quotationMarks = false; //字段中有特殊符号
memoryError = false; //内存分配失败
head->top = head->bottom; //初始化链表
head->bottom->element = 0;
do {
if( pushStart(head, c) == 1 ){
memoryError = true;
break;
}
if( c == '"' ){
endOfField = !endOfField;
quotationMarks = true;
}
if( endOfField && (c == ',' || c == '\n' || c == '\r') ){
if( c == '\n' || c == '\r' ){
endOfLine = true;
}
popStart(head);
break;
}
c = fgetc(fp);
}while(c != EOF);
if( memoryError ){
break;
} else if( ferror(fp) ){
writeLog("%s, %s, %d:%s读取时错误\n", __FILE__, __func__, __LINE__, filename);
break;
} else if( feof(fp) ){
writeLog("完整读取文件 %s\n", filename);
break;
} else if( !endOfField ){
writeLog("%s格式错误\n", filename);
break;
}
while( isblank(head->top->element) != 0 ){
popStart(head); //删除字段结尾的空白字符
}
if( quotationMarks == true ){ //特殊字符串格式检查
if( head->top->element != '"' || head->bottom->pripor->element != '"' )
continue;
popStart(head);
popEnd(head);
temp = head->top;
del = NULL;
while( temp != head->bottom ){
if( temp->element == '"' ){
del = temp->next;
if( del->element == '"' ){
temp->next = del->next;
del->next->pripor = temp;
head->top->pripor->next = del;
del->pripor = head->top->pripor;
head->top->pripor = del;
del->next = head->top;
head->bottom->element--;
} else {
formatError = true;
break;
}
}
temp = temp->next;
}
if( formatError ){
formatError = false;
continue;
}
}
strLength = lengthLinks(head);
str = NULL;
str = (char *)malloc((strLength+1) * sizeof(char));
if( str == NULL ){
writeLog("%s, %s, %d:内存错误\n", __FILE__, __func__, __LINE__);
break;
}
for( int i=0; i < strLength; i++ ){
str[i] = popEnd(head);
}
str[strLength] = '\0';
fieldCount++;
if( graph->vexnum >= MAX_VERTEX_NUM && graph->arcnum >= MAX_ARC_NUM ){
printf("已达到存储极限\n");
break;
}
if( flag == 0 ){
if( fieldCount == 1 ){
topNum = binarySearch(graph, str);
if( topNum == -1 && *str != '\0' ){
topNum = insertVex(graph, str);
} else {
free(str);
}
} else{
if( topNum != -1 ){
if( fieldCount <= 3 && *str != '\0' ){
free(graph->vertices[graph->id[topNum]]->data[fieldCount-1]);
graph->vertices[graph->id[topNum]]->data[fieldCount-1] = str;
} else {
free(str);
}
}
}
} else if( flag == 1 ){
topNum = binarySearch(graph, str);
if( topNum == -1 && *str != '\0' ){
topNum = insertVex(graph, str);
} else {
free(str);
}
if( fieldCount == 1 ){
if( topNum != -1 ){
topStr = graph->vertices[graph->id[topNum]]->data[0];
} else {
topStr = NULL;
}
} else {
if( topStr != NULL ){
insertArc(graph, binarySearch(graph, topStr), topNum);
}
}
}
if( endOfLine ){
fieldCount = 0;
}
}
if( ferror(fp) ){
writeLog("%s, %s, %d:%s读取时错误\n", __FILE__, __func__, __LINE__, filename);
} else if( feof(fp) ){
writeLog("读取节点%d,连接%d\n", graph->vexnum, graph->arcnum );
}
fclose(fp);
destroyLinks(&head);
return 0;
}
/*
* 函数介绍: 保存子符串为csv格式
* 参数:
* 返回值:
* 备注:
*/
void saveCSV(char *str, FILE *fp)
{
unsigned int flag = false;
if( str == NULL )
return;
char *tmp = str;
if( isblank(*tmp) != 0 )
flag = true;
while( *tmp != '\0' ){
if( *tmp == ',' || *tmp == '\n' || *tmp == '"' ){
flag = true;
break;
}
tmp++;
}
tmp--;
if( isblank(*tmp) != 0 )
flag = true;
tmp = str;
if( flag ){
putc('\"', fp);
while( *tmp != '\0' ){
if( *tmp == '"' )
putc('\"', fp);
putc(*tmp, fp);
tmp++;
}
putc('\"', fp);
} else {
while( *tmp != '\0' ){
putc(*tmp, fp);
tmp++;
}
}
}
/*
* 函数介绍: 保存“图”中数据到文件中
* 参数:
* 返回值:
* 备注:
*/
int saveALG(ALGraph *graph, char *filename, int flag)
{
int newline = '\n';
if( graph->vexnum > 0 ){
FILE *fp = fopen(filename, "wb");
if( fp == NULL ){
return EXIT_FAILURE;
}
if( flag == 0 ){
for( int i=0; i < graph->vexnum; i++ ){
if( graph->vertices[graph->id[i]]->data[1] == NULL
&& graph->vertices[graph->id[i]]->data[2] == NULL ){
continue;
} else {
saveCSV(graph->vertices[graph->id[i]]->data[0], fp);
for( int j=1; j < 3; j++ ){
putc(',', fp);
saveCSV(graph->vertices[graph->id[i]]->data[j], fp);
}
putc(newline, fp);
}
}
} else if( flag == 1 ){
dualNode *tmp = NULL;
for( int i=0; i < graph->vexnum; i++ ){
saveCSV(graph->vertices[graph->id[i]]->data[0], fp);
tmp = graph->vertices[graph->id[i]]->nextArc->bottom;
for( int i=tmp->element; i > 0; i-- ){
tmp = tmp->pripor;
putc(',', fp);
saveCSV(graph->vertices[tmp->element]->data[0], fp);
}
putc(newline, fp);
}
}
fclose(fp);
}
return true;
}
/*
* 函数介绍: 计算KMP的NEXT数组
* 参数:
* 返回值:
* 备注:
*/
void Next(char *T, int *next)
{
unsigned int i = 1;
next[1] = 0;
unsigned int j = 0;
while( i < strlen(T) ){
if( j == 0 || T[i-1] == T[j-1] ){
i++;
j++;
if( T[i-1] != T[j-1] ){
next[i] = j;
} else {
next[i] = next[j];
}
} else {
j = next[j];
}
}
}
/*
* 函数介绍: KMP快速匹配子串,顺序查找
* 参数:
* 返回值:
* 备注: MAX_VERTEX_NUM为没找到匹配的子串
*/
int KMP(ALGraph *graph, char *T)
{
int next[max_num] = {0}, pattern[max_num] = {0};
Next(T, next);
int i = 1, j = 1;
char *S = NULL;
int lenS = 0, lenT = strlen(T);
int num = 0, resoult = MAX_VERTEX_NUM;
for(num = 0; num < graph->vexnum; num++) {
S = graph->vertices[graph->id[num]]->data[0];
lenS = strlen(S);
if( lenS < lenT ) continue;
i = 1;
j = 1;
memcpy(pattern, next, max_num);
while( i <= lenS && j <= lenT ){
if( j == 0 || S[i-1] == T[j-1] ){
i++;
j++;
} else {
j = pattern[j];
}
}
if( j > lenT ){
resoult = num;
printf("%s,按'5'结束搜索:", S);
if( getInt() == '5' )
return resoult;
}
}
return resoult;
}
/*
* 函数介绍:UTF-8解码器,将UTF8转换为 UNICODE,参考www.cnblogs.com/mickole/articles/3663924.html
* 参数:utf8指向以UTF8编码的字符,unicode指向保存的数据即unicode编码
* 返回值:成功返回utf8编码所占用的字节数;失败返回0.
* 备注:utf8没有字节序问题,但是unicode有字节序要求;
* 字节序分为大端(Big Endian)和小端(Little Endian)两种;
* 在Intel处理器中采用小端法表示,在此采用小端法表示。(低地址存低位)
*/
int utf8ToUnicode(char *utf8, unsigned long *unicode)
{
*unicode = 0;
unsigned char *pOutput = (unsigned char *)unicode;
unsigned char temp = 0x80;
int utfBytes = 0;
while( temp & *utf8 ){
utfBytes++;
temp = temp>>1;
if( utfBytes > 1 && ((*(utf8 + utfBytes - 1) & 0xC0) != 0x80) )
return 0; //除了首字节,后面的字节前导均为“10”
}
switch( utfBytes ){
case 0:
*pOutput = *utf8;
utfBytes++;
break;
case 2:
*pOutput = (*utf8 << 6) + (*(utf8+1) & 0x3F);
*(pOutput+1) = (*utf8 >> 2) & 0x07;
break;
case 3:
*pOutput = (*(utf8+1) << 6) + (*(utf8+2) & 0x3F);
*(pOutput +1) = (*utf8 << 4) + ((*(utf8+1) >> 2) & 0x0F);
break;
case 4:
*pOutput = (*(utf8+2) << 6) + (*(utf8+3) & 0x3F);
*(pOutput+1) = (*(utf8+1) << 4) + ((*(utf8+2) >> 2) & 0x0F);
*(pOutput+2) = ((*utf8 << 2) & 0x1C) + ((*(utf8+1) >> 4) & 0x03);
break;
case 5:
*pOutput = (*(utf8+3) << 6) + (*(utf8+4) & 0x3F);
*(pOutput+1) = (*(utf8+2) << 4) + ((*(utf8+3) >> 2) & 0x0F);
*(pOutput+2) = (*(utf8+1) << 2) + ((*(utf8+2) >> 4) & 0x03);
*(pOutput+3) = (*utf8 << 6);
break;
case 6:
*pOutput = (*(utf8+4) << 6) + (*(utf8+5) & 0x3F);
*(pOutput+1) = (*(utf8+4) << 4) + ((*(utf8+5) >> 2) & 0x0F);
*(pOutput+2) = (*(utf8+2) << 2) + ((*(utf8+3) >> 4) & 0x03);
*(pOutput+3) = ((*utf8 << 6) & 0x40) + (*(utf8+1) & 0x3F);
break;
default:
return 0;
break;
}
return utfBytes;
}
/*
* 函数介绍:写入日志文件
* 参数:
* 返回值:
* 备注:
*/
int writeLog(const char *format, ...)
{
FILE *pFile = fopen("logs.log", "a+");
if( pFile == NULL ){
return -1;
}
va_list arg;
int done = 0;
va_start(arg, format);
time_t time_log = time(NULL);
struct tm *tm_log = localtime(&time_log);
fprintf(pFile, "%04d-%02d-%02d %02d:%02d:%02d ", tm_log->tm_year + 1900, tm_log->tm_mon + 1, tm_log->tm_mday, tm_log->tm_hour, tm_log->tm_min, tm_log->tm_sec);
done = vfprintf(pFile, format, arg);
va_end(arg);
fflush(pFile);
fclose(pFile);
return done;
}
程序代码
1 # INI文件由节、键、值组成
2 # 节
3 # [section]
4 # 参数(键=值)
5 # name = value
6 # 注解使用 # 号,开头为 # 号的行为注释
7 [file]
8 inputArc.csv=边信息.csv
9 inputVex.csv=节点信息.csv
10 addArc.csv=新增边.csv
11 addVex.csv=新增节点.csv
12 outputArc.csv=输出边.csv
13 outputVex.csv=输出节点.csv