大话数据结构(三)——线性表

线性表是零个或多个数据元素的有限序列。 首先它是一个序列。也就是说,元素之间是有顺序的,若元素存在多个,则第一个元素无前驱,最后一个元素无后继,其他每个元素都有且只有一个前驱和后继。 

线性表的抽象数据类型定义如下:

ADT 线性表(List)
Data
线性表的数据对象集合为{a
1, a2, ......, an}, 每个元素的类型均为Dat
其中, 除第一个元素a
1外, 每一个元素有且只有一个直接前驱元素,
除了最后一个元素
a
n外, 每一个元素有且只有一个直接后继元素。
数据元素之间的关系是一对一的关系。
Operation
InitList(*L):
初始化操作, 建立一个空的线性表L
ListEmpty(L): 若线性表为空, 返回true, 否则返回false
ClearList(*L): 将线性表清空。
GetElem(L, i, *e): 将线性表L中的第i个位置元素值返回给e
LocateElem(L, e): 在线性表L中查找与给定值e相等的元素,如果查找成功, 返回该元素在表中序号表示成功

循环集合B中的元素,判断当前元素是否存在A中,若不存在,则插入到A中即可。
/* 将所有的在线性表Lb中但不在La中的数据元素插入到La*/
void unionL(List *La, List Lb)
{
int La_len, Lb_len, i;
/*
声明与LaLb相同的数据元素e */
ElemType e;
/*
求线性表的长度 */
La_len = ListLength(*La);
Lb_len = ListLength(Lb);
for (i = 1; i <= Lb_len; i++)
{
/*
Lb中第i个数据元素赋给e */
GetElem(Lb, i, &e);
/* La
中不存在和e相同数据元素 */
if (!LocateElem(*La, e))
/* 插入 */
ListInsert(La, ++La_len, e);
}
}
值得注意的是, 注意一个很容易混淆的地方: 当你传递一个参数给函数的时候, 这个参数会不会在函数内被改动决定了使用什么参数形式。 如果需要被改动,则需要传递指向这个参数的指针, 如果不用被改动, 可以直接传递这个参数
线性表的顺序存储结构代码:
/* 存储空间初始分配量 */
#define MAXSIZE 20
/* ElemType
类型根据实际情况而定, 这里假设为int */
typedef int ElemType;
typedef struct
{
/*
数组存储数据元素, 最大值为MAXSIZE */
ElemType data[MAXSIZE];
/*
线性表当前长度 */
int length;
} SqList;

1.存储空间的起始位置: 数组data, 它的存储位置就是存储空间的存
储位置。
2.线性表的最大存储容量: 数组长度
MaxSize
3.线性表的当前长度:
length
插入算法的思路
step1:如果插入位置不合理,抛出异常;
step2:如果线性表长度大于等于数组长度,则抛出异常或动态增加容量;
step3:从最后一个元素开始向前遍历到第
i个位置,分别将它们都向后移动一个位置;
step4:将要插入元素填入位置
i处;表长加1

插入操作的实现代码如下:
/* 初始条件: 顺序线性表L已存在, 1≤i≤
ListLength(L)
*/
/*
操作结果: 在L中第i个位置之前插入新的数据元
eL的长度加1 */
Status ListInsert(SqList *L, int i, ElemType e)
{
int k;
/*
顺序线性表已经满 */
if (L->length == MAXSIZE)
return ERROR;
/*
i不在范围内时 */
if (i < 1 || i >L->length + 1)
return ERROR;
/*
若插入数据位置不在表尾 */
if (i <= L->length)
{
/*
将要插入位置后数据元素向后移动一位 */
for (k = L->length - 1; k >= i - 1; k--)
L->data[k + 1] = L->data[k];
}/
*
将新元素插入 */
L->data[i - 1] = e;
L->length++;
return OK;

}

删除操作思路:
step1:如果删除位置不合理,抛出异常;
step2:取出删除元素;
step3: 从删除元素位置开始遍历到最后一个元素位置, 分别将它们都向前移动一个位置;

step4:表长减1
删除操作实现代码:
/* 初始条件: 顺序线性表L已存在, 1≤i≤
ListLength(L) */
/*
操作结果: 删除L的第i个数据元素, 并用e返回
其值,
L的长度减1 */
Status ListDelete(SqList *L, int i, ElemType *e)
{
int k;
/*
线性表为空 */
if (L->length == 0)
return ERROR;
/*
删除位置不正确 */
if (i < 1 || i > L->length)
return ERROR;
*e = L->data[i - 1];
/*
如果删除不是最后位置 */
if (i < L->length)
{
/*
将删除位置后继元素前移 */
for (k = i; k < L->length; k++)
L->data[k - 1] = L->data[k];
}L
->length--;
return OK;
}


线性表的顺序存储结构的优缺点:
优点:1.无须为表示表中元素之间的逻辑关系而增加额外的存储空间;2.可以快速地存取表中任一位置的元素。

缺点:1.插入和删除操作需要移动大量元素;2.当线性表长度变化较大时,难以确定存储空间的容量;3.造成存储空间的“碎片”。


线性表链式存储结构

以前在顺序结构中,每个数据元素只需要存数据元素信息就可以了。现 在链式结构中,除了要存数据元素信息外,还要存储它的后继元素的存储地址。

静态链表
首先我们让数组的元素都是由两个数据域组成, datacur。 也就是说, 数组的每个下标都对应一个data和一个cur。 数据域data, 用来存放数据元素, 也就是通常我们要处理的数据; 而cur相当于单链表中的next指针, 存放该元素的后继在数组中的下标, 我们把cur叫做游标。我们把这种用数组描述的链表叫做静态链表, 这种描述方法还有起名叫做游标实现法。

循环链表
将单链表中终端结点的指针端由空指针改为指向头结点, 就使整个单链 表形成一个环, 这种头尾相接的单链表称为单循环链表, 简称循环链表 circular linked list) 。

双向链表

双向链表(double linkedlist) 是在单链表的每个结点中, 再设置一个指向 其前驱结点的指针域。 所以在双向链表中的结点都有两个指针域, 一个指向直接后继, 另一个指向直接前驱。
/* 线性表的双向链表存储结构 */
typedef struct DulNode
{
ElemType data;
struct DuLNode *prior; /*
直接前驱指针 */
struct DuLNode *next; /*
直接后继指针 */
} DulNode, *DuLinkList;

将s插入到p和p->next之间
/* p赋值给s的前驱, 如图中① */
s->prior = p;
/*
p->next赋值给s的后继, 如图中② */
s->next = p->next;
/*
s赋值给p->next的前驱, 如图中③ */
p->next->prior = s;
/*
s赋值给p的后继, 如图中④ */
p->next = s;


总结

这一章, 我们主要讲的是线性表。
先谈了它的定义, 线性表是零个或多个具有相同类型的数据元素的有限序列。 然后谈了线性表的抽象数据类型, 如它的一些基本操作。之后我们就线性表的两大结构做了讲述, 先讲的是比较容易的顺序存储结构, 指的是用一段地址连续的存储单元依次存储线性表的数据元素。通常我们都是用数组来实现这一结构。后来是我们的重点, 由顺序存储结构的插入和删除操作不方便, 引出了链式存储结构。 它具有不受固定的存储空间限制, 可以比较快捷的插入和删除操作的特点。 然后我们分别就链式存储结构的不同形式, 如单链表、 循环链表和双向链表做了讲解, 另外我们还讲了若不使用指针如何处理链表结构的静态链表方法。总的来说, 线性表的这两种结构
其实是后面其他数据结构的基础, 把它们学明白了, 对后面的学习有着至关重要的作用。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值