1. 程式人生 > >資料結構之線性表(順序表,單鏈表,迴圈連結串列,雙向連結串列)-- 圖書管理系統

資料結構之線性表(順序表,單鏈表,迴圈連結串列,雙向連結串列)-- 圖書管理系統

順序表

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

執行結果: