线性表的顺序存储结构

线性表的顺序存储结构

       相关概念

       举个栗子:

       大学的时候,宿舍有一个同学,帮大家去图书馆占座。他每次去图书馆,挑一个好地儿,把他书包里的书,一本一本按座位放好,若书不够,再把水杯,水笔都用上,长长一排,九个座硬是被他占了。

       1.  顺序存储的定义

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

       示意图:

       

       2.  顺序存储方式

       线性表的顺序存储结构,其实就和刚才图书馆占座的例子一样,就是在内存中找了块地儿,通过占位的形式,把一定内存空间给占了,然后把相同数据类型的数据元素依次存放在这块空地中。

       既然线性表的每个数据元素的类型都相同,所以可以用一维数组来实现顺序存储结构,即把第一个数据元素存到数组下标为0的位置中,接着把线性表相邻的元素存储在数组中相邻的位置。

       这里有几个关键的地方:

      1)为了建立一个线性表,要在内存中找一块地,这块地的第一位置就非常关键,他是存储空间的起始位置

      2)数组的长度就是这个最大存储容量

      3)线性表的当前长度

      3. 数据长度与线性表长度区别

      1)数组的长度是存放线性表的存储空间的长度,存储分配后这个量一般是不变的。当然,一般高级语言是可以通过编程手段实现动态分配数组,不过这会带来性能上的损耗。

      2)线性表的长度是线性表中数据元素的个数,随着线性表插入和删除操作的进行,这个量是变化的。

      在任意时刻,线性表的长度应该小于等于数组的长度。

      4.  地址计算方法

      数组是从0开始第一个下标的,于是线性表的第i个元素是要存储在数组下标为i-1的位置,即数据元素的序号和存放它的数组下标之间存在对应关系:

      

      存储器中的每个存储单元都有自己的编号,这个编号称为地址。

      假设每个数据元素占用的都是c个存储单元,那么每个数据元素地址可通过公式计算得到:  LOC(ai)=LOC(a1)+(i-1)*c

      

      它的存取时间性能为O(1)。我们通常把具有这一特点的存储结构称为随机存取结构。

      相关操作

      1.  获得元素操作(GetElem)

       获取元素的思路:

       1)考虑边界问题,顺序线性表L已存在(非空表),并且i必须在1<=i<=ListLength(L)范围内,否则抛出异常

       2)将数值下标为i-1的值返回即可

       C语言的实现如下:

 1 // 获得元素操作
 2 #define OK 1
 3 #define ERROR 0
 4 #define TRUE 1
 5 #define FALSE 0
 6 typedef int Status;
 7 /*Status是函数的类型,其值是函数结果状态代码,如OK等*/
 8 /*初始条件:顺序线性表L已存在,1<=i<=ListLength(L)*/
 9 /*操作结果:用e返回L中第i个数据元素的值*/
10 
11 Status GetElem(SqList L, int i, ElemType *e)
12 {
13     if (L.length == 0 || i < 1 || i > L.length)
14         return ERROR;
15     *e = L.data[i-1];
16     return OK;
17 }

       PHP的实现如下:

 1 <?php
 2 class Seqlist{
 3 
 4     private $seq_list; //顺序表
 5     /**
 6      * 顺序表初始化
 7      *
 8      * @param mixed $seq_list
 9      * @return void
10      */
11     public function __construct($seq_list=array()){
12         $this->seq_list = $seq_list;
13     }
14 
15     /**
16      * 返回顺序表元素个数
17      *
18      * @return int
19      */
20     public function listLength(){
21         return count($this->seq_list);
22     }
23 
24     /**
25      * 返回顺序表中下标为i的元素值
26      *
27      * @param int i
28      * @return mixed 如找到返回元素值,否则返回false
29      */
30     public function getElem($i){
31         if ($this->seq_list && $i > 0 && $i <= $this->listLength()) {
32             return $this->seq_list[$i-1];
33         }else{
34             return false;
35         }
36     }
37 }

       2.  插入操作

       插入算法的思路:

       1)如果插入位置不合理(1<=i<=ListLength(L)+1),抛出异常

       说明:最好的情况就是,插入的位置为末尾:ListLength(L)+1(不是数组下标),这个时候不用移动元素,时间复杂度是O(1)

       2)如果线性表长度大于等于数组长度,则抛出异常或动态增加容量

       3)从最后一个元素开始向前遍历到第i个位置,分别将它们都向后移动一个位置

       4)将要插入元素填入位置 i 处

       5)表长加1

       C语言的实现如下:

 1 // 插入操作
 2 #define OK 1
 3 #define ERROR 0
 4 #define TRUE 1
 5 #define FALSE
 6 typedef int Status;
 7 /*初始条件:顺序线性表L已存在,1<=i<=ListLength(L)*/
 8 /*操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1*/
 9 
10 Status ListInsert(SqList *L, int i, ElemType e)
11 {
12     int k;
13     if (L->length == MAXSIZE) /*顺序线性表已经满*/
14         return ERROR;
15     if (i < 1 || i > L->length + 1) /*当i不在范围内时*/
16         return ERROR;
17 
18     if (i <= L->length) /*若插入数据位置不在表尾*/
19     {
20         for (k = L->length - 1; k >= i - 1; k--) /*将要插入位置后数据元素向后移动一位*/
21         {
22             L->data[k + 1] = L->data[k];
23         }
24     }
25     L->data[i - 1] = e; /*将新元素插入*/
26     L->length++;
27     return OK;
28 }

       PHP的实现如下:

 1 <?php
 2 class Seqlist{
 3 
 4     private $seq_list; //顺序表
 5     /**
 6      * 顺序表初始化
 7      *
 8      * @param mixed $seq_list
 9      * @return void
10      */
11     public function __construct($seq_list=array()){
12         $this->seq_list = $seq_list;
13     }
14 
15     /**
16      * 在指定位置 i 插入一个新元素 $value
17      *
18      * @param int $i
19      * @param mixed $value
20      * @return bool 插入成功返回 true, 否则返回 false
21      */
22     public function listInsert($i, $value){
23         // 三种情况:插入位置不合理
24         if ($i > $this->listLength()+1 || $i < 1) {
25             return false;
26         }elseif ($i == $this->listLength()+1) {
27             // 最好的情况:元素插入到最后一个位置,不需要移动元素,时间复杂度为O(1)
28             $this->seq_list[$i-1] = $value;
29         }else{
30             // 从 $i-1 到最后的元素位置向后移动一位
31             for ($k = $this->listLength()-1; $k >= $i-1; $k--) {
32                 $this->seq_list[$k+1] = $this->seq_list[$k];
33             }
34             $this->seq_list[$i-1] = $value;
35         }
36 
37         return true;
38     }
39 }

       这里有一个疑问,因为前面我们提到了:在任意时刻,线性表的长度应该小于数组的长度。

       在用C语言实现的版本中,我们用到了L->length == MAXSIZE去考虑可能会产生异常的情况,但是php版本实现中却没有相应代码的体现,为什么呢?我们知道php中的变量是不需要申请可以直接使用的。那这个地方数组的长度是多大,如何计算呢?

       其实,PHP中的所谓“数组”实际上并不是纯粹的数组!而是“哈希表”或者说是“字典”。PHP的数组本身不是由基础的数据结构构成,但是其内部实现方式应用到了大部分的线性表数据结构,等到后面再来重新回顾PHP数组的内部实现原理。

       3.  删除操作

       删除算法的思路:

       1)如果删除位置不合理,抛出异常

       2)取出删除元素

       3)从删除元素位置开始遍历到最后一个元素位置,分别将它们都向前移动一个位置

       4)表长减1

       C语言的实现如下:

 1 // 删除操作
 2 #define OK 1
 3 #define ERROR 0
 4 #define TRUE 1
 5 #define FALSE
 6 typedef int Status;
 7 /*初始条件:顺序线性表L已存在,1<=i<=ListLength(L)*/
 8 /*操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1*/
 9 Status ListDelete(SqList *L, int i, ElemType *e)
10 {
11     int k;
12     if (L->length == 0) /*线性表为空*/
13         return ERROR;
14     if (i < 1 || i > L->length) /*删除位置不正确*/
15         return ERROR;
16     *e = L->data[i - 1];
17     if (i < L->length) /*如果删除不是最后位置*/
18     {
19         for (k = i; k < L->length; k++)
20 
21             L->data[k - 1] = L->data[k];
22     }
23     L->length--;
24     return OK;
25 }

       PHP的实现如下:

 1 <?php
 2 class Seqlist{
 3 
 4     private $seq_list; //顺序表
 5     /**
 6      * 顺序表初始化
 7      *
 8      * @param mixed $seq_list
 9      * @return void
10      */
11     public function __construct($seq_list=array()){
12         $this->seq_list = $seq_list;
13     }
14 
15     /**
16      * 返回顺序表元素个数
17      *
18      * @return int
19      */
20     public function listLength(){
21         return count($this->seq_list);
22     }
23 
24     /**
25      * 删除顺序表中 i 位置的元素, 并用 $value 返回其值
26      *
27      * @param int $i
28      * @return mixed 删除成功返回 $value,否则返回 false
29      */
30     public function listDelete($i)
31     {
32         //两种情况:删除位置不合理
33         if ($i < 1 || $i > $this->listLength()) {
34             return false;
35         } else {
36             $value = $this->seq_list[$i - 1];
37             for ($k = $i - 1; $k < $this->listLength() - 1; $k++) {
38                 $this->seq_list[$k] = $this->seq_list[$k + 1];
39             }
40             unset($this->seq_list[$this->listLength() - 1]);
41 
42             return $value;
43         }
44     }
45 }

      线性表顺序存储结构的优缺点  

      1.  优点:

      1)无须为表示表中元素之间的逻辑关系而增加额外的存储空间

      2)可以快速地存取表中任一位置的元素

      2.  缺点:

      1)物理上相邻存储,不便于内存利用。例如一个容量为10的数组,需要内存为10字节,但是目前没有连续10个字节空余的内存空间,但是有很多不连续的小于10字节的内存空间,这样也没办法分配。造成存储空间的”碎片”。

      2)顺序表的容量很难确定。对于C语言而言,定义一个数组是需要指定数组大小的。这个大小就是为了方便底层用于申请连续内存空间。PHP源码中在初始化一个空数组的时候,也会先创建一个长度为16的arData数组,在需要扩容的时候再进行数组扩容。

      3)不便于插入和删除操作,需要移动整个顺序表元素

 

参考资料:程杰《大话数据结构》

posted @ 2021-02-24 18:35  欢乐豆123  阅读(1205)  评论(0编辑  收藏  举报