main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "tree.h"
#include "hash.h"
#include "file.h"
#include "op.h"
int main(int argc, char **argv)
{
int ret = -1;
int op = -1;
int key = -1;
char YN = '\0';
char filename[100] = "../Database.txt"; //数据库文件路径
srand((unsigned int)time(NULL)); //随机数初始化
HashTable *myhash = InitHashTable(); //哈希表初始化
if (NULL == myhash)
{
return NULLERROR;
}
else
{
puts("提示:哈希表初始化完成!");
}
ret = InputTxt(myhash, filename);
if (ret == 0)
{
puts("提示:信息载入完成!");
}
UserData mydata; //数据
memset(&mydata, 0, sizeof(mydata)); //清空数据
TreeNode *pnode = NULL;
while (1)
{
if (NULL == myhash)
{
puts("提示:哈希表无法进行操作,已自动退出!");
return NULLERROR;
}
menu(&op);
switch (op)
{
case EXIT: //退出
{
if (OK == DestroyHashTable(&myhash))
{
puts("提示:哈希表清空内存!");
puts("提示:退出系统!");
return OK;
}
return ERROR;
break;
}
case FIND: //搜索
{
printf("请输入编号:");
scanf("%d", &key);
FindHashTable(myhash, key);
break;
}
case INSERT: //插入
{
printf("请输入插入信息!\n");
memset(&mydata, 0, sizeof(mydata)); //清空数据
printf("例子:220911887\n输入编号:");
if (EOF == scanf("%d", &mydata.key))
{
puts("提示:输入格式错误!");
memset(&mydata, 0, sizeof(mydata)); //清空数据
break;
}
printf("例子:罗立红\n输入姓名:");
if (EOF == scanf("%s", mydata.name))
{
puts("提示:输入格式错误!");
memset(&mydata, 0, sizeof(mydata)); //清空数据
break;
}
printf("例子:39\n输入年龄:");
if (EOF == scanf("%d", &mydata.age))
{
puts("提示:输入格式错误!");
memset(&mydata, 0, sizeof(mydata)); //清空数据
break;
}
printf("例子:176\n输入身高:");
if (EOF == scanf("%f", &mydata.height))
{
puts("提示:输入格式错误!");
memset(&mydata, 0, sizeof(mydata)); //清空数据
break;
}
printf("例子:18779338687\n输入手机号:");
if (EOF == scanf("%ld", &mydata.tel))
{
puts("提示:输入格式错误!");
memset(&mydata, 0, sizeof(mydata)); //清空数据
break;
}
ret = InsertHashTable(myhash, &mydata);
if (ret == 0)
{
puts("提示:插入信息完成!");
}
else
{
puts("提示:插入信息失败!");
}
memset(&mydata, 0, sizeof(mydata)); //清空数据
break;
}
case DELETE: //删除
{
printf("请输入删除信息的编号:");
scanf("%d", &mydata.key);
ret = DeleteHashTable(myhash, &mydata);
if (ret == 0)
{
puts("提示:删除信息完成!");
}
pnode = NULL;
break;
}
case UPDATE: //更新
{
printf("请输入更新信息的编号:");
scanf("%d", &mydata.key);
UpdateHashTable(myhash, &mydata);
puts("提示:更改完成!");
break;
}
case SHOW: //查看
{
ShowHashTable(myhash);
break;
}
case LOAD: //重新载入
{
printf("请输入文件路径:");
scanf("%s", filename);
ret = InputTxt(myhash, filename);
if (OK == ret)
{
puts("提示:信息载入完成!");
}
else
{
puts("提示:信息载入失败!");
}
break;
}
case SAVE: //保存
{
ret = OutputTxt(myhash, filename);
if (ret == 0)
{
puts("提示:信息已保存!");
}
else
{
puts("提示:信息保存失败!");
}
break;
}
case SAVEEXIT: //保存并退出
{
if (OK == OutputTxt(myhash, filename))
{
puts("提示:信息保存成功!");
if (OK == DestroyHashTable(&myhash))
{
puts("提示:哈希表清空内存!");
puts("提示:退出系统!");
return OK;
}
return ERROR;
}
else
{
puts("提示:信息保存失败!");
}
break;
}
default:
{
puts("提示:输入选项无效!");
break;
}
}
}
return OK;
}
tree.h
#ifndef _TREE_H_
#define _TREE_H_
//个数
#define N 100
//返回值枚举变量
enum ret
{
FILEERROR = -4,
MALLOCERROR = -3, //开辟空间错误
NULLERROR, //空指针错误
ERROR, //错误
OK, //正常
};
//数据类型
typedef struct _Data_
{
float height; //身高
long tel; //手机号
int key; //编号
int age; //年龄
char name[20]; //姓名
} UserData;
//树结点
typedef struct _TreeNode_
{
UserData data; //数据域
struct _TreeNode_ *left; //左结点指针域
struct _TreeNode_ *right; //右结点指针域
struct _TreeNode_ *parent; //父结点指针域
} TreeNode;
//树
typedef struct _Tree_
{
TreeNode *root; //根节点指针
int Treecount; //树结点计数
} Tree;
//初始化二叉树
Tree *InitTree(void);
//创建二叉树结点
TreeNode *CreateTreeNode(const UserData *data);
//插入二叉树结点
int InsertTreeNode(Tree *T, TreeNode *pnode);
//查找二叉树
TreeNode *FindTreeNode(Tree *T, int key);
//删除树结点
TreeNode *DeleteTreeNode(Tree *T, TreeNode *pnode);
//查找左子树最大的结点(前驱结点)
TreeNode *LeftTreeMAX(TreeNode *pnode);
//查找右子树最小的结点(后继结点)
TreeNode *RightTreeMIN(TreeNode *pnode);
//查看树
int ShowTree(TreeNode *pnode);
//销毁树结点
int DestroyTreeNode(TreeNode **pnode);
//销毁树以下所有结点
int DestroyTree(Tree *T);
//制造随机数据
int CreateData(UserData *data);
#endif
tree.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tree.h"
//初始化二叉树
Tree *InitTree(void)
{
Tree *T = NULL;
T = (Tree *)malloc(sizeof(Tree)); //开辟树空间
if (NULL == T)
{
return NULL;
}
memset(T, 0, sizeof(Tree));
T->Treecount = 0; //初始化树
T->root = NULL;
return T;
}
//创建二叉树结点
TreeNode *CreateTreeNode(const UserData *data)
{
if (NULL == data)
{
return NULL;
}
TreeNode *pnode = NULL;
pnode = (TreeNode *)malloc(sizeof(TreeNode)); //开辟树结点空间
if (NULL == pnode)
{
return NULL;
}
memset(pnode, 0, sizeof(TreeNode));
pnode->data = *data; //初始化树结点
pnode->left = NULL;
pnode->right = NULL;
pnode->parent = NULL;
return pnode;
}
//插入二叉树结点
int InsertTreeNode(Tree *T, TreeNode *pnode)
{
if (NULL == T || NULL == pnode)
{
return ERROR;
}
//若插入时是空树,则插入到根节点
if (NULL == T->root)
{
T->root = pnode;
}
else //若非根结点
{
TreeNode *p1 = T->root;
TreeNode *p2 = T->root;
//找到插入结点的父结点地址
while (NULL != p1)
{
p2 = p1;
if (p1->data.key > pnode->data.key)
{
p1 = p1->left;
}
else if (p1->data.key < pnode->data.key)
{
p1 = p1->right;
}
else
{
puts("提示:已存在该结点!");
return -1;
}
}
//插入到左结点
if (p2->data.key > pnode->data.key)
{
p2->left = pnode;
}
else //插入到右结点
{
p2->right = pnode;
}
pnode->parent = p2; //连接父结点
}
T->Treecount++; //树结点个树加1
return 0;
}
//查找二叉树
TreeNode *FindTreeNode(Tree *T, int key)
{
if (NULL == T)
{
return NULL;
}
TreeNode *p1 = T->root;
//查找结点
while (NULL != p1)
{
if (p1->data.key > key)
{
p1 = p1->left;
}
else if (p1->data.key < key)
{
p1 = p1->right;
}
else
{
printf("提示:已找到!\n");
return p1;
}
}
//没有查找到结点
printf("提示:没找到%d!\n", key);
return NULL;
}
//删除树结点
TreeNode *DeleteTreeNode(Tree *T, TreeNode *pnode)
{
if (NULL == T || NULL == pnode)
{
return NULL;
}
TreeNode *p1 = pnode;
//若结点是叶结点
if (NULL == p1->left && NULL == p1->right)
{
if (T->root == p1) //若结点是根节点
{
T->root = NULL;
}
else
{
if (p1 == p1->parent->left)
{
p1->parent->left = NULL; //父结点的左分支指向NULL
}
else if (p1 == p1->parent->right)
{
p1->parent->right = NULL; //父结点的右分支指向NULL
}
}
T->Treecount--; //树结点个树减1
}
//若结点有两个子结点
else if (NULL != p1->left && NULL != p1->right)
{
//找到后继结点,数据跟后继结点交换,删除后继结点
p1 = RightTreeMIN(p1->right);
pnode->data = p1->data;
DeleteTreeNode(T, p1);
}
//若结点有一个子结点
else if (NULL != p1->left || NULL != p1->right)
{
if (T->root == p1) //若结点是根节点
{
if (NULL != p1->left)
{
T->root = p1->left; //把子结点设置根节点
p1->left->parent = NULL; //子结点的父指针清空
}
else if (NULL != p1->right)
{
T->root = p1->right; //把子结点设置根节点
p1->right->parent = NULL; //子结点的父指针清空
}
}
else
{
if (p1 == p1->parent->left) //结点是父结点的左分支
{
if (NULL != p1->left) //结点有左分支
{
p1->parent->left = p1->left; //结点的父结点的左分支指向结点的左分支
p1->left->parent = p1->parent; //结点左分支的父指针指向结点的父结点
}
else if (NULL != p1->right) //结点有右分支
{
p1->parent->left = p1->right; //结点的父结点的左分支指向结点的右分支
p1->right->parent = p1->parent; //结点右分支的父指针指向结点的父结点
}
}
else if (p1 == p1->parent->right)
{
if (NULL != p1->left)
{
p1->parent->right = p1->left;
p1->left->parent = p1->parent;
}
else if (NULL != p1->right)
{
p1->parent->right = p1->right;
p1->right->parent = p1->parent;
}
}
}
T->Treecount--; //树结点个树减1
}
return p1;
}
//查找左子树最大的结点(前驱结点)
TreeNode *LeftTreeMAX(TreeNode *pnode)
{
if (NULL == pnode)
{
return NULL;
}
while (NULL != pnode->right) //左树右转
{
pnode = pnode->right;
}
return pnode;
}
//查找右子树最小的结点(后继结点)
TreeNode *RightTreeMIN(TreeNode *pnode)
{
if (NULL == pnode)
{
return NULL;
}
while (NULL != pnode->left) //右树左转
{
pnode = pnode->left;
}
return pnode;
}
//查看树
int ShowTree(TreeNode *pnode)
{
if (NULL == pnode)
{
return ERROR;
}
//中序输出
ShowTree(pnode->left);
printf("编号:%8d,姓名:%-10s,年龄:%-2d ,身高:%.1f,手机号:%ld;\n",
pnode->data.key, pnode->data.name, pnode->data.age, pnode->data.height, pnode->data.tel);
ShowTree(pnode->right);
return OK;
}
//销毁树结点
int DestroyTreeNode(TreeNode **pnode)
{
if (NULL == pnode)
{
return ERROR;
}
if (NULL == *pnode)
{
return OK;
}
DestroyTreeNode(&(*pnode)->left); //销毁左分支
DestroyTreeNode(&(*pnode)->right); //销毁右分支
if (NULL == (*pnode)->left && NULL == (*pnode)->right) //左右分支销毁完毕
{
memset((*pnode), 0, sizeof(TreeNode)); //清空内存
free((*pnode)); //释放内存
*pnode = NULL; //指针指向NULL
}
else
{
puts("销毁树结点失败!");
return ERROR;
}
return OK;
}
//销毁树所有结点,根节点指向NULL,数量清空
int DestroyTree(Tree *T)
{
if (NULL == T)
{
return ERROR;
}
if (OK == DestroyTreeNode(&(T->root))) //若销毁完根节点
{
T->Treecount = 0; //树结点个数清零
return OK;
}
return ERROR;
}
//制造随机数据
int CreateData(UserData *data)
{
if (NULL == data)
{
return NULLERROR;
}
data->height = (float)(rand() % 200) / 10 + 160; //身高160~180
data->key = 22091 * 1000 + rand() % 1000; //编号22091000~22091999
data->age = 18 + rand() % 62; //年龄18~100
data->tel = (long)1877933 * 10000 + rand() % 10000; //手机号18779330000~18779339999
char temp[20] = {97 + rand() % 26, 97 + rand() % 26, 97 + rand() % 26, 97 + rand() % 26, 97 + rand() % 26, 97 + rand() % 26};
strcpy(data->name, temp);
return OK;
}
op.h
#ifndef _OP_H_
#define _OP_H_
#include <tree.h>
#include <hash.h>
#include <file.h>
enum op
{
EXIT = -1, //退出
FIND = 1, //搜索
INSERT, //插入
DELETE, //删除
UPDATE, //更新
SHOW, //查看
LOAD, //重新载入
SAVE, //保存
SAVEEXIT //保存并退出
};
//菜单函数
void menu(int *op);
#endif
op.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "op.h"
void menu(int *op)
{
if (NULL == op)
{
return;
}
printf("——————————————————————————————————————————————————————————————————\n");
printf("\t\t\t 菜单\n");
printf("——————————————————————————————————————————————————————————————————\n");
printf("\t\t\t 请输入操作选项\n");
printf("\t\t\t 1——搜索编号\n");
printf("\t\t\t 2——插入信息\n");
printf("\t\t\t 3——删除信息\n");
printf("\t\t\t 4——更改信息\n");
printf("\t\t\t 5——查看所有信息\n");
printf("\t\t\t 6——重新载入信息\n");
printf("\t\t\t 7——保存当前信息\n");
printf("\t\t\t 8——保存并退出\n");
printf("\t\t\t-1——退出系统\n");
printf("——————————————————————————————————————————————————————————————————\n");
printf("请输入选项:");
scanf("%d", op);
return;
}
hash.h
#ifndef _HASH_H_
#define _HASH_H_
#include <tree.h>
//哈希表长
#define M 13
//哈希表
typedef struct _HashTable_
{
Tree *tree; //树指针
int Hashcount; //哈希表结点计数
} HashTable;
//初始化哈希表
HashTable *InitHashTable(void);
//插入哈希表数据
int InsertHashTable(HashTable *myhashtable, UserData *mydata);
//删除哈希表数据
int DeleteHashTable(HashTable *myhashtable, UserData *mydata);
//更新哈希表数据
int UpdateHashTable(HashTable *myhashtable, UserData *mydata);
//查看哈希表数据
int ShowHashTable(HashTable *myhashtable);
//搜索哈希表数据
TreeNode *FindHashTable(HashTable *myhashtable, int key);
//哈希表信息数量
void HashTableCount(HashTable *myhashtable);
//哈希函数
int GetHash(int key);
//清空哈希表
int CleanHashTable(HashTable *myhashtable);
//销毁哈希表
int DestroyHashTable(HashTable **myhashtable);
#endif
hash.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hash.h"
//初始化哈希表
HashTable *InitHashTable(void)
{
HashTable *phash = (HashTable *)malloc(sizeof(HashTable)); //开辟哈希表空间
if (NULL == phash)
{
return NULL;
}
memset(phash, 0, sizeof(HashTable));
phash->Hashcount = 0; //对哈希表初始化
phash->tree = (Tree *)malloc(sizeof(Tree) * M); //开辟哈希表每个key空间。为了地址连续,没有用初始化树函数。
if (NULL == phash->tree)
{
free(phash); //若开辟失败,则释放上次开辟的空间
return NULL;
}
memset(phash->tree, 0, sizeof(HashTable) * M);
int i = 0;
for (i = 0; i < M; i++) //循环对每个key头数据初始化
{
phash->tree[i].Treecount = 0;
phash->tree[i].root = NULL;
}
return phash; //返回哈希表
}
//插入哈希表
int InsertHashTable(HashTable *myhashtable, UserData *mydata)
{
if (NULL == myhashtable || NULL == mydata)
{
return ERROR;
}
int KEY = GetHash(mydata->key); //获取KEY
TreeNode *pnode = NULL;
pnode = CreateTreeNode(mydata); //创建结点
if (NULL != pnode) //若成功创建结点
{
if (OK == InsertTreeNode(&myhashtable->tree[KEY], pnode)) //插入结点
{
return OK;
}
}
return ERROR;
}
//删除哈希表数据
int DeleteHashTable(HashTable *myhashtable, UserData *mydata)
{
if (NULL == myhashtable || NULL == mydata)
{
return ERROR;
}
int KEY = GetHash(mydata->key); //获取KEY
TreeNode *pnode = NULL;
pnode = FindTreeNode(&myhashtable->tree[KEY], mydata->key); //根据key查找结点
if (NULL != pnode) //若成功找到结点
{
pnode = DeleteTreeNode(&myhashtable->tree[KEY], pnode); //删除结点
if (NULL != pnode) //若成功删除结点
{
printf("编号:%8d,姓名:%-10s,年龄:%-2d ,身高:%.1f,手机号:%ld;\n",
pnode->data.key, pnode->data.name, pnode->data.age, pnode->data.height, pnode->data.tel);
free(pnode); //释放结点
}
else
{
return ERROR;
}
}
else
{
return ERROR;
}
return OK;
}
//更新哈希表数据
int UpdateHashTable(HashTable *myhashtable, UserData *mydata)
{
if (NULL == myhashtable || NULL == mydata)
{
return ERROR;
}
int KEY = GetHash(mydata->key); //获取KEY
TreeNode *pnode = NULL;
pnode = FindTreeNode(&myhashtable->tree[KEY], mydata->key); //根据key查找结点
if (NULL != pnode) //若成功找到结点
{
*mydata = pnode->data;
printf("原姓名:%s\n输入新姓名:", (*mydata).name);
if (EOF == scanf("%s", (*mydata).name))
{
puts("提示:输入格式错误!");
memset(mydata, 0, sizeof(mydata)); //清空数据
return ERROR;
}
printf("原年龄:%d\n输入新年龄:", (*mydata).age);
if (EOF == scanf("%d", &(*mydata).age))
{
puts("提示:输入格式错误!");
memset(mydata, 0, sizeof(mydata)); //清空数据
return ERROR;
}
printf("原身高:%.1f\n输入新身高:", (*mydata).height);
if (EOF == scanf("%f", &(*mydata).height))
{
puts("提示:输入格式错误!");
memset(mydata, 0, sizeof(mydata)); //清空数据
return ERROR;
}
printf("原手机号:%ld\n输入新手机号:", (*mydata).tel);
if (EOF == scanf("%ld", &(*mydata).tel))
{
puts("提示:输入格式错误!");
memset(mydata, 0, sizeof(mydata)); //清空数据
return ERROR;
}
pnode->data = *mydata; //把新数据给该结点
memset(mydata, 0, sizeof(mydata)); //清空数据
}
return OK;
}
//查看哈希表数据
int ShowHashTable(HashTable *myhashtable)
{
if (NULL == myhashtable)
{
return ERROR;
}
HashTableCount(myhashtable); //计算哈希表结点数量
printf("哈希表数据信息总量:%d\n", myhashtable->Hashcount);
int i = 0;
for (i = 0; i < M; i++)
{
printf("(KEY=%d)的%d条信息,详细信息如下:\n", i, myhashtable->tree[i].Treecount);
ShowTree(myhashtable->tree[i].root);
}
printf("哈希表数据信息总量:%d\n", myhashtable->Hashcount);
return OK;
}
//搜索哈希表数据
TreeNode *FindHashTable(HashTable *myhashtable, int key)
{
if (NULL == myhashtable)
{
return NULL;
}
int KEY = GetHash(key); //获取KEY
TreeNode *pnode = FindTreeNode(&myhashtable->tree[KEY], key); //根据key查找结点
if (NULL == pnode)
{
return NULL;
}
else
{
printf("编号:%8d,姓名:%-10s,年龄:%-2d ,身高:%.1f,手机号:%ld;\n",
pnode->data.key, pnode->data.name, pnode->data.age, pnode->data.height, pnode->data.tel);
}
return pnode;
}
//哈希表信息数量
void HashTableCount(HashTable *myhashtable)
{
if (NULL == myhashtable)
{
return;
}
myhashtable->Hashcount = 0;
int i = 0;
for (i = 0; i < M; i++)
{ //哈希表结点数量累加
myhashtable->Hashcount = myhashtable->Hashcount + myhashtable->tree[i].Treecount;
}
}
//哈希函数
int GetHash(int num)
{
return num % M;
}
//清空哈希表
int CleanHashTable(HashTable *myhashtable)
{
if (NULL == myhashtable)
{
return ERROR;
}
int i = 0;
for (i = 0; i < M; i++)
{
if (OK != DestroyTree(&myhashtable->tree[i])) //清空每个key下面结点
{
return ERROR;
}
}
return OK;
}
//销毁哈希表
int DestroyHashTable(HashTable **myhashtable)
{
if (NULL == myhashtable || NULL == *myhashtable)
{
return ERROR;
}
if (OK == CleanHashTable(*myhashtable)) //清空哈希表下结点
{
free((*myhashtable)->tree); //释放结点头空间
free(*myhashtable); //释放哈希表空间
*myhashtable = NULL;
return OK;
}
return ERROR;
}
file.h
#ifndef _FILE_H_
#define _FILE_H_
#include "hash.h"
//从文本文件到哈希表
int InputTxt(HashTable *myhashtable, char *filename);
//从哈希表到文本文件
int OutputTxt(HashTable *myhashtable, char *filename);
//中序输出至文件流
int ShowTreeTxt(FILE *fp, TreeNode *pnode);
#endif
file.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "file.h"
//从文本文件到哈希表
int InputTxt(HashTable *myhashtable, char *filename)
{
if (NULL == myhashtable || NULL == filename)
{
return ERROR;
}
FILE *fp = fopen(filename, "r"); //打开文件
if (NULL == fp)
{
printf("提示:无法打开文件!\n");
return FILEERROR;
}
if (OK == CleanHashTable(myhashtable))
{
UserData data; //数据
while (!feof(fp)) //! feof(fp)判断标准IO文件流是否到末尾 文件IO是否到末尾用-1
{
memset(&data, 0, sizeof(data)); //清空数据
//读取一组数据(5个)插入到哈希表一次
if (5 == fscanf(fp, "%d%s%d%f%ld",
&data.key, data.name, &data.age, &data.height, &data.tel))
{
InsertHashTable(myhashtable, &data); //插入哈希表
}
}
fclose(fp); //关闭文件
}
else
{
fclose(fp); //关闭文件
printf("提示:清空旧数据失败!\n");
return ERROR;
}
return OK;
}
//从哈希表到文本文件
int OutputTxt(HashTable *myhashtable, char *filename)
{
if (NULL == myhashtable || NULL == filename)
{
return ERROR;
}
FILE *fp = fopen(filename, "w+"); //打开文件
if (NULL == fp)
{
printf("提示:无法打开文件!\n");
return FILEERROR;
}
int i = 0;
for (i = 0; i < M; i++) //循环哈希表每个位置
{
ShowTreeTxt(fp, myhashtable->tree[i].root); //中序输出至文件流
}
fclose(fp); //关闭文件
return OK;
}
//中序输出至文件流
int ShowTreeTxt(FILE *fp, TreeNode *pnode)
{
if (NULL == fp || NULL == pnode)
{
return ERROR;
}
//中序输出至文件流
ShowTreeTxt(fp, pnode->left);
fprintf(fp, "%8d %-10s %-2d %.1f %ld\n",
pnode->data.key, pnode->data.name, pnode->data.age, pnode->data.height, pnode->data.tel);
ShowTreeTxt(fp, pnode->right);
return OK;
}