資料結構之線性表(順序表,單鏈表,迴圈連結串列,雙向連結串列)-- 圖書管理系統
順序表
#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;
}
執行結果: