【数据结构】线性结构——线性表之顺序存储结构(顺序表)

目录

一、认识线性表

 二、线性表的抽象数据类型

三、线性表的顺序存储结构

1.顺序存储定义

2.数据长度和线性表长度的区别

3.地址计算方法

总结

四、顺序表的插入和删除

1.插入操作

1)插入算法的思路

2)分析

2.删除操作

1)删除算法的思路

2)分析

五、顺序存储的优缺点

优点

缺点


一、认识线性表

线性表是一个线性结构(数据元素之间存在一对一的关系)。它是由n(n>=0)个具有相同类型的数据元素a1,a2,a3.....组成的有限序列,这些数据元素称为结点,记录或表目。当n=0时,称为空表

在较复杂的线性表中,一个数据元素可以由若干个数据项组成。

特点:

  • 线性表中除第一个元素外,其他元素有且仅有一个直接前驱
  • 除最后一个元素外,其他元素有且仅有一个直接后继。

线性表的存储结构主要有以下两种:

  • 定长的顺序存储结构,简称顺序表。程序通过创建数组,分配一块连续的存储空间来建立这种存储结构,主要特点为逻辑相邻,物理相邻
  • 变长的顺序存储结构,简称链表(链式存储结构)。链式存储结构利用指针将线性表中前后相邻的两个元素连接起来以表示数据元素的线性关系。主要特点为逻辑相邻,物理不一定相邻

 二、线性表的抽象数据类型

ADT List{   

             数据对象:D = { ai | ai∈ElemSet, i = 1, 2, …, n, n ≥ 0 }

             数据关系:R = { <ai-1, ai> | ai-1, ai ∈D, i = 1, 2, …, n }   

             基本操作:       

                         LinearList * Init_List( LinearList *L )             

                                              初始条件:线性表L不存在;           

                                             操作结果:构造一个空的线性表;       

                        void Destory_List( LinearList *L)             

                                             初始条件:线性表L已经存在;             

                                             操作结果:销毁线性表L;       

                        LinearList * Clear_List( LinearList *L )             

                                              初始条件:线性表L已经存在;           

                                             操作结果:将线性表L置为空表;

                        int List_Empty( LinearList *L)             

                                             初始条件:线性表L已经存在;           

                                             操作结果:若L为空表,则返回TURE(或1),否则返回FALSE(或0);

                        int List_Length( LinearList *L)             

                                             初始条件:线性表L已经存在;           

                                             操作结果:返回L中数据元素的个数;

                        int Getdata_List( LinearList *L,int i)             

                                             初始条件:线性表L已经存在,并且满足1<=i<=ListLength(L);           

                                             操作结果:将L中第i个元素值返回;

                         int Search_List(LinearList *L,int e,int compare())

                                             初始条件:线性表L已经存在,compare()为数据元素判定函数;           

                                            操作结果:返回L中第一个与e满足compare()的数据元素的位序。若不存在,则返回值为0;

                         void PriorElem(LinearList *L,int e,int *pre_e)

                                             初始条件:线性表L已经存在;            

                                            操作结果:若e是L的数据元素,而且不是第一个数据元素则用pre_e返回他的前驱;

                                                               否则操作失败,pre_e无意义。

                            void PriorElem(LinearList *L,int e,int *next_e)

                                             初始条件:线性表L已经存在;            

                                            操作结果:若e是L的数据元素,而且不是最后一个数据元素则用next_e返回他的后继;

                                                               否则操作失败,next_e无意义。

                             LinearList *Insert_List( LinearList *L,int i,int e )             

                                              初始条件:线性表L已经存在并且满足1<=i<=ListLength(L)+1;           

                                              操作结果:在L中第i个位置前插入新的数据元素,L的长度+1;

                            LinearList *Delete_List( LinearList *L,int i,int *e )             

                                              初始条件:线性表L已经存在并且非空满足1<=i<=ListLength(L);           

                                              操作结果:删除L中第i个位置的数据元素,并用e返回其值,L的长度-1;

}ADT List;


三、线性表的顺序存储结构

1.顺序存储定义

线性表的顺序存储结构是指用一段地址连续的存储单元依次存储线性表的数据元素。

在程序设计中,用一维数组来实现顺序存储。

#define MAXSIZE 30    //存储空间初始分配量
typedef int ElemType;
typedef struct
{
    ElemType data[MAXSIZE];/*数组存储数据元素,最大值为MAXSIZE*/
    int length;    /*线性表当前长度*/
}SqList;

 建立顺序表的三个属性:

1)存储空间的起始位置(数组名data)

2)顺序表最大存储容量(MaxSize)

3)顺序表当前的长度(length)——有效数据的长度

2.数据长度和线性表长度的区别

  • 数组的长度是存储线性表的存储空间的长度,存储分配后这个量一般是不变的。(特殊情况下数组还可以动态分配空间,存储数组的空间是在程序执行过程中通过动态存储分配语句分配 )
  • 线性表长度是线性表中元素的个数,随着线性表的插入和删除操作的进行这个量是变化的。
  • 在任意时刻数组的长度都是大于等于线性表的长度。

3.地址计算方法

数组元素的序号和存储下标的关系示意图

总结

  • 1.顺序表最主要的特点是随机访问(C语言中基于数组),即通过首地址和元素序号可以在O(1)的时间内找到指定的元素。

  • 2.顺序表的存储密度高,每个结点只存储数据元素。无需给表中元素花费空间建立它们之间的逻辑关系(因为物理位置相邻特性决定)

  • 3.顺序表逻辑上相邻的元素物理上也相邻,所以插入和删除操作需要移动大量元素


四、顺序表的插入和删除

1.插入操作

1)插入算法的思路

  • 判断插入位置i是否合理,如果位置不合理抛出异常(如果线性表长度大于等于数组长度,则抛出异常或动态增加容量)
  • 从最后一个元素开始向前遍历到第i个位置,分别将它们向后移动一个位置;
  • 将要插入的元素填入i位置处
  • 表长加1.

2)分析

  • 最好情况:在表尾插入(即i=n+1),元素后移语句将不执行,时间复杂度为O(1)。

  • 最坏情况:在表头插入(即i=1),元素后移语句将执行 n次,时间复杂度为O(n)。

  • 平均情况:假设pi(pi=1/(n+1) )是在第i个位置上插入 一个结点的概率,则在长度为n的线性表中插入一个结点时所需移动结点的平均次数为(n-1)/2

2.删除操作

1)删除算法的思路

  • 判断删除位置i是否合理,如果删除位置不合理,抛出异常;
  • 取出删除元素
  • 从删除位置开始遍历到最后一个位置,分别将它们都向前移动一个位置
  • 表长减1

2)分析

  • 最好情况:删除表尾元素(即i=n),无须移动元素,时间复杂度为O(1)。

  • 最坏情况:删除表头元素(即i=1),需要移动除第一个元素外的所有元素,时间复杂度为O(n)。

  • 平均情况:假设pi(pi=1/n)是删除第i个位置上结点的概率,则在长度为n的线性表中删除一个结点时所需移动结点的平均次数为(n-1)/2


五、顺序存储的优缺点

优点

  • 无须为表示表中元素之间的逻辑关系而增加额外的存储空间
  • 可以快速存取表中任意一位置的元素

缺点

  • 插入和删除操作需要移动大量元素
  • 当线性表长度变化较大时,难以确定存储空间的容量
  • 造成存储空间的“碎片”
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据结构算法中,线性表是一种常见的数据结构,它由一组有限的、相同类型的数据元素组成,这些元素按照线性的顺序依次排列。通常情况下,我们使用数组或链表来实现线性表。 如果要在程序中使用线性表,通常需要先定义一个线性表结构体或类,用来存储线性表元素以及其他相关信息。例如,以下是一个使用数组实现线性表的 C 语言结构体: ``` typedef struct { int *data; // 存储线性表元素的数组 int length; // 线性表的长度 int size; // 数组的大小 } ArrayList; ``` 在使用线性表时,我们可以通过下标访问线性表中的元素,或者使用一些操作函数来实现插入、删除、查找等操作。例如,以下是一个使用数组实现的线性表的查找函数: ``` int search(ArrayList *list, int value) { for (int i = 0; i < list->length; i++) { if (list->data[i] == value) { return i; // 返回元素位置 } } return -1; // 没有找到元素 } ``` 除了数组,我们还可以使用链表来实现线性表。链表是由一系列节点组成的数据结构,每个节点都包含一个存储元素的数据域和一个指向下一个节点的指针域。以下是一个使用链表实现线性表的 C 语言结构体: ``` typedef struct Node { int data; // 存储元素的数据域 struct Node *next; // 指向下一个节点的指针域 } ListNode; typedef struct { ListNode *head; // 指向链表头节点的指针 int length; // 线性表的长度 } LinkedList; ``` 在使用链表时,我们需要通过遍历链表来访问其中的元素,或者使用一些操作函数来实现插入、删除、查找等操作。例如,以下是一个使用链表实现的线性表的插入函数: ``` void insert(LinkedList *list, int value, int position) { if (position < 0 || position > list->length) { // 判断插入位置是否合法 return; } ListNode *node = (ListNode*)malloc(sizeof(ListNode)); // 创建新节点 node->data = value; node->next = NULL; if (position == 0) { // 插入到链表头部 node->next = list->head; list->head = node; } else { // 插入到链表中间或末尾 ListNode *prev = list->head; for (int i = 0; i < position - 1; i++) { prev = prev->next; } node->next = prev->next; prev->next = node; } list->length++; // 更新链表长度 } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值