2.2 线性表的顺序表示

2.2 线性表的顺序表示

2.2.1 顺序表的定义

  1. 顺序表:用顺序存储的方式实现线性表顺序存储,把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现。

  2. 顺序表的特点:

    a. 表中元素的逻辑顺序与物理顺序相同
    b. 表中元素可以随机存取,通过首地址和元素序号可在时间O(1)内找到指定的元素

    设线性表L的存储的起始位置为LOC(A),即第一个元素的访问位置为LOC(A)
    sizeof(Elemtype)是每个数据元素所占用存储空间大小,则第二个元素的位置为LOC(A)+sizeof(ElemType)
    第i个元素的位置为LOC(A)+(i-1)*sizeof(ElemType)
    

    c. 存储密度高,每个结点只存储数据元素
    d. 拓展容量不方便(即便采用动态分配的方式实现,拓展长度的时间复杂度也比较高)

  3. 优点:随机存取,存储密度高

  4. 缺点:要求大片连续空间,不易改变容量

  5. 顺序表存储类型描述:
    a. 静态分配:数组大小和空间事先固定,一旦空间占满,再加入新的数据就会产生溢出,从而使程序崩溃

     ```c
     #define MaxSize 100   //定义最大长度
     typedef struct{
     	ElemType data[MaxSize];    // 存放数据元素的数组
     	int length;   //当前元素个数
     }SqList;
     
     // 基本操作--初始化一个顺序表
     void InitList(&L){
     	for(int i=0;i<MaxSIze;i++){
     		// 将所有数据元素初始化为0
     		L.data[i]=0;
     	}
     
     	// 表示L还是一个空表
     	L.length=0;
     }
     
     int main(void){
     	SqList L;
     	InitList(L);
     
     	// 遍历数组时,限制条件应该使用length而不是MaxSize
     	// MaxSize是最大存储空间,length才是当前顺序表的长度,即已存储的元素个数
     	for(int i=0;i<L.length;i++){
     		//...后续操作
     	}	
     
     	return 0;
     }
     ```
    

    b. 动态分配:存储数组的空间是在程序执行过程中通过动态分配语句分配到,一旦数据空间占满,就另外开辟一块更大的空间来替换原来的存储空间,达到扩充存储数组空间的目的

    #inlcude<stdlib.h>   //malloc,free需要
    #define InitSize 100
    typedef struct{
    	ElemType *data;
    	int MaxSize,length;    //数组最大容量和当前元素个数
    }SeqList;
    
    // C初始动态分配语句
    L.data=(ElemType* )malloc(sizeof(ElemType)*InitSize);
    // C++ 分配空间
    // L.data=new ElemType[InitSIze];
    
    // 基本操作--增加动态数组长度
    void IncreaseSize(SeqList &L, int len){
    	int *p=L.data;
    	L.data=(ElemType*) malloc(sizeof(ElemType)*(L.MaxSize+len));
    	for(int i=0;i<L.length;i++){
    		L.data[i]=p[i];
    	}
    	L.MaxSize=L.MaxSize+len;
    	free(p);   //记得释放1掉原来的空间
    }
    
    int main(void){
    	SeqList L;
    	InitList(L);
    
    	IncreaseSize(L, 5);				
    
    	return 0;
    }
    

动态分配依然是顺序存储结构,只是分配空间可以在运行时动态决定

顺序输出所有元素时,顺序表和线性表时间复杂度相同

2.2.2 顺序表上基本操作的实现

  1. 插入操作

    /*
    * 1. 在顺序表L的第i个位置插入元素e
    * 2. 判断i是否合法,i>=1 && i<=length+1, length<=MaxSize
    * 3. 将第i个位置及其后面的所有元素依次向后移动一个位置
    * 4. 将e插入到第i个位置,顺序表长度+1
    * 5. 插入成功,返回true
    */
    
    bool ListInsert(SqList &L, int i, ElemType e){
    if(i<1 || i>L.length+1 || L.length>=<Maxsize){
       return false;
    }
    
    for(int i=L.length; i>=i;i--){
       L.data[i]=L.data[i-1];
    }
    L.data[i-1]=e;
    L.length++;
    
    return true;
    }
    

    a. 最好情况:在表尾插入(i=n+1),元素移动语句不执行,时间复杂度为O(1)
    b. 最坏情况:在表头插入(i=1),元素后移语句执行n次,时间复杂度为O(n)
    c. 平均情况:在每个位置插入元素的概率相等,即 p i = 1 n + 1 p_i=\frac{1}{n+1} pi=n+11,所需移动的平均次数为

    ∑ i = 1 n + 1 p i ∗ ( n − i + 1 ) = ∑ i = 1 n + 1 1 n + 1 ∗ ( n − i + 1 ) = 1 n + 1 ∑ i = 1 n + 1 ( n − i + 1 ) = 1 n + 1 ∗ n ∗ ( n + 1 ) 2 = n 2 \sum_{i=1}^{n+1}p_i*(n-i+1)= \sum_{i=1}^{n+1} \frac{1}{n+1}*(n-i+1)\\=\frac{1}{n+1} \sum_{i=1}^{n+1}(n-i+1)\\=\frac{1}{n+1}*\frac{n*(n+1)}{2}=\frac{n}{2} i=1n+1pi(ni+1)=i=1n+1n+11(ni+1)=n+11i=1n+1(ni+1)=n+112n(n+1)=2n

  2. 删除操作

     /*
    * 1. 删除顺序表L中的第i个元素,并用引用变量e返回删除的值
    * 2. 判断i是否合法: 1<=i<=L.length
     * 3. 将被删除元素赋给引用变量e
     * 4. 将第i个元素后面的元素依次向前移动一个位置
     * 5. 删除成功返回true
    */
    
    bool ListDelete(SqList &L, int i, ElemType &e){
    	if(i<1 && i>L.length){
    		return false;
    	}
    
     	e=L.data[i-1];
    	for(int j=i; j<L.length;j++){
    		L.data[j-1]=L.data[j];
    	}
    
    	L.length-;
    	return true;
    }
    
    

    a. 最好情况:删除表尾1元素,元素移动语句执行次数为0,O(1)
    b. 最坏情况:删除表头元素,元素移动语句执行n-1次,O(n)
    c. 平均情况:每个位置上的元素被删除的元素相等: p i = 1 n p_i=\frac{1}{n} pi=n1,平均时间复杂度为:

    ∑ i = 1 n p i ∗ ( n − 1 ) = ∑ i = 1 n 1 n ∗ ( n − i ) = 1 n ∑ i = 1 n n − i = 1 n ∗ n ∗ ( n − 1 ) 2 = n − 1 2 \sum_{i=1}^{n}p_i*(n-1)=\sum_{i=1}^{n}\frac{1}{n}*(n-i)\\=\frac{1}{n}\sum_{i=1}^{n}n-i\\=\frac{1}{n}*\frac{n*(n-1)}{2}\\=\frac{n-1}{2} i=1npi(n1)=i=1nn1(ni)=n1i=1nni=n12n(n1)=2n1

  3. 查找操作

    /*
     * 1. 获取顺序表L的元素值等于给定值的元素的位置
    * 2. 判断e是否合法
    * 3. 匹配
    * 4. 返回找到的值的下标+1
    * 5. 失败则返回0
     */
    	
    int LocateElem(SqList L, ElemType e){
    	for(int i=0;i<L.length;i++){
    		f(L.data[i]==e){
    			retrn i+1;
    		}
    	}
    	
    	return 0;
    }
    

    a. 最好情况:查找元素就是表头元素,比较函数执行1次,O(1)
    b. 最差情况:查找元素就是表尾元素,比较函数执行次数为n,O(n)
    c. 平均情况:被查找的元素在每个位置上的概率相等: p i = 1 n p_i=\frac{1}{n} pi=n1,平均时间复杂度为:

    ∑ i = 1 n p i ∗ i = ∑ i = 1 n 1 n ∗ i = 1 n ∑ i = 1 n i = 1 n ∗ n ∗ ( n + 1 ) 2 = n + 1 2 \sum_{i=1}^{n}p_i*i=\sum_{i=1}^{n}\frac{1}{n}*i=\frac{1}{n}\sum_{i=1}^{n}i=\frac{1}{n}*\frac{n*(n+1)}{2}=\frac{n+1}{2} i=1npii=i=1nn1i=n1i=1ni=n12n(n+1)=2n+1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值