1 线性表的定义和基本操作
1.1 定义
线性表是具有相同数据类型的n个数据元素的有限序列,其中n为表长,当n=0时线性表是一个空表。
线性表的特点:
- 表中元素个数有限
- 表中元素具有逻辑上的顺序性,有先后次序
- 表中元素的数据类型都相同,具有抽象性
2 线性表的顺序表示
2.1 顺序表的定义
顺序表的特点是表中元素的逻辑顺序与其存储的物理顺序相同。
注意 :线性表中元素的位序是从1开始的,而数组中的元素的下标是从0开始的。
静态顺序表
c
#define MaxSize 50 //定义线性表的最大长度
typedef struct{
ElemType data[MaxSize]; //顺序表的元素
int length; //顺序表的当前长度
}SqList; //顺序表的类型定义
顺序表的基本操作:增,删,改,插。
顺序表的优点:①可随机访问;②存储密度高。缺点:①元素的插入和删除需要移动大量元素;②顺序存储分配需要一段连续的存储空间,不够灵活。
2.2顺序表的插入
在顺序表插入元素,需要做到如下两步工作:
- 将要插入的位置元素及其后续元素整体向后移动一位
- 将元素放到腾出来的位置上
代码:
c
bool ListInsert( SqList &L,int i,ElemType e)
{
if(i<1||i>L.length+1)
return false;
if(L.length>=MaxSize)
return false;
for(int j=L.length;j>=i;j++)
{
L.data[j]=L.data[j-1];
}
L.data[i-1]=e;
L.length++;
return true;
}
插入算法的平均时间复杂度为O(n)。
2.3 顺序表的删除
在顺序表中删除元素,就是找到改元素的位置,将其后续元素整体向前移动一个位置。
代码:
c
bool ListDelete( SqList &L,int i,ElemType e)
{
if(i<1||i>L.length+1)
return false;
e=L.data(i-1);
for(int j=i;j<L.length;j++)
{
L.data[j-1]=L.data[j];
}
L.length--;
return true;
}
删除算法的时间复杂度为O(n)。
2.4 顺序表按值查找
在顺序表中查找第一个元素值为e的元素,并返回位序:
c
int Locate(SqList L,ElemType e)
{
int i;
for(i=0;i<L.length;i++)
{
if(L.data[i]==e)
{
return i+1;
}
}
return 0;
}
按值查找的平均时间复杂度为O(n)。
3 线性表的链式表达
链式存储就是用指针将相互的结点连接起来,链式存储根据链表的构造不同,可分成:单向链表,单向循环链表,双向链表,双向循环链表。
3.1 单链表的定义
线性表的链式存储也称单链表,它是指通过一组任意的存储单元来存储线性表中的数据元素。
| 数据域 | 指针域 |
|---|---|
| data | next |
数据域:存放数据元素的区域
指针域:存储直接后继位置的区域
单链表:
c
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode,*LinkList;
3.1.1 初始化
带头结点的初始化:
c
bool InitList(LinkList &L)
{
L=(LNode*)malloc(sizeof(LNode));
L->next=NULL;
return true;
}
3.1.2 求表长
c
int Length(LinkList L)
{
int len=0;
LNode *p=L;
while(p->next!=NULL)
{
p=p->next;
len++;
}
return len;
}
3.1.3 插入结点操作
插入结点操作将值为x的新结点插入到单链表的第i个位置。
首先查找第i-1个结点,假设第i-1个结点为p,然后令新节点 s 的指针域指向p的后继,再令结点p的指针域指向新插入的结点*s。
c
bool ListInsert(LinkList &L,int i,ElemType e)
{
LNode *p=L;
int j=0;
while(p!=NULL&&j<i-1)
{
p=p->next;
j++;
}
if(p==NULL)
{
return false;
}
LNode *s=(LNode*)malloc(sizeof(LNode));
s->data=e;
s->next=p->next
p->next=s;
return true;
}
必须先执行 s->next=p->next,后执行 p->next=s,否则先执行 p->next=s后,指向原后继的指针就不存在了。
时间复杂度是O(n)

3.1.4 删除结点操作
删除结点操作是将单链表的第i个结点删除。
假设结点p为被删除结点的前驱,只需修改 p的指针域,将p的指针域指向q的下一个结点,然后释放*q的存储空间。
c
bool ListDelete(LinkList &L,int i,ElemType &e)
{
LNode *p=L;
int j=0;
while(p->next==NULL&&j<i-1)
(
p=p->next;
j++;
)
if(p->next==NULL||j>i-1)
{
return false;
}
LNode *q=p->next;
e=q->data;
p->next=q->next;
free(q);
return true;;
}
时间复杂度为O(n)

3.1.5 采用头插法建立单链表
该方法从一个空表开始,生成新的结点,并将读取到的数据放到新结点的数据中,然后将新结点插入到当前链表的表头,即头结点之后。
如图所示:
c
Linklist List_HeadInsert(LinkList &L)
{
LNode *s;int x;
L=(LNode*)malloc(sizeof(LNode));
L->next=NULL;
scanf("%d",&x);
while(x!=9999)
{
s=(LNode*)malloc(sizeof(LNode));
s->data=x;
s->next=L->next;
L->next=s;
scanf("%d",&x);
}
return L;
}

3.1.6 采用尾插法建立单链表
该方法将新结点插入到当前链表的表尾,为必须增加一个尾指针 r,使其始终指向当前链表的尾结点。
如图所示:
c
Linklist List_TailInsert(LinkList &L)
{
int x;
L=(LNode*)malloc(sizeof(LNode));
LNode *s,*r=L;
scanf("%d",&x);
while(x!=9999)
{
s=(LNode*)malloc(sizeof(LNode));
s->data=x;
r->next=s;
r=s;
scanf("%d",&x);
}
r->next=NULL;
return L;
}

3.2 头指针和头结点
链表中,第一个结点存储的位置叫头指针 ,如果链表由头结点,那么头指针就是指向头结点的指针。
头指针所指的不存在数据元素的第一个结点就叫做头结点 (而头结点又指向首元结点),头结点一般不存放数据,存放第一个数据元素的结点叫做第一个数据元素节点,也叫做首元结点。
