线性表
线性表及其逻辑结构
1. 定义
线性表是具有相同特性的数据元素的一个有限数列
D = {ai|1 <= i <=n, n >= 0, ai为ElemType类型} //ElemType是自定义的类型标识符
R = {r}
r = {<ai, a(i + 1)> | 1 <= i <= n - 1}
2. 基本运算
InitList(&L); //初始化函数,只要将线性表的length赋0即可
Destory(&L); //销毁线性表,释放线性表内存空间,即free(L)
ListEmpty(L); //判断线性表是否为空表,即判断length是否为0即可,返回值是true或false,即bool型
ListLength(L); //求出并输出表的长度
DispList(L); //输出线性表,即利用循环打印线性表,注意要先判断表是否为空表
GetElem(L, i, &e); //求i位置的值并用e输出
LocateElem(L, e); //按元素查找与e相同的元素
ListInsert(&L, i, e); //在i位置上插入e,之后该表的长度加一
ListDelete(&L, i, &e); //删除i位置上的元素,并用e输出该元素,之后该表的长度减一
顺序存储结构
1.顺序表
顺序表由线性表直接映射而来(顺序表可以看成是一个数组)
如图表示的是顺序表的基本形式,数据元素本身连续存储,每个元素所占的存储单元大小固定相同,元素的下标是其逻辑地址,而元素存储的物理地址(实际内存地址)可以通过存储区的起始地址加上逻辑地址(第i个元素)与存储单元大小的乘积计算而得
#define MaxSize 50 //给定线性表的最大长度
typedef struct
{
ElemType data[MaxSize]; //data[]用于储存ElemType类型的元素
int length; //存放线性表的长度
}SqList; //顺序表类型
2.基本运算
SqList InitList(SqList * &L)
{
L.length = 0; //初始化长度为0
return L;
}
//初始化即令顺序表的长度为0
SqList CreateList(SqList * &L, int n, ElemType a[]) //void类型也是ok的
{
int i = 0, k = 0;
L = (SqList *)malloc(sizeof(SqList)); //分配内存空间
while(i < n)
{
L-> data[k] = a[i];
i ++;
k ++;
}
L -> length = k;
/*for (int i = 0; i < n; i ++)
{
scanf("%d", &L -> data[i]); //完成赋值
L -> length ++;
}
*
*/ //手动输入
return L;
}
//创建顺序表,分配空间,将值输入,获得长度
SqList InsertList(SqList * &L, int i, ElemType e)
{
if (i < 1 || i > L -> length + 1) //判断位置是否有效
{
printf("位置无效!!!\n");
return 0;
}
if (L -> length >= MaxSize)//判断存储空间是否已满
{
printf("当前存储空间已满!!!\n");
return 0;
}
for (int j = L -> length; j >= i; j--)//位置i及之后元素后移
{
L -> data[j] = L -> data[j - 1];
}
L -> data[i - 1] = e;
L -> length++; //切记要改变length
return L;
}
//在i位置插入e元素
bool ListDelete(SqList * &L, int i)
{
if (i < 1 || i > L -> length) //判断位置是否正确
{
printf("位置无效!!!\n");
return false;
}
for (int j = i; j <= L -> length - 1; j++)//位置i之后元素依次前移覆盖
{
L -> data[j - 1] = L -> data[j];
}
L -> length--;
return true; //删除成功后返回true
}
//删除函数 删除位置i的元素 i之后的元素依次前移
int LocateElem(SqList * L, ElemType e)
{
int i = 0;
while(i < L -> length && L -> data[i] != e)
{
i ++;
}
if(i >= L -> length)
return flase; //查找失败返回0
else
return i + 1; //返回物理位置
}
//查找函数 按位置从小到大查找第一个值等于e的元素,并返回位置
void DestroyList(SqList * &L) {
free(L); //释放L的内存空间
}
//销毁线性表,只需将线性表的存储空间释放即可,这里需知晓我们用malloc函数分配空间,如果不进行释放,可能会造成内存泄漏。
void PrintList(SqList * L)
{
for (int i = 0; i < L-> length; i ++)
{
printf("%d ", L -> data[i]);
}
printf("\n");
}
//打印线性表
链式存储结构
1.单链表
通俗的来说链表就是由许多个包含数据元素和指针域的节点按链式结合得到的线性表。
Typedef struct
{
ElemType data; //存放数据元素
struct LNode * next; //指向后继节点
}LinkNode; //节点类型
2. 基本运算
- 插入
//代码实现
s -> next = L -> next;
L -> next = s;
- 删除
//代码实现
q = p -> next; //q用作临时保存被删节点
p -> next = q -> next;
free(q); //记得要释放内存空间
- 创建
头插法
void CreatList(LinkNode * &L, ElemType a[], int n)
{
int i = 0;
LNode * s;
L = (LinkNode *)malloc(sizeof(LinkNode));
L -> next = NULL;
for(i = 0; i < n; i ++)
{
s = (LinkNode * )malloc((sizeof(LinkNode)); //创建节点
s -> data = a[i];
s -> next = L -> next; //插入
L -> next = s;
}
}
头插法的结果获得的是与a[]逆序的单链表
尾插法
void CreatList(LinkNode * &L, ElemType a[], int n)
{
int i = 0;
LNode * s,r;
L = (LinkNode *)malloc(sizeof(LinkNode));
r = L;
for(i = 0; i < n; i ++)
{
s = (LinkNode * )malloc(sizeof(LinkNode));
s -> data = a[i];
r -> next = s;
r = s;
}
r -> next = NULL;
}
尾插法获得我的链表中元素顺序与数组里顺序相同
- 基本运算的实现
//输出线性表
void DispList(LinkNode *L)
{
LinkNode *p = L->next;
while(p!=NULL)
{
printf("%d ",p->data);
p = p->next;
}
printf("\n");
}
//求线性表的长度,让P指向头结点,n用来累计结点的个数
int ListLength(LinkNode *L)
{
int n = 0;
LinkNode *p = L;
while(p->next!=NULL)
{
n++;
p = p->next;
}
return n;
}
//插入数据元素,找到第i-1个结点由P指向它。若第i-1个结点存在,将值为e的结点(由s指向)插入的p所指结点的后面
bool ListInsert(LinkNode *&L,int i,ElemType e)
{
int j = 0;
LinkNode *p = L,*s;
if(i<0)
return false;
while(j<i-1 && p!=NULL)
{
j++;
p = p->next;
}
if(p==NULL)
return false;
else
{
s = (LinkNode *)malloc(sizeof(LinkNode)); //创建新结点s
s->data = e; //将e赋予s的值
s->next = p->next; //将结点s插入到p的后面
p->next = s;
return true;
}
}
//按元素查找
int LocateElem(LinkNode *L,ElemType e)
{
int i=1;
LinkNode *p=L->next; //P指向首结点,i置为1(首结点的序号为1)
while(p!=NULL && p->data!=e)
{
p = p->next;
i++;
}
if(p==NULL)
return 0;
else
return i;
}
//删除数据元素,找到第i-1个结点由P指向它。若第i-1个结点存在,也存在后继结点(由q指向),则删除q所指结点
bool ListDelete(LinkNode *&L,int i,ElemType &e)
{
int j = 0;
LinkNode *p = L,*q; //p指向头结点,j置为0(头结点的序号为0)
if(i<=0)
return false;
while(j<i-1&&p!=NULL)
{
j++;
p = p->next;
}
if(p==NULL)
return false;
else
{
q=p->next;
if(q==NULL)
return false;
e=q->data;
p->next = q->next; //从链表中删除q结点
free(q);
return true;
}
}
小结
这里仅介绍了线性表的顺序表和单链表,不难看出在做插入和删除的操作时,链表的操作更为省时简单,但是就存储空间利用率来说,顺序表的利用率更大。同时就学习来说,顺序表可等价于数组来学,学起来较容易。
不论哪种存储结构都有益于我们更好的认识数据结构。