数据结构和算法分析程序员

【数据结构与算法】线性表(顺序存储与链式存储结构)

2018-08-10  本文已影响32人  大基本功

定义

线性表(linear list)是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列。数据元素是一个抽象的符号,其具体含义在不同的情况下一般不同。
在稍复杂的线性表中,一个数据元素可由多个数据项(item)组成,此种情况下常把数据元素称为记录(record),含有大量记录的线性表又称文件(file)。
线性表中的个数n定义为线性表的长度,n=0时称为空表。在非空表中每个数据元素都有一个确定的位置,如用ai表示数据元素,则i称为数据元素ai在线性表中的位序。
线性表的相邻元素之间存在着序偶关系。如用(a1,…,ai-1,ai,ai+1,…,an)表示一个顺序表,则表中ai-1领先于ai,ai领先于ai+1,称ai-1是ai的直接前驱元素,ai+1是ai的直接后继元素。当i=1,2,…,n-1时,ai有且仅有一个直接后继,当i=2,3,…,n时,ai有且仅有一个直接前驱

抽象数据类型

所谓的抽象数据类型就是把数据类型和相关炒作绑在一起.

线性表的物理存储结构

线性表有两种物理存储结构:顺序存储结构和链式存储结构

线性表的顺序存储结构

#define MAXSIZE 20
typedef int ElemType;
typedef struct{
   ElemType data[MAXSIZE];
   int length;//线性表当前长度
}Sqlist;
获取元素操作
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
//Status 是函数的类型,其值是函数结果状态代码,如OK等.
//初始条件:顺序线性表L已存在
//操作结果:用e返回L中第i个数据元素的值
Status GetElem(sqlist L, int i ,ElemType *e){
 if(L.length == 0 || i< 1 || i>L.length ){
returen ERROR;
}
*e = L.data[i-1];
return OK;
}
插入新元素

插入算法的基本思路
① 如果插入位置不合理,抛出异常
② 如果线性长度大于等于数组长度,抛出异常或动态增加数组容量
③从最后一个元素开始向前遍历到第i个位置,分别将他们都向后移动一个位置
④ 将要插入元素填入位置i处
⑤ 线性表长+1

Status ListInsert(Sqlist *L ,int i, ElemType e ){

int k;
if(L->length == MAXSIZE)//顺序线性表已满{
 return ERROR;
}
if(i<1 | | i>L->length+1)//当I不在范围内时{
 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;
删除操作

删除算法的思路
①如果删除位置不合理抛出异常
②取出删除元素
③从删除元素位置开始遍历到最后一个元素位置,分别将他们都向前移动一个位置
④表长-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;
插入和删除时间复杂度分析
线性表顺序存储结构的优缺点

优点:
①无须为表示表中元素之间的逻辑关系而增加额外的存储空间
②可以快速的存取表中任意位置的元素
缺点:
①插入和删除操作需要移动大量元素
②当线性表长度变化较大时,难以确定存储空间的容量
③容易造成存储空间的碎片

线性表的链性存储结构

线性表的顺序存储结构最大缺点就是插入和删除时需要移动大量元素,需要耗费时间,其原因就在于相邻两元素的存储位置也具有邻居关系,它们在内存中的位置是紧挨着,中间没有间隙,无法快速的插入和删除,所以要移动大量元素,于是就有了链式存储结构

定义
单链表

对于线性表来说,总得有个头有个尾,链表也不例外。我们把链表中的第一个节点的存储位置叫做头指针,最后一个节点指针为空(NULL)


头指针与头结点的异同

头指针:

头结点:

空链表图例
单链表的存储结构

在C语言中可以用结构指针来描述单链表

typedef struct Node{
   ElemType data; //数据域
   struct Node* Next;//指针域
}Node;
typedef struct Node* LinkList;
//可以看出结点由存放数据元素的数据域和存放后继结点地址的指针域组成
单链表的读取

上代码:

Status GetElem(LinkList L, int I, ElemType *e){
   int j;
   LinkList p;
  
   p = L->next;
   j = 1;

  while(p && j <i){
   p = p->next;
   ++j;
}  

if( !p || j>i){
 return ERROR;
}

*e = p->data;
return OK;

}
单链表的插入

如图我们发现不用改变其他结点,只需要让s->next和p->next的指针做一点改变即可

s->next = p->next;
p->next = s;

单链表第i个数据插入结点算法思路:

Status ListInsert(LinkList *L ,int I, ELemType e){
  
 int j;
LinkList p,s;
p = *L;
j = 1;
while(p && j<1){
 p = p->next;
 j++;
}
if( !p || j>1){
 return ERROR;
}
s = (LinkList)malloc(sizeof(Node));
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
单链表的删除

假设a2的结点为q,要实现结点q删除单链表的操作,其实就是将它的前结点的指针绕过指向后继结点即可

即: p->next = p->next->next;
单链表第i个数据删除结点算法思路:

Status ListDelete(LinkList *L ,int I, ELemType *e){
  
 int j;
LinkList p,q;
p = *L;
j = 1;
while(p->next && j<1){
 p = p->next;
 ++j;
}
if( !(p->next) || j>1){
 return ERROR;
}

q = p->next;
p->next = q->next;
*e = q->data;
free(q);
return OK;
}
效率PK

单链表的整表创建

创建单链表的过程是一个动态生成链表的过程,从空表的初始状态起,依次建立各元素结点并逐个插入链表

单链表的整表创建的算法思路如下:

头插法建立单链表

头插法从一个空表开始,生成新结点,读取数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头上,直到结束为止
简单来说,就是把新加进的元素放在表头后的第一个位置

上代码

void CreatListHead(LinkList *L, int n){
 LinkList p;
 int I;
 srand(time(0));初始化随机数
 *L = (LinkList)malloc(sizeof(Node));
(*L)->next = NULL;
for (I = 0 ;I <n; I++){
 
 p =(LinkList)malloc(sizeof(Node));
 p->data = rand()%100+1;
 p->next = (*L)->next;
 (*L )->next = p;
}
}

头部法生成的链表中结点的次序和输入的顺序是相反的

尾部发建立单链表

上代码

void CreatListTail(LinkList *L, int n){

LinkList p,r;
int I;
srand(time(0));
*L = (LinkList)malloc(sizeof(Node));
r = *L;
for( I = 0;i<n;i++){
p = (Node *)malloc(sizeof(Node));
p->data = rand()%100+1;
r->next = p;
r = p;
}
r->next = NULL;
}

单链表的整表删除

单链表整表删除的算法思路

上代码

Status ClearList(LinkList *L){
LinkList p,q;
p = (*L)->next;
while(p){
q = p->next;
free(p);
p=q;
}
(*L)->next = NULL;
return OK;
}

单链表结构与顺序存储结构优缺点

从存储分配方式,时间性能,空间性能三方面来对比

存储分配方式
时间性能

查找

插入和删除

空间性能

综上:若线性表需要频繁的查找,很少进行插入和删除操作宜采用顺序存储结构;若需频繁插入和删除时,宜采用单链表结构

上一篇下一篇

猜你喜欢

热点阅读