图书管理系统

#include    "windows.h"
#include    "stdio.h"
#include    "stdlib.h"
#include    "conio.h"

#define        OK        1
#define        ERROR    0
#define        TRUE    1
#define        FALSE    0
#define        OVERFLOW -1
#define        MAX_NAME_LEN    20                        // 姓名最大长度
#define        MAX_BKNAME_LEN    30                        // 书名最大长度
#define        MAX_BOOKS        100                        // 书库中一个著者最多著作数
#define        KEEP_DAYS        90                        // 图书出借的期限
#define        logfile            "LibraryLogs.log"        // 系统日志文件
typedef        int                Status;

char         *books[MAX_BOOKS];                        // 某位著者著作名指针数组
char         author[MAX_NAME_LEN];                    // 著者姓名数组
int            books_counter;                            // 著者著作计数

typedef    struct    ReaderNode                            // 借阅者
{
    int        cardnum;                                // 借阅证号
    char    Readername[MAX_NAME_LEN];                // 借阅者姓名
    union
    {
        struct
        {
            struct    ReaderNode *nextr;                // 下一个借阅者指针
        };
        struct
        {
            struct    ReaderNode *nextb;                // 下一个预约者指针
        };
    };
}ReaderNode,*ReaderType;                            // 读者类型


typedef    struct    BookNode                            // 图书结构体
{
    int            booknum;                            // 书号
    char        bookname[MAX_BKNAME_LEN];            // 书名
    char        writer[MAX_NAME_LEN];                // 著者名
    int            current, total;                        // 现存量和总库存
    int            publishyear;                        // 出版年份
    float        price;                                // 定价
    ReaderType    reader;                                // 读者链表指针
    ReaderType    appointmenter;                        // 预约者链表指针
} BookNode,*BookType;                                // 图书类型


#define        m         3                // 定义3叉B树
typedef    BookNode    Record;            // 记录指针为图书结点类型
typedef    int            KeyType;

typedef        struct    BTNode            // B树结点
{
    int                keynum;            // 每个结点关键字个数
    struct    BTNode    *parent;        // 父亲指针
    KeyType            key[m+1];        // 关键字数组,0号单元未用
    struct    BTNode    *ptr[m+1];        // 子数指针
    Record            *rec[m+1];        // 记录指针,0号单元未用
}BTNode,*BTree;                        // B树节点类型和B树类型
typedef        BTree    Library;

typedef    struct
{
    BTNode    *pt;                    // 指向找到的结点或应该插入的结点
    int        i;                        // 关键字序号
    int        tag;                    // 1表示查找成功,0表示查找失败
}Result;                            // B树查找结果类型


void NewRoot(BTree T, BTree p, KeyType k, BTree ap,Record *rec)
// 当插入B树时T为空或根结点分裂为q和ap两个节点,需建立一个根节点空间
// 本函数为T申请一块空间,插入p,k,ap和记录rec
{
    T = (BTree)malloc(sizeof(BTNode));
    T->keynum = 1;
    T->ptr[0] = p;                                // 插入
    T->ptr[1] = ap;
    T->key[1] = k;
    T->rec[1] = rec;
    if (p) p->parent= T;                        // 刷新T的子树ap的父亲指针
    if (ap) ap->parent = T;
    T->parent = NULL;                            // 根节点双亲为NULL
}

void    Insert(BTree q, int    i, KeyType k, BTree ap, Record *rec)
// 将k和ap分别插入到q->key[i+1]和q->ptr[i+1],并插入关键字为k的记录rec
{
    int        j;
    for (j = q->keynum;j > i; j--)               // 记录、关键字、子树指针后移
    {
        q->key[j+1] = q->key[j];
        q->ptr[j+1] = q->ptr[j];
        q->rec[j+1] = q->rec[j];
    }
    q->key[i+1] = k;                            // 插入
    q->ptr[i+1] = ap;
    q->rec[i+1] = rec;
    q->keynum ++;                                // 关键字个数增1
    if (ap) ap->parent = q;                       // 刷新q的子树ap的父亲指针
}

void    Split(BTree    q, int n, BTree ap)
// 以n为分界将结点q分裂为q和ap2个结点
{
    int        i;
    ap = (BTree)malloc(sizeof(BTNode));            // 新申请ap空间
    ap->ptr[0] = q->ptr[n];
    for (i = n+1;i <= m; i++)                   // q上n后的关键字、子树指针、记录转移到ap
    {
        ap->key[i-n] = q->key[i];
        ap->ptr[i-n] = q->ptr[i];
        ap->rec[i-n] = q->rec[i];
    }
    ap->keynum = q->keynum - n;                    // 计算ap的关键字个数
    q->keynum = n-1;                            // q的关键字个数减少
    ap->parent = q->parent;
    for (i=0; i<=m-n; i++)
        if (ap->ptr[i]) ap->ptr[i]->parent = ap;   // 刷新ap的子树的父亲指针
}

int  Search(BTree p, KeyType k)
// 在B树p结点中查找关键字k的位置i,使key[i]<=k<key[i+1])
{
    int        i;
    for (i=0; i < p->keynum && (p->key[i+1] < k||p->key[i+1] == k); i++);
    return i;
}

Status InsertBTree(BTree T, KeyType k, BTree q, int i,Record *rec)
//  在m阶B树T上结点*q的key[i]与key[i+1]之间插入关键字K和记录rec。
//  若引起结点过大,则沿双亲链进行必要的结点分裂调整,使T仍是m阶B树。
{
    BTree ap = NULL;
    int finished = FALSE;
    if (!q)    NewRoot(T, NULL, k, NULL,rec);    // T是空树,生成仅含关键字K的根结点*T
    else
    {
        while (!finished)
        {
            Insert(q, i, k, ap,rec);            // 将k和ap分别插入到q->key[i+1]和q->ptr[i+1]
            if (q->keynum < m) finished = TRUE; // 插入完成
            else
            {
                Split(q, (m+1)/2, ap);            // 分裂结点Q                           调用前面的
                k = q->key[(m+1)/2];
                rec = q->rec[(m+1)/2];
                if (q->parent)
                {
                    // 在双亲结点*q中查找k的插入位置
                    q = q->parent;
                    i = Search(q, k);
                }
                else finished = OVERFLOW;        // 根节点已分裂为*q和*ap两个结点
            }
        }
        if (finished == OVERFLOW)                // 根结点已分裂为结点*q和*ap
            NewRoot(T, q, k, ap,rec);            // 需生成新根结点*T,q和ap为子树指针
    }
    return OK;
} //  InsertBTree



Result    SearchBTree(BTree T, KeyType k)
// 在m阶B树上查找关键字k,返回结果(pt,i,tag)。若查找成功,则特征值tag=1,指针pt所指结点中第i个关键字等于k;
// 否则返回特征值tag=0,等于k的关键字应插入在pt所指结点中第i和第i+1个关键字之间。
{
    int        i = 1;
    BTree    p = T, q = NULL;                        // 初始化,p指向待查结点,q指向p的双亲
    int        found = FALSE;
    while (p && !found)
    {
        i = Search(p, k);                            // 查找k的位置使p->key[i]<=k<p->key[i+1]
        if (i> 0 && k == p->key[i])    found = TRUE;
        else                                         // 未找到,则查找下一层
        {
            q = p;
            p = p->ptr[i];
        }
    }
    if (found)
    {
        Result    r = {p, i, 1};       // 查找成功
        return r;
    }
    else
    {
        Result    r = {q, i, 0};       // 查找不成功,返回k的插入位置信息
        return r;
    }
}

void    TakePlace(BTree q, int i)
// *q结点的第i个关键字为k,用q的后继关键字替代q,且令q指向后继所在结点,
{
    BTree    p = q;
    q = q->ptr[i];
    while (q->ptr[0]) q = q->ptr[0];               // 搜索p的后继
    p->key[i] = q->key[1];                        // 关键字代替
    p->rec[i] = q->rec[1];                        // 记录代替
    i = 1;                                        // 代替后应该删除q所指结点的第1个关键字
}

void    Del(BTree q, int i)
// 删除q所指结点第i个关键字及其记录
{
    for (;i < q->keynum ;i++)                   // 关键字和记录指针前移
    {
        q->key[i] = q->key[i+1];
        q->rec[i] = q->rec[i+1];
    }
    q->keynum --;                                // 关键字数目减1
}

Status    Borrow(BTree q)
// 若q的兄弟结点关键字大于(m-1)/2,则从兄弟结点上移最小(或最大)的关键字到双亲结点,
// 而将双亲结点中小于(或大于)且紧靠该关键字的关键字下移至q中,并返回OK,否则返回EREOR。
{
    int        i;
    BTree    p = q->parent, b;                    // p指向q的双亲结点
    for (i = 0 ; p->ptr[i] != q;i++) ;           // 查找q在双亲p的子树位置
    if (i >= 0 && i+1 <= p->keynum && p->ptr[i+1]->keynum > (m-1)/2)
    {
        // 若q的右兄弟关键字个数大于(m-1)/2
        b = p->ptr[i+1];                        // b指向右兄弟结点
        q->ptr[1] = b->ptr[0];                    // 子树指针也要同步移动
        q->key[1] = p->key[i+1];                // 从父节点借第i+1个关键字
        q->rec[1] = p->rec[i+1];
        p->key[i+1] = b->key[1];                // b第一个关键字上移到父节点
        p->rec[i+1] = b->rec[1];
        for (i =1 ;i <= b->keynum;i++)           // b第一个关键字上移,需把剩余记录前移一位
        {
            b->key[i] = b->key[i+1];
            b->rec[i] = b->rec[i+1];
            b->ptr[i-1] = b->ptr[i];
        }
    }
    else if (i > 0 &&  p->ptr[i-1]->keynum > (m-1)/2)
    {
        // 若q的左兄弟关键字个数大约(m-1)/2
        b = p->ptr[i-1];                        // b指向左兄弟结点
        q->ptr[1] = q->ptr[0];
        q->ptr[0] = b->ptr[b->keynum];
        q->key[1] = p->key[i];                    // 从父节点借第i个关键字
        q->rec[1] = p->rec[i];
        p->key[i] = b->key[b->keynum];            // 将b最后一个关键字上移到父节点
        p->rec[i] = b->rec[b->keynum];
    }
    else return ERROR;                            // 无关键字大于(m-1)/2的兄弟
    q->keynum ++;
    b->keynum --;
    for (i = 0 ;i <=q->keynum; i++)
        if (q->ptr[i]) q->ptr[i]->parent = q;   // 刷新q的子结点的双亲指针
    return OK;
}

void    Combine(BTree q)
// 将q剩余部分和q的父结点的相关关键字合并到q兄弟中,然后释放q,令q指向修改的兄弟
{
    int        i, j ;
    BTree p = q->parent, b;                        // p指向q的父亲
    for (i = 0; p->ptr[i] != q; i++) ;           // 插好q在父亲p中的子树位置
    if (i == 0)                                   // 如为0,则需合并为兄弟的第一个关键字
    {
        b = p->ptr[i+1];
        for (j = b->keynum ; j >= 0 ;j--)       // 将b的关键字和记录后移一位
        {
            b->key[j+1] = b->key[j];
            b->rec[j+1] = b->rec[j];
            b->ptr[j+1] = b->ptr[j];
        }
        b->ptr[0] = q->ptr[0];                    // 合并
        b->key[1] = p->key[1];
        b->rec[1] = p->rec[1];
    }
    else if (i > 0)                               // 若q在父亲的子树位置大约0
    {
        // 需合并为兄弟b的最后一个关键字
        b = p->ptr[i-1];
        b->key[b->keynum+1] = p->key[i];        // 合并
        b->rec[b->keynum+1] = p->rec[i];
        b->ptr[b->keynum+1] = q->ptr[0];
    }
    if (i == 0 || i == 1)                       // 若i为0或1,需将父节点p关键字前移一位
        for ( ; i < p->keynum; i++)
        {
            p->key[i] = p->key[i+1];
            p->ptr[i] = p->ptr[i+1];
            p->rec[i] = p->rec[i+1];
        }
    p->keynum --;
    b->keynum ++;
    free(q);
    q = b;                                        // q指向修改的兄弟结点
    for (i = 0;i <= b->keynum; i++)
        if (b->ptr[i]) b->ptr[i]->parent = b;   // 刷新b的子结点的双亲指针
}

Status    DeleteBTree(BTree T,KeyType k)
// 在m阶B树T上删除关键字k及其对应记录,并返回OK。如T上不存在关键字k,则返回ERROR。
{
    KeyType    x=k;
    BTree    q,b = NULL;
    int        finished = FALSE,i = 1;
    Result res = SearchBTree(T,k);                // 在T中查找关键字k
    if (res.tag == 0 ) return ERROR;               // 未搜索到
    else
    {
        q = res.pt;                                // q指向待删结点
        i = res.i;
        if (q->ptr[0]) TakePlace(q, i);           // 若q的子树不空,(非底层结点)
        // 则以其后继代之,且令q指向后继所在结点
        Del(q,i);                                // 删除q所指向结点中第i个关键字及记录
        if (q->keynum>=(m-1)/2||!q->parent)       // 若删除后关键字个数不小于(m-1)/2或q是根节点
        {
            finished = TRUE;                    // 删除完成
            if (q->keynum == 0 ) T = NULL;       // 若q的关键字个数为0 ,则为空树
        }
        while (!finished)
        {
            if (Borrow(q))    finished = TRUE;   // 若q的相邻兄弟结点关键字大于(m-1)/2,则从该
            // 兄弟结点上移一个最大(或最小)关键字到
            // 父节点,从父节点借一关键字到q
            else                                 // 若q相邻兄弟关键字个数均等于┌m /2┑-1
            {
                Combine(q);                        // 将q中的剩余部分和双亲中的相关关键字合并至q的一个兄弟中
                q = q->parent;                    // 检查双亲
                if (q == T && T->keynum ==0 )   // 若被删结点的父节点是根T且T的关键字个数为0
                {
                    T = T->ptr[0];                // 新根
                    T->parent = NULL;
                    free(q);                    // 删除原双亲结点
                    finished = TRUE;
                }
                else if (q->keynum >= m/2) finished = TRUE;
            }                                // 合并后双亲关键字个数不少于(m-1)/2,完成
        }
    }
    return OK ;
}

void    ShowBTree(BTree T,short x)
// 递归以凹入表形式显示B树T,每层的缩进量为x,初始缩进量为8
{
    int    i;
    x = x+7;
    if (!T)    return ;
    printf("\n");
    for (i = 0;i<=x;i++)
        putchar(' ');                          // 缩进x
    for (i = 1 ;i <= T->keynum;i++)
        printf("%d,",T->key[i]);
    for (i = 0 ;i <= T->keynum;i++)               // 递归显示子树结点关键字
        ShowBTree(T->ptr[i],x);
}




void    InitLibrary(Library L)
// 初始化书库L为空书库。
{
    L = NULL;
}

void    InsertBook(Library L ,BookType B , Result res)
// 书库L已存在,res包含B书在书库L中的位置或应该插入的位置
// 如果书库中已存在B书,则只将B书的库存量增加,否则插入B书到书库L中。
{
    if (res.tag == 0)
        InsertBTree(L, B->booknum, res.pt, res.i, B);    // 如果书库中不存在该书,则插入
    else                                                 // 如果已存在
    {
        BookType b = res.pt->rec[res.i];
        b->current = b->current + B->total;                   // 现存量和总库存增加
        b->total = b->total + B->total;
    }
}

Status    DeleteBook(Library L ,BookType B)
// 如果书库中存在B书,则从书库中删除B书的信息,并返回OK,否则返回ERROR
{
    if (DeleteBTree(L,B->booknum))    return OK;   // 如果删除成功,返回OK
    else return ERROR;                            // 否则(删除不成功)返回ERROR
}


int    BorrowBook(Library L ,BookType B ,ReaderType R)
// 书库L存在,B书是书库中的书并且可被读者R借阅
// 借出一本B书,登记借阅者R的信息,改变现存量,
{
    if (B->current > 0)                           // 若现存量大于0
    {
        B->reader = R;
        B->current--;                              // 现存量减1
    }
    return TRUE;
}

int        ReturnBook(Library L ,int b ,int r,BookType B ,ReaderType R)
// B为还书书号,R为还书者借阅证号, 若书库中不存在书号为B的书,则返回-1
// 若有R借阅B书的记录,则注销该记录,并用B和R返回图书信息和借阅者信息并返回1,
// 若没有r借阅b书的记录,则用B返回图书信息,并返回0
{
    ReaderType pre,p;
    Result    res = SearchBTree(L, b);        // 搜索
    if (!res.tag)
        return -1;                    // 未搜索到,返回-1
    B = res.pt->rec[res.i];                        // 用B记录图书信息
    p=res.pt->rec[res.i]->reader;
    for ( ; p ;pre = p,p = p->nextr)               // 搜索借书者链表
        if (p->cardnum == r)                       // 找到则用R返回借阅者信息
        {
            R = p;
            pre ->nextr = p->nextr;
            B->current++;                        // 现存量增1
            return 1;
        }
    return 0;                                    // 无该读者借阅该书信息则返回0
}






void    Menu()
// 显示图书管理系统菜单
{
    system("cls");
    printf("\n");
    printf("                        ╔══════════════╗\n");
    printf("                        ║    欢迎使用图书管理系统    ║\n");
    printf("                        ╚══════════════╝\n");
    printf(" \n\n\n");
    printf("\t  友情提示:本系统可进行的操作如下(1-9):\n");
    printf("\t  *****************************\n");
    printf("\t  *                           * \n");
    printf("\t  *    1     新书入库         * \n");
    printf("\t  *                           * \n");
    printf("\t  *    2     清除库存         * \n");
    printf("\t  *                           * \n");
    printf("\t  *    3     图书出借         * \n");
    printf("\t  *                           * \n");
    printf("\t  *    4     图书归还         * \n");
    printf("\t  *                           * \n");
    printf("\t  *    5     退出系统         * \n");
    printf("\t  *                           * \n");
    printf("\t  ***************************** \n");
}


void    PrintH()
// 打印图书表格表头
{
    printf("\n");
    printf("                    ╭═══════════════╮              \n");
    printf("╭═════════║       【  图书信息  】       ║════════════╮");
    printf("║───┬─────╰═══════════════╯───┬────┬───║");

    printf("║书号  │    书名              │ 著者       │现存│总库存│出版年份│定价  ║");

}

void    PrintT()
// 打印图书表格表尾
{
    printf("║───┼───────────┼──────┼──┼───┼────┼───║");
    printf("╰══════════════════════════════════════╯\n");
}

void    PrintD(BookType B )
// 显示B书的基本信息。
{
    printf("║───┼───────────┼──────┼──┼───┼────┼───║");
    printf("║ %-4d │《%s》",B->booknum,    B->bookname);
    //gotoxy(32,wherey());
    printf("│ %-11s│%-4d│ %-4d │%-6d  │%-6.1f║",B->writer,
           B->current,B->total,B->publishyear,B->price);
}

void    PrintBook(BookType B)
// 以表格形式显示一本书的基本信息(书号,书名,著者,现存量,总库存量,出版年份,价格)
{
    PrintH();        // 表头
    PrintD(B);        // 数据
    PrintT();        // 表尾
    printf("\n");
}



int    main()
{
    Library        L;
    int            booknum,cardnum;
    char        in;
    BookType    B;
    Result        res;
    ReaderType    R;
    short       x=8;                //初始缩进量为8
    int        k;
    L=NULL;
    
    while (1)
    {
        Menu();                            // 显示菜单
        in = getch();
        system("cls");
        switch (in-'0')                   // 判断用户选择
        {
        case 1:    // 图书入库
            while (in != 'M' && in != 'm')
            {
                B = (BookType)malloc(sizeof(BookNode));
                B->reader = NULL;                             // 下一个借阅者指针置空
                printf("\n\n\t请输入要入库的书号:");
                scanf("%d",&B->booknum);
                res = SearchBTree(L, B->booknum);                // 查找入库书号
                if (res.tag)                                       // 书库中已存在该书号的书
                {
                    PrintBook(res.pt->rec[res.i]);                // 显示这本书
                    printf("\n\n\t该书已存在如上,请输入新增入库册数: ");
                    fflush(stdin);
                    scanf("%d",&B->total);
                    InsertBook(L, B, res);                        // 该图书入库,数量增加
                    free(B);
                }
                else                                             // 书库中不存在该书号,则插入到书库L中
                {
                    fflush(stdin);
                    printf("\n\t请输入该书 书名: ");
                    gets(B->bookname);
                    printf("\n\t请输入该书著者: ");
                    fflush(stdin);
                    gets(B->writer);
                    printf("\n\t请输入该书册数: ");
                    fflush(stdin);
                    scanf("%d",&B->current);
                    B->total = B->current;
                    printf("\n\t插入后B树如下:\n\n");
                    ShowBTree(L,x);                                // 显示插入后B树状态
                }
                printf("\n\n\t图书入库完成,按M键返回主菜单,按其他任意键继续图书入库....");
                in = getch();
            }
            break;


        case 2:    // 清除库存
            while (in != 'M' && in != 'm')
            {
                printf("\n\n\t请输入要清除库存图书书号: ");
                scanf("%d",&booknum);
                res = SearchBTree(L, booknum);                    // 查找用户输入的书号
                if (res.tag)                                       // 如果查找到
                {
                    B = res.pt->rec[res.i];
                    PrintBook(B);                                // 显示找到的书
                    printf("\t确认删除上面的图书<Y/N>?");    // 提示是否确认删除
                    in = getch();
                    if (in == 'Y' || in == 'y')               // 如果确认删除
                    {
                        DeleteBook(L, B);                        // 删除图书
                        printf("\n\n\t图书%d从书库中清除完毕!\n\n\t删除后B树如下",booknum);
                        ShowBTree(L,x);                            // 显示删除后B树状态
                    }
                }
                else    printf("\n\n\t书库中不存在书号为%d的书!",booknum);
                printf("\n\n\t按'M'返回主菜单,按其他任意键继续清除库存...");
                in = getch();
            }
            break;


        case 3: // 图书出借
            while (in != 'M' && in != 'm')
            {
                system("cls");
                printf("\n\n\t请输入要借阅的图书书号: ");
                scanf("%d",&booknum);
                res = SearchBTree(L, booknum);                    // 在书库中搜索图书booknum
                if (res.tag)                                       // 如果找到
                {
                    R = (ReaderType)malloc(sizeof(ReaderNode)); // 新申请一个读者空间
                    R->nextr  = NULL;                    // 下一个借阅者指针置空
                    B = res.pt->rec[res.i];
                    printf("\n\n\t您查找的图书如下:");
                    PrintBook(B);                                // 显示找到的图书
                    printf("\n\n\t请输入您的借书证号:");        // 读入借阅者信息
                    scanf("%d",&R->cardnum);
                    printf("\n\n\t请输入您的姓名: ");
                    gets(R->Readername);
                    if (BorrowBook(L, B, R))                       // 如果该借阅者可以借阅该书
                    {
                        printf("\n\n\t借书成功!");
                    }
                    else
                    {
                        printf("\n\n\t对不起,您不能借阅该书!该书现存量少于0或已被他人预约。");
                        free(R);                                // 释放该读者空间
                    }
                }
                else printf("\n\n\t书库中不存在图书%d!",booknum);
                printf("\n\n\t按'M'返回主菜单,按其他任意键继续借阅图书...");
                in = getch();
            }
            break;


        case 4: // 图书归还
            while (in != 'M' && in != 'm')
            {
                system("cls");
                printf("\n\n\t请输入你要归还的图书号: ");
                scanf("%d",&booknum);
                printf("\n\n\t请输入你的借书证号: ");
                scanf("%d",&cardnum);

                k = ReturnBook(L, booknum, cardnum, B, R);// 为读者cardnum还书
                if (k == 1)                                       // 如果还书成功
                {
                    printf("\n\n\t还书成功!");
                    free(R);                                    // 释放该读者借书记录
                }
                else if (k == 0)                                   // 如果没有该读者借阅该书的记录
                {
                    R = (ReaderType)malloc(sizeof(ReaderNode));
                    R->cardnum = cardnum;
                    strcpy(R->Readername,"###");
                    printf("\n\n\t没有您借图书%d的记录!",booknum);
                    free(R);
                }
                else printf("\n\n\t书库中不存在图书%d!",booknum);
                printf("\n\n\t按'M'返回主菜单,按其它任意键继续还书...");
                in = getch();
            }
            break;


        case 9:
            system("cls");
            printf("\n\n\n\n\n\t退出系统,确认<Y/N>?...");    // 提示是否确认退出系统
            in = getch();
            if (in == 'y' ||in == 'Y')
            {
                //    RecordLogs(8);                                // 记录日志-退出系统
                exit(0);                                    // 退出
            }
            break;



        default:
            break;
        }
    }
    return 0;
}

  

posted @ 2013-12-25 20:06  博园少主  阅读(474)  评论(0编辑  收藏  举报