《软件技术基础》之《线性表》
线性表是最基础和最常用的一类数据结构,它表示的是线性结构。
逻辑结构:在线性结构中,数据元素之间存在着一对一的关系,其特点是数据元素之间按某种规定存在一个顺序关系。
线性表的分类:
线性表的定义
n个同类型数据元素的有限序列,记为:L=(a1,a2,…,ai,…,an)。
其中,L为表名,i为数据元素ai在线性表中的位序,n为线性表的表长,n=0时称为空表。
数据元素之间的关系:
ai-1领先于ai ,ai领先于ai+1 。称ai-1是ai的直接前驱,ai+1是ai的直接后继。
除第一元素a1外,均有唯一的前驱;除最后元素an外,均有唯一的后继。
线性表数据结构的定义
数据结构的一般定义:Data Structure=(D,L,S,O)
D=data(数据) L=logic(逻辑)S=storage(物理存储)O=operation(基本操作)
O是在逻辑结构上对数据结构或数据元素的一组基本运算,与具体实现无关,可以利用给定的基本操作构造更复杂的运算。
线性数据结构的存储结构
顺序存储结构
用一组地址连续的存储单元依次存放线性表中的数据元素。
以“存储位置相邻”表示有序对<ai-1,ai>,即:LOC(ai)=LOC(ai-1)+d。d是一个一个数据元素所占的存储单位长度,用物理地址的相邻关系来表示逻辑次序的相邻。
实现实例(C语言描述):
#define LIST_INIT_SIZE 100
#define LISTINCREMENT 10
Typedef struct Sqlist {
ElemType *elem; // 存储地址基址
int length; // 当前长度
int listzise; // 当前分配的存储容量
}Sqlist; //顺序表
插入元素操作
步骤:
- 检查插入位置是否合法,如果合法则继续,否则退出;
- 判表是否已占满;因为事先静态地分配空间,可能存在所分配存储空间全部被占用的情况,此时也不能实现插入;
- 若前面检查通过则数据元素依次向后移动一个位置;为避免覆盖原数据,应从最后一个开始向前依次移动;
- 新数据元素放到恰当位置;
- 表长加 1。
算法实现:
Status List_Insert(ListPtr L,int pos,ElemType elem)
{
Status status=range_error;
int len=L->length,i;
if(len == MAXSIZE) status=overflow;
else if(1<=pos && pos<=len+1)
{
for(i=len;i>=pos;i--) L->elem[i+1]=L->elem[i]; // 数据后移一个位置
L->elem[pos]=elem;
L->length++;
status=success;
}
return status;
}
删除元素操作
步骤:
- 检查删除位置是否合法;
- 若检查通过,数据元素依次向前移动一个位置;
- 表长减1。
算法实现:
Status List_Remove(ListPtr L,int pos)
{
Status status=range_error;
int len=L->length,i;
if(1<=pos && pos<=len)
{
for(i=pos;i<len;i++) L->elem[i]=L->elem[i+1]; // 数据前移一个位置
L->length--;
status=success;
}
return status;
}
链式存储结构(单链表)
用一组地址任意的存储单元存放线性表中的数据元素。
数据域(数据元素)+指针域(指示后继元素存储位置)= 结点
以“结点的序列”表示线性表,称为链表。
逻辑结构:(a1,a2,…,ai,…,an)
物理结构:
以线性表中第一个数据元素a1的存储地址作为线性表的地址,称为线性表的头指针。
有时为了操作方便,在第一个结点之前虚加一个“头结点”,并用链表的头指针指向头结点,称为带头结点的单链表。
类型定义
typedef struct node {
ElemType data; // 数据域
struct node *next; // 指针域
}ListNode,*ListNodePtr;
typedef ListNodePtr List,*ListPtr;
变量定义和使用
ListNode n1,n2; // 定义两个结点变量
ListNodePtr p=&n1; // 定义一个指向结点的指针变量p,并存放n1的地址(指针)
n1.nest=&n2; // 结点n1的指针域存放结点n2的地址
List L; // 定义一个单链表L
在单链表中获取第pos个元素
ListNode List_Retrieve(ListPtr L,int pos)
{
if(pos < 0) return null;
if(pos == 0) return L;
ListNode *node=L;
int i=0;
while(node && i<pos)
{
node=node->next;
i++;
}
if(node == null) return null;
return node;
}
插入操作
步骤:
- 找到ai-1的位置;
- 构造一个数据域为elem的新结点;
- 将其挂在单链表上。
算法实现:
Status List_Insert(ListPtr L,int pos,ElemType elem)
{
Status status;
ListNodePtr pre,s;
status=List_SetPosition(L,pos-1,&pre); // 找到插入位置的前驱
if(status == success)
{
s=(ListNodePtr)malloc(sizeof(ListNode));
if(s)
{
s->data=elem;
s->next=pre->next;
pre->next=s;
}
else status=fatal;
}
return status;
}
删除操作
步骤:
- 求得ai-1的指针p;
- 修改ai-1的后继域为ai+1的地址;
- 释放第pos个结点所占的存储空间。
算法实现:
Status List_Remove(ListPtr L,int pos)
{
Status status=range_error;
ListNodePtr pre,q;
status=List_SetPosition(L,pos-1,&pre); // 找到插入位置的前驱
if(status == success)
{
q=pre->next;
pre->next=q->next;
free(q);
}
return status;
}