1.线性表定义
线性表是具有相同数据类型的n(n>=0)个数据元素的有限序列,其中n为表长,当n为0时,该线性表是一个空表。除第一个元素外,每个元素有且仅有一个直接前驱,除最后一个元素外,每个元素有且仅有一个直接后继。
线性表的特点:
1.表中元素的个数有限
2.表中元素具有逻辑上的顺序性,在序列中各元素排序有其先后次序
3.表中元素都是数据元素,每一个元素都是单个元素
4.表中元素的数据类型都相同,这意味着每一个元素都占有相同大小的存储空间
5.表中元素具有抽象性,即仅讨论元素间的逻辑关系,不考虑元素究竟表示什么内容
知识框架:
2.顺序表
线性表的顺序存储成为顺序表,它是用一组地址连续的存储单元,依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻。
顺序表的特点:
1.可以随机访问,即通过首地址和元素符号可以在O(1)的时间内找到指定的元素。
2.表中元素的逻辑顺序与其物理顺序相同。
3.存储密度高,每个结点只存储数据元素。
注意:线性表中元素的位序是从1开始,而数组中元素的下标 是从0开始。
线性表的顺序存储类型描述为:
#define MaxSize 50 //定义线性表的最大长度
typedef struct{
int data[MaxSize]; //线性表的元素
int length; //线性表的当前长度
}SqList; //线性表的类型定义
顺序表的基本操作如下:
(1)插入操作
在顺序表L的第i(1<=i<=L.length+1)个位置插入新元素e。如果i的输入不合法,则返回false,表示插入失败;否则将顺序表的第i个元素及其后的所有元素右移一个位置,腾出一个空位置插入新元素e,顺序表长度加1,插入成功,返回true。
bool ListInsert(SqList &L, int i, int e ){
if(i<1 || i>L.length +1) return false; //判断i的范围是否有效
if(L.length >= MaxSize) return false; //当前存储空间已满,不能插入
for(int j = L.length ; j>=i; j--){
L.data[j] = L.data[j-1]; //将第i个元素及之后的元素后移
}
L.data[i-1] = e; //在位置i处加入e
L.length ++; //线性表长度加1
retur true;
}
(2)删除操作
删除顺序表L中第i(1<=i<=L.length)个位置的元素,成功则返回true,并将被删除的元素用引用变量e返回,否则返回false。
bool ListDelete(SqList &L, int i, int &e){
if(i<1 || i>L.length +1) return false;
e = L.data[i-1]; // 将被删除的值赋值给e
for(int j=i; j<L.length ;j++){
L.data[j-1] = L.data[j]; //将第i个位置之后的元素前移
}
L.length --;
return false;
}
(3)按值查找(顺序查找)
在顺序表L中查找第一个元素值等于e的元素,并返回其位序。
int Locate(SqList L, int e){
int i;
for(i 0; i<L.length ;i++){
if(L.data[i] ==e){
return i+1; //下标为i的元素值等于e,返回其位序i+1
}
}
return 0; //退出循环,说明查询失败
}
3.单链表
线性表的链式存储又称为单链表,它不要求逻辑上相邻的两个元素在物理位置上也相邻,为了建立起数据元素之间的线性关系,对每个链表结点,除了存放元素自身的信息之外,还需要存放一个指向其后继的指针,因此,对线性表的插入、删除不需要移动元素,只需要修改指针。
为了操作方便,在单链表第一个结点之前附加一个结点,称为头结点,头结点的数据域可以不设任何信息,也可以记录表长等相关信息,头结点的指针域指向线性表的第一个元素结点。
头结点和头指针的区分:不管带不带头结点,头指针始终指向链表的第一个结点,而头结点是带头结点链表中的第一个结点,结点内通常不存储信息。
引入头结点,可以带来两个优点:
(1)由于开始结点的位置被存放在头结点的指针域中,所以在链表的第一个位置上的操作和在表的其他位置上的操作一致,无须进行特殊处理。
(2)无论链表是否为空,其头指针是指向头结点的非空指针(空表中头结点的指针域为空),因此空表和非空表的处理也就统一。
单链表中结点类型描述如下:
typedef struct LNode{ //定义单链表结点类型
int data; //数据域
struct LNode *next; //指针域
}LNode, *LinkList;
单链表的基本操作如下:
(1)尾插法建立单链表
将新结点插入到当前链表的表尾中,为此必须增加一个尾指针r,使其始终指向当前链表的尾结点,代码如下::
LinkList CreatList(LinkList &L){
int x; //设置元素类型为整型
L =(LinkList)malloc(sizeof(LNode));
LNode *s, *r = L; //r为表尾指针
scanf("%d", &x);
while(x != 9999){
s = (LinkList)malloc(sizeof(LNode));
s->data = x;
r->next =s;
r = s; //r指向新的表尾结点
scanf("%d", &x);
}
r->next = NULL; //尾结点指针空置
return L;
}
(2)按序号查找结点值
在单链表中从第一个结点出发,顺指针next域逐个往下搜索,直到找到第i个结点为止,否则返回最后一个结点指针域NULL。
LNode *Get(LinkList L, int i){
int j =1; //计数,初始为1
LNode *p = L->next ; //头结点指针赋给p
if(i == 0) return L; //若i等于0,则返回头结点
if(i < 1) return NULL; //若i无效,返回NULL
while(p&&j <i){
p = p->next ; //从第1个结点开始找,查找第i个结点
j++;
}
return p; //返回第i个结点的指针
}
(3)按值查找表结点
从单链表第一个结点开始,由前往后依次比较表中各结点数据域的值,若某结点数据域的值等于定值e,则返回该结点的指针;若整个单链表中没有这样的结点,则返回NULL。代码如下:
LNode *Locate(LinkList L, int x){
LNode *p = L->next ;
wile(p != NULL && p->data !=e ){
p = p->next ;
}
return p; //找到后返回该结点指针p,否则返回NULL
}
(4)插入结点操作
插入操作是将值为x的新结点插入到单链表的第i个位置。先检查插入位置的合法性,然后找到待插入位置的前驱结点,即第i-1个结点,再在其后插入新结点。代码如下:
void insert(LinkList &L, int x){
LNode *s,*p;
s = new LNode; //新建结点
s->next = NULL;
p = Get(L,i-1); //查找插入位置的前驱结点
s->data = x; //插入结点的数据域为x;
s->next = p->next ;
p->next = s; //前一个位置的结点指针指向新节点
}
(5)删除结点操作
删除操作是将单链表的第i个结点删除,先检查删除位置的合法性,然后查找表中第i-1个结点,即被删除结点的前驱结点,再将其删除。假设结点p为找到的删除结点的前驱结点,为了实现这一操作后的逻辑关系的变化,仅需修改p的指针域,即将p的指针域next指向q的下一个结点。代码如下:
void Delet(LinkList &L, int i){
LNode *s, *p;
p = Get(L,i-1); //查找插入位置的前驱结点
s = p->next ; //令s指向被删除结点
p->next = s->next ; //将*s结点从链中断开
free(s); //释放结点的存储空间
}