(自用,侵删)
线性结构基本特点:除第一个元素无前驱,最后一个元素无后继外,其他所有元素都有一个前驱和一个后继
线性表是最常用的一种线性结构
线性表:由n个数据特性相同的元素构成的有限序列
顺序表
线性表的顺序存储表示:用一组地址连续的存储单元依次存储线性表的数据元素
随机存取
//顺序表存储结构
#define MAXSIZE 100
typedef struct
{
ElemType *elem;//存储空间基地址,ElemType为元素类型
int length;
}SqList;//顺序表结构类型为SqList
稀疏矩阵顺序存储结构类型定义
#define MAXSIZE 100
typedef struct
{
float coef;//系数
int expn;//指数
}Polynomial;
typedef struct
{
Polynomial *elem;//存储空间基地址
int length;
}SqList;
【例】图书数据顺序存储类型
#define MAXSIZE 1000
typedef struct
{
char no[30];//书编号
char name;[50]//书名
float price;//图书价格
}Book;
typedef struct
{
Book *elem;
int length;
}SqList;
通过L.elem[i-1]访问表中位置序号为i的图书记录
基本操作
初始化
Status InitList(SqList &L)
{
L.elem=new ELemtype[MAXSIZE]
if(L!=elem) exit(OVERFLOW);
L.length=0;
return Ok;
}
取值
Status GetElem(SqList L,int i,Elemtype &e)
{
if(i>L.length||i<1)
return ERROR;
e=L.elem[i-1];
return OK;
}
插入
Status ListInsert(SqList &L,int i,Elemtype e)
{
int j;
if((i>L.length+1)||(i<1))return ERROR;
if(j=L.length) return ERROR;//j为链表中第j个位置
for(j=L.length,j>i,j--)
L.elem[j]=L.elem[j-1];
L.elem[i-1]=e;
L.length++;
return OK;
}
删除
链表
任意存储单元存储线性表的元素
顺序存取
存储本身的信息和直接后继的信息,组成了数据元素的存储映像,称为结点,
包括数据域,指针域
指针或链:指针域中存储信息
线性链表或单链表:链表中每个结点只包含一个指针域的结点
链式映像或非顺序映像:数据元素逻辑相邻物理位置不紧邻的数据结构
//单链表每个结点的存储结构
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LNode,*LinkList;//LinkList为指向结构体的指针类型
首元结点:链表中存储第一个数据元素的结点
头结点:首元结点前附设的一个结点,可以不存储任何信息,也可以存储与数据元素类型相同的其他附加信息(如,当数据元素为整数型时,头结点的数据域可存放该头结点的长度)
其指针域保存首元结点的的地址,元素操作与其他结点相同(元素类型相同)
L为空指针,链表为空时,不设头结点,L==NULL,设头结点,L->next==NULL
头指针:指向链表的第一个结点的指针(设头结点指向头结点,不设指向首元结点)
基本操作
初始化
Status InitList(LinkList &L)
{
L=new LNode;//生成头结点,头指针L指向头结点
L->next=NULL;
return OK;
}
取值
Status GetElem(LinkList &L,int i,ElemType &e)
{
p=L-next;//-
int j;
while(p&&j<i)
{
p=p->next;
j++;
}
if(!p||p>i)return ERROR;
e=p->data;
return OK;
}
按值查找
LNode *LocateElem(LinkList &L,ElemType e)
{
p=L->next;
int i=1;
while(p&&p->data!=e)
p=p->next;
return p;
}
插入
插入到结点i的位置,即之前的位置(注:此函数为两个元素之间插入)
Status ListInsert(LinkList &L,int i,ElemType e)
{
p=L;
int j=0;
/*
s=new LNode;
q=new LNode;
while(p&&j<i)
{
q=p;
p=p->next;
j++;
}
q->next=s;
s->next=p;
s->data=e;
return Ok;*/此段代码冗余
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;
return OK;
}
删除
循环链表
表中最后一个结点的指针域指向头结点
无头结点 | 有头结点 | |
单链表 | p!=NULL | p->next!=NULL |
循环单链表 | p!=L | p->next!=L |
将两个表合并为一个表:
p=B->next->next;
B->next=A->next;
A->next=p;
双向链表
//双向链表的存储结构
插入
删除
顺序表和链表的比较
空间性能
是否需要预先分配存储空间 | ||
顺序表 | 不需要 | |
链表 | 需要 |
存储密度大小
存储密度=数据元素本身占用的存储量 / 节点结构占用的存储量
顺序表:100%(不考虑空闲区),单链表:50%
为节约空间,长度变化不打时采用顺序表
时间性能
存储元素 | 插入和删除 | |
顺序表 | O(1) | O(n) |
链表 | O(n) | O(1) |
线性表的应用
线性表的合并
有序表的合并
有序表:数据元素相互可比较,且依值非递增或非递减有序排列的线性表
顺序有序表
链式有序表
习题
1.1二叉树顺序存储结构比链式存储结构节省存储空间 ×
对于完全二叉树顺序存储结构可以比链式存储结构节省空间,链式存储结构不会浪费存储空间,但对于大规模的二叉树会占用大量的指针空间
2.1如果线性表最常用的操作是在最后一个元素之后插入一个元素,和删除第一个元素,采用仅有尾指针的单循环链表存储方式最节省运算时间
2.2最后一个结点后插入一个结点或删除最后一个结点:带头结点的双循环链表,带尾指针的双循环链表
2.3存取第一个元素和前驱后继的值:线性表
2.4静态链表中指针表示的是下一元素在数组中的位置
静态链表类似于数组,需要预先分配存储空间,在插入和删除时最为方便只需要修改指针,动态链表在需要时才申请内存空间(通过molloc或free函数),空间利用率高
2.5单链表中指针p指向其中某个结点,在该结点前插入一个由指针s指向的结点,无法通过已知条件实现
在结点前插入必须知道该结点前驱结点的指针