图书管理系统

#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;

}

  

你可能感兴趣的:(管理)