• 数据结构学习笔记(第二章线性表)


    目录

    第二章 线性表

    1.定义:

    2.线性表的基本操作

    3.顺序表

    顺序表的基本操作

    4.链表

    单链表:

    单链表基本操作

     双链表

     循环链表

     静态链表

    第二章 线性表

    1.定义:

    线性表是具有相同数据类型的n(n>=0)个数据元素的有限序列,其中n为表长,当n=0时线性表是一个空表。若用L命名线性表,则其一般表示为

    L=(a1,a2,.....an)

    a1是表头元素,an是表尾元素。除a1外都有前驱,除an外都有后继

    线性表是一种逻辑结构,表示元素之间一对一的相邻关系。

    线性表包括顺序表和链表,顺序表里面元素的地址是连续的。链表里面节点的地址不一定连续的,是通过指针连起来的。顺序表和链表是指存储结构。

    2.线性表的基本操作

    InitList(&L):初始化线性表

    Length(L):求表长

    LocateElem(L,e):按值查找操作

    GetElem(L,i):按位查找操作

    ListInsert(&L,i,e):插入操作

    ListDelete(&L,i,&e):删除操作

    PrintList(L):输出操作

    Empty(L): 判空操作

    DestroyList(&L):销毁操作

    注意:&表示c语言中的引用调用,在c语言中用指针也可达到同样效果。

    请参考

    c语言 引用调用_C中按值调用和按引用调用之间的区别_culing2941的博客-CSDN博客

    3.顺序表

    线性表的顺序存储又称顺序表,逻辑地址上相邻的地址元素,物理地址也相邻。

    特点:表中元素逻辑顺序与物理顺序相同,用数组实现的,一维数组可以是静态的,也可以是动态的。

    1. 静态分配
    2. #define MaxSize 50//定义线性表最大长度
    3. typedef struct{
    4. ElemType data[MaxSize];//顺序表元素
    5. int length;//顺序表当前长度
    6. }SqList//顺序表类型定义
    7. 动态分配
    8. #define InitSize 100//初始化表长度
    9. typedef struct{
    10. ElemType *data;//指示动态分配数组的指针
    11. int MaxSize,length;//数组最大容量和当前个数
    12. }SeqList//类型定义
    13. 动态分配初始化语句:
    14. c: L.data=(ElemType*)malloc(sizeof(ElemType)*InitSize);
    15. c++: L.data=new ElemType[InitSize];
    16. 注:动态分配不是链式存储,他是顺序存储结构,物理结构并没有发生变化,依然是随机存取方式,只是分配的空间大小可以在运行时动态决定。

    注:线性表从1开始,而数组从0开始。

    优点:随机访问特性,查找O(1)时间,存储密度高;逻辑上相邻的元素,物理上也相邻;

    缺点:插入删除需移动大量元素。

    顺序表的基本操作

    插入操作:

    最好情况:表尾插入,时间复杂度o(1)

    最坏情况:表头插入,时间复杂度o(n)

    平均情况:o(n/2)=o(n)

    删除操作: 

    最好情况:表尾删除,时间复杂度o(1)

    最坏情况:表头删除,时间复杂度o(n)

    平均情况:o(n/2)=o(n)

    查找操作:

    1. int LocateElem(SqList L,ElemType e){
    2. int i;
    3. for(i=0;i<L.length;i++{
    4. if(L.data[i]==e)
    5. return i+i;//返回其位序
    6. return 0;//查找失败
    7. }

    最好情况:表头查找,时间复杂度o(1)

    最坏情况:表尾查找或查找失败,时间复杂度o(n)

    平均情况:o(n/2)=o(n)

    4.链表

    与顺序存储相比,允许存储空间不连续,插入删除时不需要移动大量的元素,只需修改指针即可,但查找某个元素,只能从头遍历整个链表。且不能随机存取

    单链表:

    定义:

     单链表分为带头结点和不带头结点两种,不管有没有头结点,头指针都指向链表的第一个节点(头结点是第一个结点)。

    头结点:数值域可不设任何信息,头结点的指针域指向链表的第一个元素。

    带头节点的好处有:

    (1)链表第一位置节点上的操作和其它位置上的操作一致

    (2)无论链表是否为空,头指针都指向头结点(非空),空表和非空表处理一样

    单链表基本操作

    1.头插法建立新链表

     

     读入数据顺序与生成链表元素顺序是相反的。每个节点插入时间为o(1),设单链表长为n,则总时间复杂度为o(n)

    如果没有设立头结点,则需要在每次插入新节点后,将他的地址赋值给头指针L。

    2.尾插法

     

    1. LinkList List_TailInsert(LinkList &L){
    2. int x;
    3. L=(LinkList)malloc(sizeof(LNode));
    4. LNode *s,*r=L;//r为尾指针
    5. scanf("%d",&x);//输入结点值
    6. while(x!=9999){//9999表示结束
    7. s=(LNode *)malloc(sizeof(LNode));
    8. s->data=x;
    9. r->next=s;
    10. r=s;//r指向新的表尾指针
    11. scanf("%d",&x);
    12. }
    13. r->next=NULL;//尾指针指置空
    14. return L;
    15. }
    16. 时间复杂度与头插法相同

    3.按序号查找结点值

     时间复杂度o(n)

    4.按值查找表结点

    时间复杂度o(n)

    5.插入结点操作

     本算法主要时间开销在查找i-1元素上,时间复杂度为o(n),若结点已给定,则在其后插入结点时间复杂度为o(1)。

     6.删除结点操作

     时间复杂度为o(n)

     7.求表长操作

     双链表

    由于单链表插入删除都得从头遍历,比较麻烦,时间复杂度高。所以我们引入了双链表来解决

    主要就是结点中有两个指针,分别指向前驱和后继

    1. typedef struct DNode{
    2. ElemType data;//数据域
    3. struct DNode *prior,*next;//前驱后继指针
    4. }DNode, *DLinkList;

     由于引入了双指针,所有我们的插入删除时间复杂度变为了o(1)

    1.插入

     2.删除

     循环链表

     1.循环单链表

    引用这种后,我们对表头表尾操作的时间复杂度都变为了o(1)

    2.循环双链表

     

     静态链表

    借助数组来描述线性表的链式存储结构,结点也有数据域和指针域。

    左边为静态链表示例,右图为静态链表对应的单链表

    1. #define MaxSize 50//定义静态链表的最大长度
    2. typedef struct{
    3. ElemType data;//存储数据类型
    4. int next;//下一个元素的数组下标
    5. }SLinkList[MaxSize];

  • 相关阅读:
    5.Docker 私有库及镜像推送
    300元左右的耳机哪个性价比最好、好用的开放式耳机推荐
    DevOps初学者的指南——阿里出品学习图册带你掌握高薪技术!
    Python基础小知识问答系列-匿名函数
    用面向对象的方式操作 JSON 甚至还能做四则运算 JSON 库
    QCheckBox、margin、border、pandding、QHoxLayout、QSplitter
    云原生边缘设备解决方案Akri on k3s初体验
    modesim verilog仿真验证基本流程(新建工程方式)
    视觉在自动泊车系统中的设计与实现和挑战综述
    python练习题(慕课配套,三四五章)
  • 原文地址:https://blog.csdn.net/weixin_53011574/article/details/125560480