數據結構之線性表(順序表,單鏈表,循環鏈表,雙向鏈表)-- 圖書管理系統

順序表

#include <iostream>
#include <cstring>
#include <cstdlib>///exit()頭文件exit(0):正常執行程序並退出程序。exit(1):非正常執行導致退出程序
#include <fstream>///fstream是C++ STL中對文件操作的合集
#include <iomanip>///c++ cin,cout格式化輸入輸出
using namespace std;
#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status;
typedef int ElemType;
const int MAXSIZE = 110;

struct Book
{
    string id;///編號
    string name;///書名
    double price;///定價
};
struct SqList
{
    Book *elem;///存儲空間的基地址
    int length;///當前長度
};
Status InitList_Sq(SqList &L)///順序表的初始化
{
    ///構造一個空的順序表L;
    L.elem = new Book[MAXSIZE];///爲順序表分配一個大小爲MAXNSIZE的數組空間
    if(!L.elem) exit(OVERFLOW);
    L.length = 0;
    return OK;
}
Status GetElem(SqList L, int i, Book &e)///順序表的取值
{
    if(i<1 || i>L.length) return ERROR;///判斷i值是否合理,若不合理,則返回ERROR
    e = L.elem[i-1];///elem[i-1]單元存儲第i個數據元素
    return OK;
}
int LocateElem_Sq(SqList L, double e)
{
    for(int i=0; i<L.length; i++)
    {
        if(L.elem[i].price==e) return i+1;///查找成功,返回序號i+1
    }
    return 0;///未查找到,返回0
}
Status ListInsert_Sq(SqList &L, int i, Book e)///順序表的插入
{
    ///在順序表的第i個位置插入新的元素 i值的合法範圍: 1<=i<=L.length+1
    if(i<1 || i>L.length+1) return ERROR;
    if(L.length==MAXSIZE) return ERROR;///當前存儲空間已滿
    for(int j=L.length-1; j>=i-1; j--)
    {
        L.elem[j+1] = L.elem[j];///插入位置及其之後的元素向後移動一個位置
    }
    L.elem[i-1] = e;///將新元素放入第i個位置(下標爲i-1)
    ++L.length;///表長加1
    return OK;
}
Status ListDelete_Sq(SqList &L, int i)///順序表的刪除
{
    ///在順序表中刪除第i個位置的數據
    if(i<1 || i>L.length) return ERROR;///i值不合法
    for(int j=i; j<L.length; j++)
    {
        L.elem[j-1] = L.elem[j];///被刪除之後的元素前移一個單位
    }
    --L.length;///表長減1
    return OK;
}
int main()
{
    SqList L;
    int i=0, temp, a, c, choose;
    double price;
    Book e;///定義一個Book類型的結構體
    string head_1, head_2, head_3;
    cout << "1. 建立\n";
	cout << "2. 輸入\n";
	cout << "3. 取值\n";
	cout << "4. 查找\n";
	cout << "5. 插入\n";
	cout << "6. 刪除\n";
	cout << "7. 輸出\n";
	cout << "0. 退出\n\n";
	choose = -1;
	while(choose!=0)
    {
        cout<< "請選擇:";
        cin>> choose;
        switch(choose)
        {
            case 1:
            {
                if(InitList_Sq(L)) cout<< "成功建立順序表\n\n";
                else cout<< "順序表建立失敗\n\n";
            }
            break;
            case 2:
            {
               i = 0;
               L.elem = new Book[MAXSIZE];
               if(!L.elem) exit(OVERFLOW);
               L.length = 0;
               fstream file;
               file.open("book.txt");
               if(!file)
               {
                   cout<< "錯誤!未找到文件" <<endl;
                   exit(OVERFLOW);
               }
               file>> head_1 >> head_2 >> head_3;///編號 書名 定價
               while(!file.eof())
               {
                   file>> L.elem[i].id >> L.elem[i].name >> L.elem[i].price;
                   i++;
               }
               cout<< "輸入 book.txt 信息完畢\n\n";
               L.length = i;
               file.close();
            }
            break;
            case 3:///順序表的取值
            {
                cout<< "請輸入一個位置用來取值:\n";
                cin>>i;
                temp = GetElem(L,i,e);
                if(temp!=0)
                {
                    cout<< "查找成功\n";
                    cout<< "第" << i << "本圖書的信息是:\n";
                    cout<< left << setw(15) << e.id << "\t"  ///在C++中,setw(int n)用來控制輸出間隔。
                        << left << setw(50) << e.name << "\t"
                        << left << setw(5) << e.price << endl
                        << endl;
                }
                else cout<< "查找失敗,位置超出範圍!\n\n";
            }
            break;
            case 4: //順序表的查找
            {
                cout<< "請輸入所要查找價格:";
                cin>> price;
                temp = LocateElem_Sq(L, price);
                if(temp != 0)
                {
                    cout<< "查找成功\n";
                    cout<< "該價格對應的書名爲:" << L.elem[temp - 1].name << endl << endl;
                }
                else cout << "查找失敗!沒有這個價格對應的書籍\n\n";
            }
			break;
            case 5: ///順序表的插入
            {
                cout<< "請輸入插入的位置和書本信息,包括:編號 書名 價格(用空格隔開):";
                cin>> a;
                cin>> e.id >> e.name >> e.price; //輸入a和b,a代表插入的位置,b代表插入的數值(書本信息)
                if (ListInsert_Sq(L, a, e))  cout << "插入成功.\n\n";
                else cout << "插入失敗.\n\n";
            }
			break;
		    case 6: //順序表的刪除
            {
                cout<< "請輸入所要刪除的書籍的位置:";
                cin>> c;
                if (ListDelete_Sq(L, c))    cout<< "刪除成功.\n\n";
                else cout<< "刪除失敗.\n\n";
            }
            break;
            case 7: ///順序表的輸出
            {
                cout<< "當前圖書系統信息(順序表)讀出:\n";
			    for(i = 0; i < L.length; i++)
				cout<< left << setw(15) << L.elem[i].id << "\t"
                    << left << setw(50) << L.elem[i].name << "\t"
                    << left << setw(5) << L.elem[i].price << endl;
                cout << endl;
            }break;
		}
    }
    return 0;
}

這個代碼是用到了文件操作的,所以需要在該cpp文件所在的文件夾裏建一個book.txt存儲一些數據信息,才能執行操作。

舉個例子:給定以下數據信息

ISBN                              書名                                   定價
9787302257646            程序設計基礎                    25
9787302219972            單片機技術及應用             32
9787302203513            編譯原理                           46
9787811234923            彙編語言程序設計教程      21
9787512100831            計算機操作系統                17
9787302265436            計算機導論實驗指導         18
9787302180630            實用數據結構                    29
9787302225065            數據結構(C語言版)       38
9787302171676           C#面向對象程序設計         39
9787302250692           C語言程序設計                  42
9787302150664           數據庫原理                        35 
9787302260806           Java編程與實踐                 56
9787302252887           Java程序設計與應用教程   39
9787302198505           嵌入式操作系統及編程       25
9787302169666           軟件測試                            24
9787811231557           Eclipse基礎與應用             35

 運行結果:

單鏈表

#include<iostream>
#include<string>
#include<cstdlib>
#include<iomanip>
#include<fstream>
using namespace std;
#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status;

struct Book
{
    string id;///編號
    string name;///書名
    double price;///定價
};
typedef Book ElemType;///ElemType僅僅代表數據類型
typedef struct LNode
{
    ElemType data;///節點的數據域
    struct LNode *next;///節點的指針域
}LNode, *LinkList;///LinkList爲指向結構體LNode的指針類型(Linklist p == LNode *p,結構體指針)

Status InitList_L(LinkList &L)///構造一個空的單鏈表L
{
    L=new LNode;///生成新結點作爲頭結點,用頭指針L指向頭結點
    L->next=NULL;///頭結點的指針於置空
    return OK;
}
Status GetElem_L(LinkList L, int i, Book &e)///單鏈表的取值,用e返回L中第i個數據元素的值(複雜度O(n))
{
    int j;///用j做計數器初值爲1
    LinkList p;
    p=L->next;///用指針p指向首元結點
    j=1;
    while(j<i && p)///只要當前結點的指針p不爲空(NULL),並且沒有到達序號爲i的結點,則繼續循環
    {
        p = p->next;
        ++j;
    }
    if(!p || j>i) return ERROR;///如果指針p爲空(即:i>表長) 或者 計數器j>i,說明i<=0
    e=p->data;///取第i個結點的數據域
    return OK;
}

LNode *LocateElem_L(LinkList L, int e)///單鏈表的按值查找(時間複雜度O(n))(指針函數:返回值是一個指針)
{///在帶頭結點的單鏈表L中查找值爲e的元素
    LinkList p;
    p = L->next;///初始化,P指向首元結點
    while(p && p->data.price!=e)///順鏈域向後掃描,直到p爲空或p所指的數據域等於e
    {
        p=p->next;///p指向下一個結點
    }
    return p;///查找成功返回值爲e的結點地址p,查找失敗返回NULL
}

Status ListInsert_L(LinkList &L, int i, Book &e)///單鏈表的插入
{///將值爲e的新結點插入到表的第i個結點的位置上,即插入到a[i-1]與a[i]之間
    int j;
    LinkList p, s;
    p = L;
    j = 0;
    while(p && (j<i-1))///查找第i-1個結點,p
    {
        p = p->next;///(當j=0,p指向第一個結點,即當j=i-1,p指向第i個結點)
        ++j;
    }
    if (!p || (j>i-1)) return ERROR;///i>n+1 或者 i<1
    s = new LNode;///生成新結點*s
    s->data = e;///將結點*s的數據域置爲e
    s->next = p->next;///將結點*s的指針域指向結點a[i]
    p->next = s;///將結點*p的指針域指向結點*s
    return OK;
}
Status ListDelete_L(LinkList &L, int i)///單鏈表的刪除
{///在帶頭結點的單鏈表中,刪除第i個元素
    LinkList p, q;
    int j;
    p = L;
    j = 0;
    while((p->next)&&(j < i - 1))///查找a[i-1]結點,並由指針p指向該結點
    {
      p=p->next;
      ++j;
    }
    if (!(p->next)||(j>i-1)) return ERROR;///當i>n 或 i<1時,刪除位置不合理
    q = p->next;///臨時保存被刪結點i的地址以備釋放
    p->next = q->next;///改變刪除結點的前驅結點的指針域
    delete q;///釋放刪除結點的空間
    return OK;
}
int main()
{
    int a,n,choose;
    double price;
    Book e;
    LinkList L,p;
    string head_1, head_2, head_3;
    cout<<"1. 建立\n";
    cout<<"2. 輸入\n";
    cout<<"3. 取值\n";
    cout<<"4. 查找\n";
    cout<<"5. 插入\n";
    cout<<"6. 刪除\n";
    cout<<"7. 輸出\n";
    cout<<"0. 退出\n\n";

    choose = -1;
    while(choose != 0)
    {
        cout<< "請選擇:";
        cin>> choose;
        switch(choose)
        {
            case 1:///建立
            {
                if (InitList_L(L)) cout<<"成功建立鏈表!\n\n";
            }
            break;
            case 2:///前插法創建單鏈表
            {///前插法是通過將新結點逐個插入到鏈表的頭部(頭結點之後)來創建鏈表,每次申請一個新結點
             ///讀入相應的數據元素值,然後將新結點插入到頭結點之後
                L = new LNode;
                L->next = NULL;///建立一個帶頭結點的空鏈表
                int length = 0;
                fstream file;
                file.open("book.txt");
                if (!file)
                {
                    cout << "未找到相關文件,無法打開!" << endl;
                    exit(ERROR);
                }
                file >> head_1 >> head_2 >> head_3;
                while(!file.eof())///處理到文件結束
                {
                    p = new LNode;///生成新結點*p
                    file>> p->data.id >> p->data.name >> p->data.price;///將數據賦值給新結點*p的數據域
                    p->next = L->next;
                    L->next = p;///將新結點*p插入到頭結點之後
                    length++;///表長加1
                }
                cout<< "輸入 book.txt 信息完畢\n\n";
                file.close();
            }
            break;
            case 3:///單鏈表的序號取值
            {
                cout<<"請輸入一個位置用來查找:";
                cin >>a;
                if(GetElem_L(L,a,e))
                {
                    cout<<"查找成功\n";
                    cout<<"第"<<a<<"本圖書的信息是:\n";
                    cout<< left << setw(15) << e.id << "\t"
                        << left << setw(50) << e.name << "\t"
                        << left << setw(5) << e.price << endl << endl;
                }
                else cout<<"查找失敗\n\n";
            }
            break;
            case 4:///單鏈表的按值查找
            {
                cout<<"請輸入所要查找的價格:";
                cin >> price;
                if(LocateElem_L(L, price)!=NULL)
                {
                    cout<< "查找成功\n" ;
                    cout<< "該價格對應的書名:" << LocateElem_L(L, price)->data.name << endl << endl;
                }
                else cout<<"查找失敗!定價"<<price<<" 沒有找到\n\n";
            }
            break;
            case 5:///單鏈表的插入
            {
                cout<< "請輸入插入的位置和書的信息,包括:編號 書名 價格(用空格隔開):";
                cin>>a;
                cin>> e.id >> e.name >> e.price;
                if(ListInsert_L(L,a,e))   cout<<"插入成功.\n\n";
                else  cout<<"插入失敗!\n\n";
            }
            break;
            case 6:///單鏈表的刪除
            {
                cout<<"請輸入所要刪除的書籍位置:";
                cin >>a;
                if(ListDelete_L(L, a))   cout<<"刪除成功!\n\n";
                else  cout<<"刪除失敗!\n\n";
            }
            break;
            case 7:///單鏈表的輸出
            {
                cout << "當前圖書系統信息(鏈表)讀出:\n";
                p=L->next;
                while(p)
                {
                    cout<< left << setw(15) << p->data.id << "\t"
                        << left << setw(50) << p->data.name << "\t"
                        << left << setw(5) << p->data.price <<endl;
                    p=p->next;
                }
                cout<<endl;
            }
            break;
        }
    }
    return 0;
}

還用上面的那組數據吧!

循環鏈表

循環鏈表特點:最後一個結點的指針域指向頭結點,整個鏈表形成一個環。循環鏈表和單鏈表的操作基本一致,唯一的差別就是:遍歷鏈表的時候,判斷當前指針p是否指向表尾結點的終止條件不同。在單鏈表中,判別條件爲p!=NULL 或 p->next!=NULL,而在循環單鏈表的判斷條件爲p!=L 或 p->next!=L。

循環鏈表的合併問題:(對設置有頭指針和尾指針的兩個循環鏈表)①:第二個表的尾指針指向第一個表的頭結點。②:將第一個表的尾指針指向第二個表的第一個結點。③:然後釋放第二個表的頭結點。 

p = B->next->next;/// p爲第二個鏈表的第一個結點的地址
B->next = A->next;///將第二個表的尾指針 指向 第一個表的 頭結點
A->next = p;///將第一個結點的尾指針 指向 第二個表的第一個結點
A B 分別爲兩個鏈表的尾地址

雙向鏈表

 在雙向鏈表的節點中有兩個指針域,一個指向直接後繼,一個指向直接前驅。

和上面的單鏈表類似,可以試着比較學習。

#include <bits/stdc++.h>
using namespace std;
#define OK 1
#define ERROR 0
typedef int Status;

struct Book
{
    string id;
    string name;
    double price;
};
typedef Book ElemType;
typedef struct DuLNode
{
    ElemType data; ///數據域
    struct DuLNode *prior;///指向直接前驅
    struct DuLNode *next;///指向直接後繼
}DuLNode,*DuLinkList;

Status InitList_L(DuLinkList &L)
{
    L = new DuLNode;
    L->prior = NULL;
    L->next = NULL;
    return OK;
}

DuLNode *GetElem_DuL(DuLinkList L, int i)///指針函數,返回一個DuLNode的地址。該函數返回第i個位置的地址
{
    DuLinkList p, s;
    p = L;
    int cnt = 0;
    while(cnt<i && p)
    {
        p = p->next;
        s = p;
        cnt++;
    }
    return s;
}
Status List_Insert(DuLinkList &L, int i, ElemType e)///雙向鏈表的插入
{///在帶有頭結點的雙向鏈表L中的第i個位置之前插入元素e
    DuLinkList p, s;
    if(!(p = GetElem_DuL(L,i))) return ERROR;///在L中確定第i個元素的位置指針p,p爲NULL時,第i個元素不存在
    s = new DuLNode;///生成新結點*s;
    s->data = e;///將結點*s的數據域爲e
    s->prior = p->prior;///先將s的前驅指向第i-1個結點
    p->prior->next = s;///將i-1個結點的後驅指向s
    s->next = p;///將s的後驅指向第i個結點
    p->prior = s;///將第i個結點的前驅指向s
    return OK;
}
Status ListDelete_DuL(DuLinkList &L, int i)///雙向鏈表的刪除
{///刪除帶頭結點的雙向鏈表中的第i個元素
    DuLinkList p;
    if(!(p = GetElem_DuL(L,i))) return ERROR;///在L中確定第i個元素的位置指針p,p爲NULL時,第i個元素不存在
    p->prior->next = p->next;///將第i-1個結點的前驅 指向 第i+1個結點
    p->next->prior = p->prior;///將第i+1個結點的前驅 指向 第i-1個結點
    delete p;///釋放被刪結點的空間
    return OK;
}
int main()
{
    int a, n, choose;
    DuLinkList L, p;
    Book e;
    string head_1, head_2, head_3;
    cout<<"1. 建立\n";
    cout<<"2. 輸入\n";
    cout<<"3. 插入\n";
    cout<<"4. 刪除\n";
    cout<<"5. 輸出\n";
    cout<<"0. 退出\n\n";

    choose = -1;
    while(choose != 0)
    {
        cout<< "請選擇:";
        cin>>choose;
        switch(choose)
        {
            case 1:
            {
                if(InitList_L(L)) cout<<"成功建立鏈表!\n\n";
            }
            break;
            case 2:
            {
                DuLinkList r;
                L = new DuLNode;
                L->prior = NULL;
                L->next = NULL;
                r = L;
                fstream file;
                file.open("book.txt");
                if(!file)
                {
                    cout<< "未找到相關文件,無法打開!" << endl;
                    exit(ERROR);
                }
                file>> head_1 >> head_2 >> head_3;
                while(!file.eof())///使用後插法創建雙向鏈表(通過尾指針r)
                {
                    p = new DuLNode;///生成新結點*p
                    file>> p->data.id >> p->data.name >> p->data.price;
                    r->next = p;
                    p->prior = r;
                    p->next = NULL;
                    r = p;
                }
                cout<< "輸入 book.txt 信息完畢\n\n";
                file.close();
            }
            break;
            case 3:
            {
                cout<< "請輸入插入的位置和書的信息:包括:編號 書名 價格(用空格分開):";
                cin>> a;
                cin>> e.id >> e.name >> e.price;
                if(List_Insert(L, a, e)) cout<< "插入成功.\n\n";
                else cout<<"插入失敗!\n\n";
            }
            break;
            case 4:
            {
                cout<< "請輸入要刪除的書籍位置:";
                cin>> a;
                if(ListDelete_DuL(L, a)) cout<< "刪除成功!\n";
                else cout<< "刪除失敗!\n\n";
            }
            break;
            case 5:
            {
                cout<< "當前圖書系統信息(鏈表)讀出:\n";
                p = L->next;
                while(p)
                {
                    cout<< left << setw(15) << p->data.id << "\t"
                        << left << setw(50) << p->data.name << "\t"
                        << left << setw(5) << p->data.price << endl;
                    p = p->next;
                }
                cout<< endl;
            }
            break;
        }
    }
    return 0;
}

運行結果: 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章