数据结构1,线性表基础-顺序表基本操作(1)

数据结构

​ 数据结构三要素——逻辑结构,物理(存储)结构,数据运算

​ <存储结构不同,运算的实现方式不同>

线性表

线性表的定义2.2.1

—数据结构三要素之逻辑结构

​ 定义(“逻辑结构”)和基本操作 (“运算”)

​ 线性表是具有相同的数据类型的n(n>=0)个数据元素有限 序列,其中n为表长,当n=0时线性表是一个空表。若用L命名线性表,则其一般表示为: L=(a1,a2,…,ai,ai+1,…,an) 角标从1开始

​ 元素所占的数据空间是一样大的 有限序列 有序序列

​ ai是线性表中的“第i个”元素,线性表中的位序(从1开始):用数组实现时需要注意

​ a1是表头元素;an是表尾元素

​ 除第一个元素外,每个元素有且仅有一个直接前驱;除最后一个元素外,每个元素有且仅有一个直接后继

线性表的基本操作 “运算”

带有&的参数:说明有修改需要带回
函数内是形参,不加引用符号修改不了(C++)

​ InitList(&L):初始化表。构造一个空的线性表L,分配内存空间。

​ DestroyList(&L):销毁操作。销毁线性表,并释放线性表L所占用的内存空间。 从无到有,

​ 从有到无。

​ ListInsert(&L,i,e):插入操作。在表L(线性表)中的第i个位置上插入指定元素e。

​ (函数名(线性表,位置,指定元素的值))

​ ListDelete(&L,i,&e):删除操作。删除表L中的第i个位置的元素,并用e返回删除元素的值。

​ (函数名(线性表,位置,指定元素的值)) 增删

​ LocateElem(L,e):按值查找操作。在表L中查找具有给定关键字值的元素。

​ (可以给定一个元素e的值在线性表在L中查找,线性表中有没有一个数据元素和e的值相同)

​ GetElem(L,i):按位查找操作。获取表L中第i个位置的元素的值。

​ (传入一个参数i,i指明了想要查找的线性表中的第i个元素) 、查(”改“之前也要“查”)

其他常用操作:

​ Length(L):求表长。返回线性表L的长度,即L中数据元素的个数。

​ PrintList(L):输出操作。按前后顺序输出线性表L的所有元素值。

​ Empty(L):判空操作。若L为空表,则返回true,否则返回false。

Tips:

  1. 对数据的操作(记忆思路)——创建,销毁,增删改查
  2. C语言函数的定义—— <返回值类型> 函数名(<参数1类型> 参数1,<参数2类型> 参数2,…)
  3. 实际开发中,可根据实际需求定义其他的基本操作
  4. 函数名和参数的形式、命名都可改变。(key:命名要有可读性)
  5. 什么时候要传入参数的引用“&”——对参数的修改结果需要“带回来

为什么要实现对数据结构的基本操作?

  1. 团队合作编程,你定义的数据结构要让0别人能够很方便的使用(封装)
  2. 将常用的操作/运算封装成函数,避免重复工作,降低出错风险

顺序表

顺序表的定义2.2.2

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

​ 线性表L逻辑结构 a1-a2-a3-4-a5

线性表是具有相同数据类型的n(n>=0)个数据元素的有限 序列(每个数据元素所占空间一样大)

内存
LOC(L) 设线性表第一个元素的存放位置是LOC(L) LOC是location的缩写
a1 存放位置=LOC(L)+数据元素的大小
a2 存放位置=LOC(L)+2*数据元素的大小
a3 存放位置=LOC(L)+3*数据元素的大小
a4
a5

如何知道一个数据元素大小? C语言中sizeof(Elem Type)

ElemType 就是你的顺序表中存放的数据元素类型

eg:sizeof(int) = 4B

eg: typedef struct{

​ int num;

​ int people;

​ }Customer;

sizeof(int) = 4B

sizeof(Customer) = 8B

顺序表的实现——静态分配

#define MaxSize 10        //定义最大长度
typedef struct{
    ElemType data[MaxSize];   //用静态的“数组”存放数据元素
    //ElemType填写具体数据类型
    int length;              //顺序表的当前长度
}SqList;                     //顺序表的类型定义(静态分配方式)     Sq:sequence --- 顺序,序列

Q:如果“数组”存满了怎么办?

A:可以放弃治疗,顺序表的表长刚开始确定后就无法更改(存储空间是静态的)

如果刚开始就声明一个很大的内存空间,会很浪费。

给各个数据元素分配连续的存储空间,大小为MaxSize*sizeof(ElemType)

eg:

#include <studio.h>
#define MaxSize 10    //定义最大长度
typedef struct{
    int data[MaxSize];	//用静态“数组”存放数据元素
    int length;		//顺序表的当前长度
}SqList;			//顺序表的类型定义
/*
本例中数据原数类型(ElemType)是int

*/
//基本操作——初始化一个顺序表
void InutList(SqList &L){
    for(int i=0; i<MaxSize; i++)//可以去掉
        L.data[i] = 0;	//将所有数据元素设置为默认初始化值
    /*
    2.执行for  把各个数据元素的值设为默认值(可省略)
    */
    L.length=0;		//顺序表初始化长度为0
    /*
    3. 将length的值设为0
    */
}

int main(){
    SqList L;	//声明一个顺序表     
    /*
    1. 在内存中分配存储顺序表L的空间。包括:MaxSize*sizeof(ElemType)和存储length的空间
    */
    InitList(L);//初始化顺序表
    //尝试“违规”打印整个data数组    错误
    for(int i=0 ; i<MaxSize   (i<L.length这种访问也不够好,更好的做法是使用基本操作来访问各个数据元素);i++)
        printf("data[%d]=%d\n",i,L.data[i]);//内存中会有遗留的“脏数据”
    return 0;
}

顺序表的实现——动态分配

#define InitSize 10       //顺序表的初始长度
typedef struct{
    ElemType *data;		//指示动态分配数组的指针
    int MaxSize;		//顺序表的最大容量
    int length;		//顺序表的当前长度
}SeqList;			//顺序表的类型定义(动态分配方式)

Key: 动态申请和释放内存空间

C——malloc、free函数

​ 申请、 释放

​ L.data = *(ElemType ) malloc (sizeof(ElemType) * InitSize);

​ 申请一整片连续的内存空间,这内存空间有起始地址,在malloc执行结束后会返回一个指向这一片内存空间开始地址的指针 (malloc 函数返回一个指针,需要强制转型为你定义**(ElemType *)**的数据元素类型指针) InitSize是数组的初始长度

C++ ——new、delete关键字

#include <stdlib.h>		//malloc、free函数的头文件
#define IntSize 10 //默认的最大长度
typedef struct{
    int *data; 		//指示动态分配数组的指针
    int MaxSize;	//顺序表的组大容量
    int length;		//顺序表的当前长度
}SeqList;

//函数 InitList 用于初始化一个动态分配的顺序表
void InitList(SeqList &L){
    //用malloc函数申请一片连续的存储空间
    L.data = (int *)malloc(InitSize*sizeof(int));
    L.length = 0;
    L.MaxSize = InitSize;
}

//增加动态数组的长度
void IncreaseSize(Seqlist &L,int len){
    int *p = L.data;
    L.data=(int *)mallloc((L.MaxSize+len)*sizeof(int));
    for(int i=0;i<L.length; i++){
        L.data[i] = p[i];	//将数据复制到新区域    时间开销大!
    }
    L.MaxSize = L.MaxSize+len;	//顺序表最大长度增加 len
    free(p);	//释放原来的内存空间
}
/*
	注:realloc函数也可实现IncreaseSize函数,但建议初学者使用malloc和free更能理解背后的过程
*/
int main(){
    SeqList L;		//声明一个顺序表
    InitList(L);	//初始化顺序表
    //...往顺序表中随便插入几个元素...
    IncreaseSize(L,5);
    return 0;
}

顺序表的特点

  1. 随机访问,即可以在O(1)时间内找到第i个元素。
  2. 存储密度高,每个结点只存储数据元素 本身 (链式存储,需要在存储元素本身时存储元素位置指针)
  3. 拓展容量不方便(即便采用动态分配的方式实现,拓展超长度的时间复杂度也比较高)

顺序表的插入和删除2.2.3

插入:

​ 用存储位置的相邻来体现数据元素之间的逻辑关系。

ListInsert(&L,i,e):插入操作。在表L(线性表)中的第i个位置上插入指定元素e。

​ (函数名(线性表,位置,指定元素的值))

#define MaxSize 10//定义最大长度
typedef struct{
    ElemType data[MaxSize];//用静态的"数组"存放数据元素
    int length;//顺序表的当前超长度
}SqList;//顺序表的类型定义
//注:该代码简历在顺序表的“静态分配”实现方式之上,“动态分配”也雷同
//1
bool ListInsert(SqList &L,int i ,int e){
    if(i<1||i>L.length+1)   	//判断i的范围是否有效
        return false;
    if(L.length>=MaxSize)		//当前存储空间已满,不能插入
        return false;
    for(int j=L.length;j>=i;j--)	//将第i个元素及之后的元素后移
        L.data[j]=L.data[j-1];
    L.data[i-1]=e;				//在位置i处存放e
    L.length++;				//长度+1
    return true;
}//好的算法,应该具有“健壮性”。能处理异常情况,并给使用者反馈
/*
关注最深层循环语句的执行次数与问题规模n的关系   问题规模 n = L.length 表长
最好情况:新元素插入到表尾,不需要移动元素
		i= n+1,循环0次;最好时间复杂度 = O(1)
最坏情况:新元素插入到表头,需要将原有的n个元素全部向后移动
		i= 1,循环n次;最坏时间复杂度= O(n);
平均情况:假设新元素插入到任何一个位置的概率相同,即i=1,2,3,...,length+1的概率都是p=1/n+1
	i=1,循环n次;i=2,循环n-1次;i=3,循环n-2次...i=n+1时,循环0次
	平均循环次数 = np + (n-1)p+(n-2)p+.....+1*p = n(n+1)/2*1/n+1=n/2
	
	所以平均时间为O(n)
*/
//2
void ListInsert(SqList &L,int i ,int e){//i的合法值为  【1,length+1】
    for(int j=L.length;j>=i,j--)//将第i个元素及之后的元素后移
        L.data[j]=L.data[j-1];
    L.data[i-1]=e;		//在位置i处放e
    L.length++;		//长度+1
}
/*
注意位序、数组下标的关系,并从后面的元素依次移动
基操——让自己实现的数据结构可以让别人很方便的使用
*/
int main(){
    SqList L;  //声明一个顺序表
    InitList(L);	//初始化顺序表
    //...		此处省略一些代码,插入几个元素
    ListInsert(L,3,3);
    return 0;
删除:

​ ListDelete(&L,i,&e):删除操作。删除表L中的第i个位置的元素,并用e返回删除元素的值。

​ (函数名(线性表,位置,指定元素的值)) 增删

bool ListDelete(SqList &L,int i ,int &e){
    if(i<||i>L.length)		//判断i的范围是否有效
        return false;
    for(int j=i;j<L.length;j++)		//将被删除的元素赋值给e
        L.data[j-1]=L.data[j];	//将第i个位置后的元素前移
    L.length--;			//线性表长度-1
    return true;		
}

int main(){
    SqList L; 	//声明一个顺序表
    InitList(L);	//初始化顺序表
    int e = -1;	//用变量e把删除的元素“带”回来
    if(ListDelete(L,3,e))
        printf("已删除第3个元素,删除元素值为=%d\n",e);
    else
        printf("位序i不合法,删除失败\n");
    return 0;
}
/*
最好情况:删除表尾元素,不需要移动其他元素
	i=n,循环0次;最好时间复杂度=O(1)
最坏情况:删除表头元素,需要将后续的n-1个元素全部向前移动
	i=1,循环n-1次;最坏时间复杂度=O(n)
平均情况:假设删除任何一个元素的概率相同,即i=1,2,3,...,length的概率都是p=1/n
	i=1,循环n-1次;i=2时,循环n-2次;i=3,循环n-3次...i=n时,循环0次。
	平均循环次数=n-1/2   平均时间复杂度=O(n)
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mi蜜桃

谢谢打赏,我会持续更新的哦~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值