《数据结构》—— 线性表(上)

本文内容,参考自《大话数据结构》(程杰著) ,一部分自己修改,如:把C语言换成了Java语言。写作目的,意在加强记忆。

本文写作工具,使用 Typora ,Markdown 大法好。不过,和掘金提供的,有一些不兼容。

 1.线性表的定义

线性表(List):零个或多个数据元素的有限序列。

这里需要强调几个关键的地方。

首先它是一个序列。也就是说,元素之间是有顺序的,若元素存在多个,则第一个元素无前驱,最后一个元素无后续,其他每个元素都有且只有一个前驱或后继。

然后,线性表强调是有限的,元素个数是有限的。事实上,在计算机中处理的对象都是有限的,那种无限的数列,只存在于数学的概念中。

若将线性表记为 (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时,有且仅有一个直接前驱。

所以线性表元素的个数n(n ≧ 0)定义为线性表的长度,当 n=0 时,称为空表。

在非空表中的每个数据元素都有一个确定的位置,如 a1 是第一个数据元素,an 是最后一个数据元素, ai 是第 i 个数据元素,称 i 为数据元素 ai 在线性表中的位序。

比如,一年里的十二个月,是不是线性表呢?


当然是,1月开始,12月结束,当中的月份都有前驱和后继,而且一共只有十二个,完全符合线性表的定义。

2.线性表的顺序存储结构

我们来看看线性表的两种物理结构的第一种——顺序存储结构。

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

说白了,就是在内存中找了块地,通过占位的形式,把一定内存空间给占了,然后把相同数据类型的数据元素依次存放。线性表的每个数据元素,类型都相同,所以可以用数组来实现顺序存储结构,把第一个数据元素存到数组下标为 0 的位置,接着把线性表相邻的元素存储在数组中相邻的位置。

下面我们以数组实现的顺序存储结构,来说明一下,线性表顺序存储结构的读取、插入和删除。

public class Demo {
​
    private final int maxSize = 20;//存储空间初始化分配量
    private int length;//当前长度
    private int[] arr;//数组
    
    public void Demo() {
        this.arr = new int[maxSize];
    }
    
    //获取线性表的长度
    public int length() {
        return length;
    }
    
    //读取线性表第 index 个元素
    public int get(int index) {
        if(length==0 || index<0 || index>length-1) {
            throw new IndexOutOfBoundsException();
        }
        int i = arr[index];
        return i;
    }
  
    //存储元素
    public void add(int item) {
        if(length==maxSize) {
            throw new IndexOutOfBoundsException();
            //线性表已经满了
        }
        arr[length] = item;
        length++;
    }
    
    //插入元素思路:
    //如果插入的位置不合理,抛出异常
    //如果线性表长度大于等于数组长度,则抛出异常或动态增加容量(可参考ArrayList源码)
    //从最后一个元素开始向前遍历到第i个位置,分别将它们都向后移动一个位置
    //将要插入元素填入位置i处
    //表长加1
    public boolean insert(int index,int key) {
        if(length==maxSize) {
            throw new IndexOutOfBoundsException();
            //线性表已经满了
        }
        if(index<0 || index>length) {
            throw new IndexOutOfBoundsException();
            //index 不在范围内,这里是因为顺序存储结构,元素之间不能留空
        }
        if(index<=length-1) {
            //插入的元素,不在表最后
            //需要把插入位置之后的元素,向后移动一位
            for(int k=length-1;k>=index;k--) {
                arr[k+1] = arr[k];
            }
        }
        arr[index] = key;
        length++;
        
        return true;
    }
    
    //删除元素思路:
    //如果删除位置不合理,抛出异常
    //从删除元素位置开始遍历到最后一个元素位置,分别将它们都向前移动一个位置
    //表长减1
    public boolean delete(int index) {
        if(length==0) {
            throw new IndexOutOfBoundsException();//线性表为空
        }
        if(index<0 || index>length) {
            throw new IndexOutOfBoundsException();
            //index 不在范围内
        }
        if(index < length-1) {//不是删除最后的位置
            for(int k=index;k < length-1;k++) {
                arr[k-1] = arr[k];
            }
        }
        length--;
        return true;
    }
    
}复制代码

根据上面的代码,可以得出:

线性表的顺序存储结构,在存、读数据时,不管是哪个位置,时间复杂度都是 O(1)。

而插入或删除时,如果元素要插入到最后一个位置,或者删除最后一个元素,此时时间复杂度为 O(1),因为不需要移动元素。如果元素要插入到第一个位置或删除第一个元素,那此时的时间复杂度为 O(n)。根据概率原理,每个位置插入或删除元素的可能性是相同的,也就说位置靠前,移动次数多,位置靠后,移动次数少。最终平均移动次数和最中间的那个元素的移动次数相等,为 (n-1)/2。根据大O阶推导方法,可以得出,平均时间复杂度为O(n)。

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

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



你可能感兴趣的:(《数据结构》—— 线性表(上))