順序表
#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;
}
運行結果: