线性表
由同类型数据元素构成的有序线性序列的线性结构
- 表中元素个数称为线性表的长度
- 线性表没有元素时,称为空表
- 表起始位置称表头,表结束位置称表尾
对于除空表外的任何表,我们说 A i + 1 A_{i+1} Ai+1后继 A i A_i Ai并称 A i − 1 A_{i-1} Ai−1前驱 A i A_i Ai
线性表的抽象数据类型描述
线性表(List)
数据对象集:线性表是n个数据元素的有限序列
操作集
List MakeEmpty()
初始化一个空线性列表L
ElementType FindKth(int k,List L)
返回线性表L中第k个位置的元素
int Find(ElementType x,List L)
返回线性表L中第一个与x相等的元素位置
void Insert(ElementType X,int k,List L)
在线性表L中第k个位置插入元素X
void Delete(int K,List L)
删除线性表L中第K个位置的元素
int Length(List L)
返回线性表L的元素个数
线性表的顺序存储实现
利用数组的连续存储空间顺序存放线性表中的元素
typedef struct LNode *List;
struct LNode{
ElemType Data[MAXSIZE];
int Last;
};
struct LNode L;
List PtrL;
访问下标为i的元素:L.Data[i]或PtrL->Data[i]
线性表的长度:L.Last+1或PtrL->Last+1
主要操作实现:
初始化(建立空的顺序表)
List MakeEmpty(){
List PtrL;
PtrL = (List)malloc(sizeof(struct LNode));
PtrL->Last = -1;
return PtrL;
}
查找(下标增序找到的第一个合适的元素)
int Find(ElemType X,List PtrL){
int i = 0;
while(i <= PtrL->Last && PtrL->Data[i] != X) i++;
if(i > PtrL->Last) return -1;//没找到
else return i;//找到了,返回下标
}
插入
void Insert(ElemType X, int i, List PtrL) {
int j;
if(PtrL->Last == MAXSIZE-1){//满了,不能插入
printf("顺序表已满");
return;
}
if(i < 1 || i > PtrL->Last+2){//检查插入位置的合法性
printf("插入位置不合法");
return;
}
for(j=PtrL->Last; j>=i-1; j--) PtrL->Data[j+1] = PtrL->Data[j];//将第i-1个元素到最后一个元素向后移动一个位置
PtrL->Data[i-1] = X;//新元素插入
PtrL->Last++;//Last仍指向最后元素
return;
}
删除
void Delete(int i,List PtrL){
int j;
if(i<1 || i>PtrL->Last+1){//检查删除位置的合法性
printf("不存在第%d个元素",i);
return;
}
for(j=i; j<=PtrL->Last; j++) PtrL->Data[j-1] = PtrL->Data[j];//将第i个元素到最后一个元素向前移动一个位置
PtrL->Last--;
return;
}
线性表的顺序存储实现完整代码
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
typedef int ElemType;
typedef struct LNode* List;
struct LNode {
ElemType Data[MAXSIZE];
int Last;
};
List PtrL;
int IsEmpty(List L);
List MakeEmpty();
void Insert(ElemType X, int i, List L);
int FindTheFirst(ElemType x, List L);
void Traverse(List L);
void Delete(int i, List L);
int Length(List L);
int main() {
PtrL = MakeEmpty();
if (PtrL == NULL) {
printf("内存分配失败\n");
return;
}
int choice, site;
ElemType X;
while (1) {
printf("请选择你的操作:\n"
"1.插入一个单元\n"
"2.查找一个单元\n"
"3.遍历已有顺序表\n"
"4.删除一个单元\n"
"5.查看表长\n"
"6.删除这个顺序表\n");
scanf("%d", &choice);
switch (choice) {
case 1:
printf("插入的元素:");
scanf("%d", &X);
printf("想插入到的位置:");
scanf("%d", &site);
Insert(X, site, PtrL);
break;
case 2:
if (IsEmpty(PtrL)) break;
printf("要找什么?");
scanf("%d", &X);
FindTheFirst(X, PtrL) == 0 ? printf("没有该元素\n") : printf("在顺序标的第%d个\n", FindTheFirst(X, PtrL));
break;
case 3:
if (IsEmpty(PtrL)) break;
Traverse(PtrL);
break;
case 4:
if (IsEmpty(PtrL)) break;
printf("删第几个");
scanf("%d", &site);
Delete(site, PtrL);
break;
case 5:
if (IsEmpty(PtrL)) break;
printf("表长为%d\n", Length(PtrL));
break;
case 6:
if (IsEmpty(PtrL)) ;
else printf("已经删除,");
printf("已经自动退出程序");
free(PtrL);
exit(0);
}
printf("\n");
}
return 0;
}
int IsEmpty(List L) {
if (L == NULL || L->Last == -1) {
printf("还没有表呢\n");
return 1;
}
return 0;
}
List MakeEmpty() {
List PtrL = (List)malloc(sizeof(struct LNode));
if (PtrL != NULL) {
PtrL->Last = -1;
}
return PtrL;
}
void Insert(ElemType X, int site, List L) {
if (L->Last == MAXSIZE - 1) {
printf("表已经满了\n");
return;
}
else if (site < 1 || site > L->Last + 2) {
printf("这地方插不了\n");
return;
}
else {
L->Last++;
int sub_insert = L->Last + 1;
while (sub_insert != L->Last) {
L->Data[sub_insert] = L->Data[--sub_insert];
}
L->Data[sub_insert] = X;
printf("已插入\n");
return;
}
}
int FindTheFirst(ElemType x, List L) {
int index = 0;
while (index <= L->Last && L->Data[index] != x) index++;
if (index > L->Last) return 0;
else return index + 1;
}
void Traverse(List L) {
for (int sub_traverse = 0; sub_traverse <= L->Last; sub_traverse++) {
printf("%d", L->Data[sub_traverse]);
if (sub_traverse < L->Last) printf(" ");
}
return;
}
void Delete(int site, List L) {
while (site <= L->Last + 1) {
L->Data[site - 1] = L->Data[site++];
}
L->Last--;
return;
}
int Length(List L) {
return L->Last + 1;
}
线性表的链式存储实现
不要求逻辑上相邻的两个元素物理上也相邻,通过“链”建立起数据元素之间的逻辑关系
- 插入删除不需要移动元素,只需修改指针
typedef struct LNode *List;
struct LNode{
ElementType Data;
List Next;
};
struct LNode L;
List PtrL;
主要操作实现:
求表长
int Length(List PtrL){
List p = PtrL;//p指向第一个节点
int j = 0;
while(p){
p = p->Next;
j++;//当前p指向的是第j个节点
}
return j;
}
查找
按照序号查找:FindKth
List FindKth(int K, List PtrL){
List p = PtrL;
int i = 1;
while(p! = NULL && i<K){
p = p->Next;
i++;
}
if(i == K) return p;
else return NULL;
}
按照值查找:Find
List Find(ElementType X, List PtrL){
List p = PtrL;
while(p != NULL && p->Data != X) p = p->Next;
= PtrL;
return p;
}
插入
先构造一个新节点,用s指向
再找到链表的第i-1个节点,用p指向
然后修改指针,插入节点(p之后插入新节点是s)
操作实现:
List Insert(ElementType X,int i,List PtrL){
List s,p;
if(i==1){//在表头插入
s = (List)malloc(sizeof(struct LNode));
s->Data = X;
s->Next = PtrL;
return s;
}
p = FindKth(i-1,PtrL);//找到第i-1个节点
if(p == NULL){//第i-1个节点不存在
printf("参数i错误");
return NULL;
}else{
s = (List)malloc(sizeof(struct LNode));
s->Data = X;
s->Next = p->Next;
p->Next = s;
return PtrL;
}
}
删除
找到链表的第i-1个结点,用p指向
再用指针s指向p的下一个节点
修改指针,删除s所指节点
释放s所占空间
操作实现:
List Delete(int i, List PtrL){
List p,s;
if(i==1){//删除第一个节点
s = PtrL;//s指向第一个节点
if(Ptrl!= NULL)
PtrL = PtrL->Next;
else return NULL;
free(s);
return PtrL;
}
p = FindKth(i-1,PtrL);
if(p == NULL){
printf("第%d个元素不存在",i);
return NULL;
}else if(p->Next == NULL){
printf("第%d个元素不存在",i);
return NULL;
}else{
s = p->Next;
p->Next = s->Next;
free(s);
return PtrL;
}
}
链表实现的顺序表完整代码
一个包含学生信息(主要包括学号、姓名、班级和成绩等)的链表
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int ElemType;
typedef struct {
char Stu_ID[10];
char Stu_name[20];
char Class_ID[10];
int score;
}Student;
struct Node {
Student Data;
struct Node* Next;
};
typedef struct Node* LNode;
LNode List;//头结点
void Menu();
LNode Create_Student_List();
bool IsEmpty(LNode List);
void Show(LNode List);
void Find_By_Name(LNode List, const char* name);
bool Find_By_Position(LNode List, int position);
bool Insert_By_Position(LNode List, int position, Student stu);
bool Delete_By_Position(LNode List, int position, Student stu);
int Count(LNode List);
void DestroyList(LNode* List);
int main() {
Student stu;
char stu_name[20];
int position, choice;
printf("初始化一个学生信息链表\n");
List = Create_Student_List();
if (List == NULL) {
printf("内存分配失败");
return;
}
while (1) {
Menu();
scanf("%d", &choice);
switch (choice) {
case 1:
if (IsEmpty(List)) break;
Show(List);
break;
case 2:
if (IsEmpty(List)) break;
printf("\n请输入要查学生的姓名:");
scanf("%s", stu_name);
Find_By_Name(List, stu_name);
break;
case 3:
if (IsEmpty(List)) break;
printf("\n请输入要查学生的逻辑序号:");
scanf("%d", &position);
if (!Find_By_Position(List, position))
printf("该逻辑序号不存在\n");
break;
case 4:
printf("\n请输入一个新学生的插入位置(逻辑序号):");
scanf("%d", &position);
printf("\n请输入新学生的信息(学号,姓名,班级,成绩):");
scanf("%s", stu.Stu_ID);
scanf("%s", stu.Stu_name);
scanf("%s", stu.Class_ID);
scanf("%d", &stu.score);
if (!Insert_By_Position(List, position, stu))
printf("插入失败!");
else
printf("插入成功!");
Show(List);
break;
case 5:
if (IsEmpty(List)) break;
printf("\n请输入要删除学生的位置(逻辑序号):");
scanf("%d", &position);
if (position < 1) printf("输入位置不合法");
else if (!Delete_By_Position(List, position, stu))
printf("删除失败!");
else printf("删除成功!");
Show(List);
break;
case 6:
printf("\n学生表中学生的人数为:%d\n", Count(List));
break;
case 7:
DestroyList(&List);
exit(0);
}
}
return 0;
}
void Menu() {
printf("\n1: 显示学生信息\n");
printf("2: 根据姓名进行查找\n");
printf("3: 根据指定的位置输出相应的学生信息\n");
printf("4: 插入一个学生\n");
printf("5: 删除一个学生\n");
printf("6: 统计表中学生个数\n");
printf("7: 退出\n");
printf("\n请选择:");
}
LNode Create_Student_List() {
LNode List = (LNode)malloc(sizeof(struct Node));
List->Next = NULL;
return List;
}
bool IsEmpty(LNode List) {
if (List->Next == NULL) {
printf("还没有添加任何学生");
return true;
}
return false;
}
void Show(LNode List) {
LNode PtrL = List->Next;
while (PtrL) {
printf("%s %s %s %d\n", PtrL->Data.Stu_ID, PtrL->Data.Stu_name, PtrL->Data.Class_ID, PtrL->Data.score);
PtrL = PtrL->Next;
}
return;
}
void Find_By_Name(LNode List, const char* name) {
LNode PtrL = List->Next;
while (strcmp(name, PtrL->Data.Stu_name) != 0 && PtrL->Next != NULL) {
PtrL = PtrL->Next;
}
if (strcmp(name, PtrL->Data.Stu_name) == 0) {
printf("找到了\n%s %s %s %d\n", PtrL->Data.Stu_ID, PtrL->Data.Stu_name, PtrL->Data.Class_ID, PtrL->Data.score);
return;
}
else printf("没有该学生\n");
}
bool Find_By_Position(LNode List, int position) {
int index = 1;
LNode PtrL = List->Next;
while (PtrL->Next != NULL && index < position) {
PtrL = PtrL->Next;
index++;
}
if (index != position) return false;
else {
printf("找到了\n%s %s %s %d\n", PtrL->Data.Stu_ID, PtrL->Data.Stu_name, PtrL->Data.Class_ID, PtrL->Data.score);
return true;
}
}
bool Insert_By_Position(LNode List, int position, Student stu) {
int index = 1;
LNode NewNode = Create_Student_List();
NewNode->Data = stu;
LNode PtrL = List;
while (PtrL->Next != NULL && index < position) {
PtrL = PtrL->Next;
index++;
}
if (index != position) return false;
else {
NewNode->Next = PtrL->Next;
PtrL->Next = NewNode;
return true;
}
}
bool Delete_By_Position(LNode List, int position, Student stu) {
int index = 1;
LNode PtrL = List->Next;
while (PtrL->Next != NULL && index < position) {
PtrL = PtrL->Next;
index++;
}
if (index != position) return false;
else {
stu = PtrL->Next->Data;
printf("删除学生:%s %s %s %d\n", stu.Stu_ID, stu.Stu_name, stu.Class_ID, stu.score);
LNode j = PtrL->Next;
PtrL->Next = PtrL->Next->Next;
free(j);
return true;
}
}
int Count(LNode List) {
int count = 0;
LNode PtrL = List;
while (PtrL->Next != NULL) {
PtrL = PtrL->Next;
count++;
}
return count;
}
void DestroyList(LNode* List) {
LNode PtrL, temp;
PtrL = *List;
while (PtrL != NULL) {
temp = PtrL->Next;
free(PtrL);
PtrL = temp;
}
*List = NULL;
}
广义表
广义表是线性表的推广
对于线性表而言,n个元素都是基本的单元素
广义表中,这些元素不仅可以是单元素也可以是另一个广义表
typedef struct GNode *GList;
struct GNode{
int Tag;//标志域,0表示单元素,1表示广义表
union{//子表指针域Sublist与单元数据域Data复用,及公用存储空间
ElementType Data;
GList SubList;
}URegion;
GList Next;//指向后继节点
};
双链表
其开销是一个附加的链,它增加了空间的需求,同时也使得插入和删除的开销增加了一倍,因为有更多的指针需要定位。另外,它简化了删除操作,因为你不再被迫使用一个指向前驱元的指针来访问一个关键字
循环列表
让最后的单元反过来指向第一个单元,它可以有表头,也可以没有(若有,则最后的单元就指向它),并且还可以是双向链表
多重链表
链表中的结点可能同时隶属于多个链
- 多重链表中的结点有多个指针域
- 但包含两个指针域的链表并不一定是一个多重链表,比如双向链表就不是多重链表
树,图这样相对复杂的数据结构都可以采用多重链表方式存储