一、定义
线性表是具有相同数据类型的n(n>-1)个数据元素的有限序列。
二、特点
线性表有唯一的表头元素和表尾元素。除了表头元素,每个元素有且仅有一个直接前驱;除了表尾元素,每个元素有且仅有一个直接后继。
三、存储结构
1、线性表的顺序存储——顺序表
定义:用一组地址连续的存储单元依次存储线性表中的数据元素,使得逻辑上相邻的两个元素在物理位置上也相邻。
特点:表中元素的逻辑顺序和物理顺序相同;存储密度高;随机存取。
结构体定义
#define MaxSize 50
typedef struct{
ElemType data[MaxSize]; //内容
int length; //长度
}SqList; //类名
但在考试中,通常用数组和长度表示顺序表:
ElemType data[MaxSize]; //内容
int n; //长度
算法
1、按值查找(返回元素e的下标)
int findElem(SqList L, ElemType e){
for(int i=0; i<L.length; i++)
if(e==L.data[i])
return i;
return -1;
}
2、插入数据(在下标为i的位置插入元素e)
int insertElem(SqList &L, int i, ElemType e){
// i在0到length-1之间
if(i<0||i>L.length||L.length>=MaxSize)
return 0;
//从最后一个位置到第i个下标的位置都要往后挪一个位置
for(int j=L.length-1;j>=i;j--)
L.data[j+1]=L.data[j];
L.data[i]=e;
L.length++;
return 1;
}
3、删除数据(删除下标为i的元素,并返回给e)
int deleteElem(SqList &L, int i, ElemType &e){
//i在0到length-1之间
if(i<0||i>L.length-1)
return 0;
e=L.data[i];
//从第i个下标的位置到最后一个位置都要往前挪一个位置
for(int j=i;j<L.length;j++)
L.data[j]=L.data[j+1];
L.length--;
return 1;
}
2、线性表的链式存储——单链表
定义:通过一组任意的存储单元来存储线性表中的数据元素。
类型1 单链表
表结点定义
typedef struct LNode{
ElemType data; //数据域
struct LNode *next; //指针域
}LNode, *LinkList; //类型名
算法
1、头插法建表
void headInsert(LNode *&L, ElemType arr[], int n){
LNode *s;
int i;
L = (LNode*)malloc(sizeof(LNode));//创建头结点
L->next = NULL;
for(i=0;i<n;i++){ //n是总长度
s = (LNode*)mallco(sizeof(LNode));
s->data = arr[i]; //arr是已经存好数据的数组
s->next = L->next; //头插法的特点
L->next = s;
}
}
2、尾插法建表
void tailInsert(LNode *&L, ElemType arr[], int n){
LNode *s,*r;
int i;
L = (LNode*)malloc(sizeof(LNode));
L->next = NULL;
r = L;
for(i=0;i<n;i++){
s = (LNode*)mallco(sizeof(LNode));
s->data = arr[i];
r->next = s;
r = s;
}
r->next = NULL;
}
3、按序查找
LNode *findItemByNumber(LNode *L, int i){
LNode *p=L;
int j=-1;
if(i<0)
return NULL;
while(p!=NULL&&j<i){
p=p->next;
j++;
}
return p;
}
4、按值查找
LNode *findItemByData(LNode *L, ElemType e){
LNode *p=L->next;
while(p!=NULL){
if(p->data == e)
break;
p = p->next;
}
return p;
}
5、插入结点
void insertNode(LNode *L, int i, ElemType e){
LNode *p = findItemByNumber(L, i-1); //找到前驱结点
LNode *s = (LNode*)mallco(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
}
6、删除结点
void deleteNode(LNode *L, int i){
LNode *p = findItemByNumber(L, i-1); //找到前驱结点
LNode *s = p->next;
p->next = s->next;
free(s);
}
7、求表长
int getLength(LNode *L){
LNode *p = L->next;
int i=0;
while(p!=NULL){
i++;
p = p->next;
}
return i;
}
类型2 双链表
表结点定义
typedef struct DNode{
ElemType data;
struct DNode *prior,*next;
}DNode, *DLinkList;
算法
1、插入结点
void insertDNode(DNode *L, int i, ElemType e){
DNode *p = L;
int j=0;
if(i<1) return ;
while(j<i-1 && p!=NULL) //找到前驱结点
p=p->next;
DNode *s = (DNode*)mallco(sizeof(DNode));
s->data = e;
s->next = p->next;
p->next->prior = s;
s->prior = p;
p->next = s;
}
2、删除结点
void deleteDNode(DNode *L, int i){
DNode *p = L;
int j=0;
if(i<1) return ;
while(j<i-1 && p!=NULL) //找到前驱结点
p=p->next;
DNode *s = p->next;
p->next = s->next;
s->next->prior = p;
free(s);
}
类型3 循环单链表
循环单链表的尾结点指针不是NULL,而是指向头结点。因此,它的判空条件不是头结点指向NULL,而是指向头结点本身。循环单链表上的插入和删除操作都是等价的,无需判断是否在表尾操作。
类型4 循环双链表
循环双链表的尾结点的后继指针指向头结点,头结点的前驱指针指向尾结点。它的判空条件是头结点的前驱和后继指针是否指向头结点本身。
类型5 静态链表
结构体定义
#define MaxSize 50
typedef struct{
ElemType data;
int next;
}SLinkList[MaxSize];
3、线性表和链表的比较
顺序表 | 单链表 | |
---|---|---|
存取方式 | 顺序存取、随机存取 | 顺序存取 |
逻辑结构与物理结构 | 逻辑上相邻的数据在物理存储位置上也相邻 | 逻辑上相邻的数据在物理存储位置上不一定相邻 |
增删查操作 | 顺序表无序,时间复杂度为O(n);顺序表有序,时间复杂度为O(log2n) | 时间复杂度为O(n) |
空间分配 | 预先分配 | 按需分配 |