目录
前言
本章主要介绍顺序表的基本形式、结构和两种基本实现方式。
0 铺垫
内存的基本单位是1个字节来作为单位的;1个字节是由8位二进制码组成的1个储存单元;计算机在去标识的时候,也是按照1个存储单元去进行标识的。那么我们保存数据也就是需要用到多个存储单元来放到一起。
例如,对于整型int数据,需要申请多少个存储单元才能把这个数存起来?这个是我们目前需要解决的问题。那么对于正常的32位基本整型来说,每个数据需要占用4个字节。那么储存形式(以整数1为例)如下图:
同理以char类型为例(python没有),这个char如何理解?就理解为字符串里的1个字符;字符串是一堆字符,char可以理解为1个字符,那么1个字符会占用1个字节,也就是1个储存单元。那么这个时候的话,我们就会想了:如果储存类型不同,如整型1次占用4个字节,而char占用1个字节,那么它们也就决定了储存空间所占用的大小是不一样的。
第二点我们从“取”的角度来考虑,如果我们在“存”的时候就告诉它了,01,02,03,04这四个单元对应的是实际上是整数,那么我们再取的时候就是以整体来去进行取的。那么如果在“存”的时候,我告诉你这实际上不是整数,而是4个char,那么取出来的时候,就不能把他们当做整体来看待,而应该以4个单char来分别进行“取”。
理解完之后,我们再来思考:如上述的int = 1,2,3这3个整数来如何来去存呢?那么为了便于理解,我们就把刚才所举1个整数例子(表示数据1的图)当做1个块。如下图:
现在存储单元1,2,3是没有任何关联的,但事实上1,2,3是存在某种关联的,即它们都是整数数据。那么我们就连续的用地址存,如下图所示:
1 顺序表
在程序中,经常需要将一组(通常是同为某个类型的)数据元素作为整体管理和使用,需要创建这种元素组,用变量记录它们,传进传出函数等。一组数据中包含的元素个数可能发生变化 (可以增加或删除元素)。
对于这种需求,最简单的解决方案便是将这样一组元素看成一个序列,用元素在序列里的位置和顺序,表示实际应用中的某种有意义的信息,或者表示数据之间的某种关系。
这样的一组序列元素的组织形式,我们可以将其抽象为线性表。一个线性表是某类元索的一个集合,还记录着元素之间的一种顺序关系。线性表是最基本的数据结构之一,在实际程序中应用非常广泛,它还经常被用作更复杂的数据结构的实现基础。
1.1 函数表的基本形式
根据线性表的实际存储方式,分为两种实现模型:
顺序表,将元素顺序地存放在一块连续的存储区里,元素间的顺序关系由它们的存储顺序自然表示。
链表,将元素存放在通过链接构造起来的一系列存储块中。
图a表示的是顺序表的基本形式,数据元素本身连续存储,每个元素所占的存储单元大小固定相同,元素的下标是其逻辑地址,而元素存储的物理地址(实际内存地址)可以通过存储区的起始地址Lo c(eo)加上逻辑地址(第i个元素)与存储单元大小(c)的乘积计算而得。比如,我们要存下列表中的数据:
如果起始地址是0x23,那么后边因为每一个整型数据占用4个字节,所以后边依次是0x27,0x31,0x35。此时,也就相当于Li相当于存的是0x23地址。
下面,我们开始读取数据,如果要读取Li[0]时,也就是从起始地址0x23开始读4个字节;如果是Li[3]的话,那么就是Li[3]=0x23+3*4Byte。
故,访问指定元素时无需从头遍历,通过计算便可获得对应地址,其时间复杂度为O(1)。
以上是顺序表的基本布局,那么下面介绍一种元素外置的顺序表。
如果列表不一定全是整型,而是整型+字符串。
4Byte=32
整数"12"占4个字节,"ab"占2个。那么现在存储的基本单元的大小已经不同一了,不同一就没有办法按照顺序来存了。怎么办呢?只能保证存储的每一个数据的大小都是相同的。实际上存在不同类型数据的时候,所占用的存储单元是不一样的。
在这里,比如要取li[0],首先拿到的是0x324,然后找到0x324里面存储的数据是0x100,然后顺着0x100找到这个存储位置,然后就找到了12这个数据;同理,如果取到li[2]也是一样的。
2 顺序表的结构与实现
2.1 顺序表的结构
一个顺序表的完整信息包括两部分,一部分是表中的元素集合,另一部分是为实现正确操作而需记录的信息,即有关表的整体情况的信息,这部分信息主要包括元素存储区的容量和当前表中已有的元素个数两项。
也就是一开始需要预估列表的容量(不能超),另一个是目前存储的数据个数。
那么,我们现在知道了构造的时候需要表头和数据区两部分,那么表头和数据又该怎么组合到一起呢?我们看2.2节。
2.2 顺序表的两种基本实现方式
a) 图a为一体式结构,存储表信息的单元与元素存储区以连续的方式安排在一块存储区里,两部分数据的整体形成一个完整的顺序表对象。一体式结构整体性强,易于管理。但是由于数据元素存储区域是表对象的一部分,顺序表创建后,元素存储区就固定了。
图b为分离式结构,表对象里只保存与整个表有关的信息(即容量和元素个数),实际数据元素存放在另一个独立的元素存储区里,通过链接与基本表对象关联。表头信息去找一块存储单元储存起来,除了存储存容量和占用大小之外,这个表头还会存在第3个单元,也就是地址单元,他去指向真实的数据区。
2.3 元素存储区替换
一体式结构由于顺序表信息区与数据区连续存储在一起,所以若想更换数据区,则只能整体搬迁,即整个顺序表对象(指存储顺序表的结构信息的区域)改变了。
分离式结构若想更换数据区,只需将表信息区中的数据区链接地址更新即可,而该顺序表对象不变
2.4 元素存储区扩充
采用分离式结构的顺序表,若将数据区更换为存储空间更大的区域,则可以在不改变表对象的前提下对其数据存储区进行了扩充,所有使用这个表的地方都不必修改。只要程序的运行环境(计算机系统)还有空闲存储,这种表结构就不会因为满了而导致操作无法进行。人们把采用这种技术实现的顺序表称为动态顺序表因为其容量可以在使用中动态变化。
扩充的两种策略:
每次扩充增加固定数目的存储位置,如每次扩充增加10个元素位置,这种策略可称为线性增长。
特点:节省空间,但是扩充操作频繁,操作次数多。
每次扩充容量加倍,如每次扩充增加一倍存储空间。
特点:减少了扩充操作的执行次数,但可能会浪费空间资源。以空间换时间,推荐的方式。
总结
本章节主要介绍了顺序表基本形式、结构和基本实现方式。