本文转载自微信公众号「前端万有引力」,作者一川。转载本文请联系前端万有引力公众号。
写在前面
对于开发而言,数组是我们经常打交道的数据类型,那么它的内部存储结构是怎样的呢?我们将进行详细介绍。
线性表
线性表是最基本、最简单,也是最常用的一种数据结构,线性表就是n个具有相同特性的数据元素的有序数列。
前驱元素:若A元素在B元素的前面,则称A为B的前驱元素。
后驱元素:若B元素在A元素的后面,则称B为A的后驱元素。
线性表的分类:线性表中数据存储的方式是分为顺序存储和链式存储,其中:
- 顺序存储是用一段地址连续的存储单元依次存储线性表的数据元素。
- 链式存储是用一段地址不连续的存储单元存储数据,而使用结点进行连接查询地址。
随机访问:由于线性表存储在连续的内存位置,因此可以通过它们的索引来计算内存地址,以便随机访问数据。
顺序表
顺序表的存储方式其实就是在内存中找到空位置后进行占位,然后将数据元素依次进行存放到空位置。
是不是听起来有点像数组的结构,对的,数据就是一种线性表结构。因此我们可以使用数组进行表示顺序表,线性表在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素间逻辑上的相邻关系。
数组的长度是存放线性表的存储空间的长度,分配存储后这个量一般是不变的。线性表的长度是线性表中数据元素的个数,随着线性表中插入和删除操作的进行,这个量是变化的。切记,在任何时刻线性表的长度应该小于等于数组的长度。
- class SequenceList{
- constructor(){
- // 存储元素的数组
- this.elemList = [];
- // 当前顺序表中的元素个数
- this.elemLength = 0;
- }
- // 将一个线性表置为空表
- clear(){
- this.elemLength = 0;
- }
- // 判断当前线性表是否为空表
- isEmpty(){
- return this.elemLength === 0;
- }
- // 获取当前线性表的长度
- length(){
- return this.elemLength;
- }
- // 向线性表中添加元素
- insert(elem){
- this.elemList[this.elemLength++] = elem;
- }
- // 在第i个元素插入元素elem
- insertIndex(i,elem){
- // 先将i索引位置的元素及其后面位置的元素依次向后移动一位
- for(let index = this.elemLength - 1; index > i; index--){
- this.elemList[index] = this.elemList[index-1];
- }
- // 把elem元素存放在i索引位置
- this.elemList[i] = elem;
- }
- // 删除指定位置i处的元素后,返回该元素
- remove(i){
- // 记录索引i处的值
- let current = this.elemList[i];
- // 索引i后面元素依次向前移动一位即可
- for(let index = i; index < this.elemList - 1; index++){
- this.elemList[index] = this.elemList[index+1];
- }
- // 元素个数-1
- this.elemLength--;
- return current;
- }
- // 查找elem元素首次出现在顺序表的位置
- indexOf(elem){
- for(let i = 0; i< this.elemLength; i++){
- if(this.elemList[i] === elem){
- return i;
- }
- return -1;
- }
- }
- }
数组的优缺点
优点:
- 在内存空间的利用率高
- 查询元素效率高,通过元素下标进行存取
缺点:
- 插入和删除元素效率低。(在数组中间插入或删除元素时,需要先移动遍历整个表才能操作元素并重新排序)
- 有空间长度限制,不能扩增线性表长度。(当存取元素长度大于顺序表的元素个数时,会出现“溢出”;当元素长度小于预先分配的空间时,空间浪费严重)
小结
本篇文章是《懂点算法》系列的第二篇文章,主要讲述的线性表的第一类顺序存储,以及介绍了如何实现顺序存储。