线性表即链表,基本特点是除第一个元素无直接前驱,最后一个元素无直接后继之外,其他么个数据元素都有一个前驱和后继。是最基本且最常用的一种线性结构。
2.1线性表的定义和特点
由n(n>=0)个数据特性相同的元素否城的有限序列成为线性表,n为线性表长度,当n=0称空表。
举例:1.26个英文字母的字母表是一个线性表,数据元素是单个字母。
2.学生信息表中,每一个学生为一个数据元素,包括学号、姓名、性别等等数据项。
2.2案例:图书信息管理系统。
2.3线性表的类型定义
线性表是一个相当灵活的数据结构,其长度可根据需要增长或缩短,即对线性表的数据元素不仅可以进行插入和删除等操作。
线性表的抽象数据类型包括:1. 初始化2. 取值3. 查找4. 插入5. 删除
2.4线性表的顺序表示和实现
2.4.1线性表的顺序存储表示
线性表的顺序表示指的是用一组地址连续的存储单元一次存储线性表的数据元素。
这种存储结构的线性表为顺序表。其特点是,逻辑上香菱的数据元素,其物理次序也是相邻的。
其关系可用下图来表示
线性表是一种随机存储的存储结构,在高级语言中通常用数组表示,在C语言中可用动态分配的一维数组表示线性表。
顺序表的类型定义:
#define MAXSIZE 100 //最大长度 typedef struct { ElemType *elem; //指向数据元素的基地址 int length; //线性表的当前长度 <span style="font-family: Arial, Helvetica, sans-serif;">}SqList;</span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">2.3.2顺序表中基本操作的实现</span>
实例:顺序表的基本操作
#include<iostream> #include<fstream> //file #include<string> #include<iomanip> //io manipulator using namespace std; #define OK 1 #define ERROR 0 #define OVERFLOW -2 typedef int Statues; typedef int ElemType; #define MAXSIZE 100 struct Book { string id; //ISBN string name; double price; }; typedef struct { Book *elem; //base address int length; } SqList; //Init Statues InitList_Sq(SqList &L) { L.elem = new Book[MAXSIZE]; //Application memory space ★ if (!L.elem) exit(OVERFLOW); L.length = 0; } //Get Statues GetElem(SqList L, int i, Book &e) { if (i < 1 || i > L.length) return OVERFLOW; e = L.elem[i - 1]; return OK; } //Locate Statues LocateElem_Sq(SqList L, double e) { for (int i = 0; i < L.length; i++) if (L.elem[i].price == e) return i + 1; return 0; } //Insert Statues ListInsert_Sq(SqList &L, int i, Book e) { 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; ++L.length; return OK; } //Delete Statues ListDelete_Sq(SqList &L, int i) { if ((i<1) || (i>L.length)) return ERROR; for (int j = i; j <= L.length; j++) L.elem[j - 1] = L.elem[j]; --L.length; return OK; } int main() { SqList L; int i = 0, temp, a, c, choose; double price; 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 << "6.删除\n"; cout << "7.输出\n"; cout << "8.写入\n"; cout << "0.退出\n"; choose = -1; while (choose != 0) { cout << "请选择:"; cin >> choose; switch(choose) { case 1: if (InitList_Sq(L)) cout << "Create the List successful!\n\n"; else cout << "Create the List failed.\n\n"; break; case 2: { //scanf //涉及到文件操作 括号不能省 i = 0; L.elem = new Book[MAXSIZE]; if (!L.elem) exit(OVERFLOW); L.length = 0; fstream file; file.open("book.txt"); if (!file) { cout << "can not found the file." << endl; exit(ERROR); } 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 << "scanf book.txt done." << endl; L.length = i; file.close(); } break; case 3: cout << "Please enter the pos\n"; c >> i; temp = GetElem(L, i, e); if(temp != 0) { cout << "find successful!" << endl; cout << "the information of book"<< i << "is:\n"; /* cout << left << setw(15) left--align setw--word count */ cout << left << setw(15) << e.id << "\t" << left << setw(50) << e.name << "\t" << left << setw(5) << e.price << endl << endl; } else cout << "find failed." << endl; break; case 4: //query price //weakness:query the first book of this price. cout << "please enter the price." << endl; cin >> price; temp = LocateElem_Sq(L, price); if(temp != 0) { cout << "find successful!" << endl; cout << "the name is" << L.elem[temp - 1].name << endl; } else cout << "find failed!" << endl; break; case 5: //insert cout << "Please enter the insertpos ID name price of the book" << endl; cin >> a; cin >> e.id >> e.name >> e.price; if (ListInsert_Sq(L, a, e)) cout << "find successful!"<<endl; else cout << "find failed!" << endl; break; case 6: //delete a book cout << "please enter the pos" << endl; cin >> c; if (ListDelete_Sq(L, c)) cout << "delete successful!" << endl; else cout << "delete failed!" << endl; break; case 7: //travel cout << "There is the information:\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; case 8: {//enter to the file fstream file; file.open("book1.txt"); if (!file) { cout << "can not found the file." << endl; exit(ERROR); } for (i = 0; i < L.length; i++) file << 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 << "write done." << endl; file.close(); } default: break; } } return 0; }
2.5线性表的链式表示和实现
2.5.1单链表的定义和表示
对于数据元素ai来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息。这两部分信息组成数据元素ai的存储映像,称为节点。
它包括两个域:其中存储数据元素信息的域称为数据域;存储直接后继存储位置的域称为指针域。
#include<iostream> #include<fstream> //file #include<string> #include<iomanip> //io manipulator using namespace std; #define OK 1 #define ERROR 0 #define OVERFLOW -2 typedef int Statues; typedef int ElemType; struct Book { string id; //ISBN string name; double price; }; typedef struct LNode { Book data; struct LNode *next; } LNode, *LinkList; string head_1, head_2, head_3; int length; //init Statues InitList_L(LinkList &L) { L = new LNode; L->next = NULL; return OK; } //get the no.i element Statues GetElem_L(LinkList L, int i, Book &e) { int j; LinkList p; p = L->next; j = 1; while (j < i && p) { p = p->next; ++j; } if(!p || j>i && p) { return ERROR; } e = p->data; return OK; } //Locate the price LNode *LocateElem_L(LinkList L, int e) { LinkList p; p = L->next; while (p && p->data.price != e) p = p->next; return p; } // Statues ListInsert_L(LinkList &L, int i, Book &e) { int j; LinkList p, s; p = L; j = 0; while (p && j < i - 1) { p = p->next; ++j; } if (!p || j > i - 1) return ERROR; s = new LNode; s->data = e; s->next = p->next; p->next = s; ++length; return OK; } Statues ListDelete_L(LinkList &L, int i) { LinkList p, q; int j; p = L; j = 0; while( (p->next) && (j<i-1) ) { p = p->next; ++j; } if (!p || j > i - 1) return ERROR; q = p->next; p->next = q->next; delete q; --length; return OK; } //前插 void CreateList_H(LinkList &L, int n) { LinkList p; L = new LNode; L->next = NULL; 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; file >> p->data.id >> p->data.name >> p->data.price; p->next = L->next; L ->next = p; length++; } file.close(); } void CreateList_R(LinkList &L, int n) { //算法2.12 后插法创建单链表 //正位序输入n个元素的值,建立带表头结点的单链表L LinkList p, r; L = new LNode; L->next = NULL; //先建立一个带头结点的空链表 r = L; //尾指针r指向头结点 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;//生成新结点 file >> p->data.id >> p->data.name >> p->data.price;//输入元素值赋给新结点*p的数据域 p->next = NULL; r->next = p;//将新结点*p插入尾结点*r之后 r = p;//r指向新的尾结点*p length++; //同时对链表长度进行统计 } file.close(); } //CreateList_L int main() { int a, n, choose; double price; Book e; LinkList L, p; InitList_L(L); 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: //使用后插法创建单链表 CreateList_R(L, length); cout << "输入 book.txt 信息完毕\n\n"; 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; }2.5.3循环链表
将尾指针指向首节点元素
举例:哈夫曼编码
#include "cstdio" #include "stdlib.h" #include <iostream> using namespace std; #define S sizeof(struct node) struct node { int num; struct node *next; }; typedef struct node NODE; NODE *createlinklist(int n) { NODE *head, *p, *q; int i = 1; head = p = (struct node*)malloc(sizeof(struct node)); p->num = i; for (i = 2; i <= n; i++) { q = (struct node*)malloc(sizeof(struct node)); if (q == 0) return(0); p->next = q; p = q; p->num = i; } p->next = head; /*使链表尾指向链表头 形成循环链表*/ return head; } void printlinklist(NODE *p, int n) { int i; NODE *q = p; if (NULL == q->next) { printf("the list is NULL!"); return; } printf("所有玩家的信息列表:\n"); for (i = 1; i <= n; i++) { if (NULL == q) { printf("the list is NULL!"); return; } printf("%d ", p->num); p = p->next; } printf("\n"); } void joseph(NODE *p, int n, int m) { int i, j; NODE *q; for (i = 1; i<n; i++) { for (j = 1; j <= m - 1; j++) { p = p->next; } q = p->next; p->next = q->next; printf("%d ", q->num); free(q); } printf("\n最后剩余的是第%d号.\n", p->num); p->next = NULL; } void main() { NODE *head; int n, m; printf("请输入人数N:\n"); scanf_s("%d", &n); printf("输入K:\n"); scanf_s("%d", &m); head = createlinklist(n); printlinklist(head, n); printf("依次被选出的是:\n"); joseph(head, n, m); }2.5.4双向链表
在单链表一个指针的基础上多了一个指针。
#include<iostream> #include<string> #include<fstream> #include<iomanip> using namespace std; #define OK 1 #define ERROR 0 #define OVERFLOW -2 typedef int Status; //Status 是函数返回值类型,其值是函数结果状态代码。 typedef int ElemType; //ElemType 为可定义的数据类型,此设为int类型 struct Book { string id;//ISBN string name;//书名 double price;//定价 }; typedef struct DuLNode { Book data; //数据域 struct DuLNode *prior; //直接前驱 struct DuLNode *next; //直接后继 } DuLNode, *DuLinkList; string head_1, head_2, head_3; int length; Status InitDuList_L(DuLinkList &L) { //构造一个空的双向链表L L = new DuLNode; //生成新结点作为头结点,用头指针L指向头结点 L->next = NULL; //头结点的指针域置空 L->prior = NULL; return OK; } DuLNode *GetElemP_DuL(DuLinkList L, int i) { //在带头结点的双向链表L中查找第i个元素,返回结点的地址 int j; DuLinkList p; p = L->next; j = 1; //初始化,p指向第一个结点,j为计数器 while (j < i && p) { //顺链域向后扫描,直到p指向第i个元素或p为空 p = p->next; ++j; } if (!p || j > i) return NULL; //第i个元素不存在 return p; } //GetElemP_DuL Status ListInsert_DuL(DuLinkList &L, int i, Book e) { //算法2.13 双向链表的插入 //在带头结点的双向链表L中第i个位置之前插入元素e,i的合法值为1<=i<=表长+1 DuLinkList s, p; if (!(p = GetElemP_DuL(L, i))) //在L中确定第i个元素的位置指针p return ERROR; //p为NULL时,第i个元素不存在 if (i == 1) {//在双向链表的第一个元素上插入 s = new DuLNode; //生成新结点s s->data = e; //将结点s数据置为e DuLinkList p = L->next; L->next = s; s->prior = L; s->next = p;//将结点*s插入L中 p->prior = s; ++length; } else if (i == length) {//在双向链表的最后一个元素上插入 s = new DuLNode; //生成新结点s s->data = e; //将结点s数据置为e DuLinkList p = L; while (p->next) p = p->next;//将LinkList p指向双向链表结尾 p->next = s; s->prior = p;//将结点*s插入到p的后面,插入到L中 s->next = NULL; ++length; } else { s = new DuLNode; //生成新结点*s s->data = e; //将结点*s数据域置为e s->prior = p->prior; //将结点*s插入L中,此步对应图2.20① p->prior->next = s; //对应图2.20② s->next = p; //对应图2.20③ p->prior = s; //对应图2.20④ ++length; } return OK; } //ListInsert_DuL Status ListDelete_DuL(DuLinkList &L, int i) { //算法2.14 双向链表的删除 //删除带头结点的双向链表L中第i个位置之前插入元素e,i的合法值为1<=i<=表长 DuLinkList p; if (!(p = GetElemP_DuL(L, i))) //在L中确定第i个元素的位置指针p return ERROR; //p为NULL时,第i个元素不存在 if (i == 1)//删除双向链表的第一个元素 L = L->next; else if (i == length) {//删除双向链表的最后一个元素 p->prior->next = NULL; delete p; --length; return OK; } else { p->prior->next = p->next; //修改被删结点的前驱结点的后继指针,对应图2.21① p->next->prior = p->prior; //修改被删结点的后继结点的前驱指针,对应图2.21② delete p; //释放被删结点的空间 --length; return OK; } }//ListDelete_DuL void CreateDuList_L(DuLinkList &L) { //正位序输入n个元素的值,建立带表头结点的双向链表L,同时建立前驱指针 DuLinkList r, p; L = new DuLNode; L->next = NULL; //先建立一个带头结点的空链表 r = L;//尾指针r指向头结点 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 DuLNode; //生成新结点 file >> p->data.id >> p->data.name >> p->data.price; //输入元素值 p->next = NULL; r->next = p; //插入到表尾 r = p; //r指向新的尾结点 p->prior = L->prior; //插入到表头 L->prior = p; length++; } file.close(); } //CreateDuList_L int main() { int a, choose; Book e; DuLinkList L, p; 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 (InitDuList_L(L)) cout << "成功建立双向链表!\n\n"; break; case 2: //使用后插法创建双向链表 CreateDuList_L(L); cout << "输入 book.txt 信息完毕\n\n"; break; case 3: //双向链表的插入 cout << "请输入两个数分别代表插入的位置和数值(书的信息:编号&书名&价格):"; cin >> a; cin >> e.id >> e.name >> e.price; if (ListInsert_DuL(L, a, e)) cout << "插入成功.\n\n"; else cout << "插入失败!\n\n"; break; case 4: //双向链表的删除 cout << "请输入所要删除的书籍的位置:"; cin >> a; if (ListDelete_DuL(L, a)) cout << "删除成功!\n\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; }2.6顺序表和链表的比较
未完待续...