当前位置:   article > 正文

保研复习数据结构记(1)--概念,线性表_计算机保研数据结构专业课知识

计算机保研数据结构专业课知识

作为自己复习的参照物,所以复习系列主要以提问和回答形式为主。

一.前言

数结构解决什么问题:对现实世界信息化;高效解决程序问题

二.复习C语言指针

  • 什么是指针:是一个存放变量地址的变量,type* var=&anothervar,我们称var为指针,anothervar为被存放地址的变量,借用菜鸟教程上面的图片解答:

三.数据结构基本概念

  • 什么是数据:数据是信息的载体,是客观世界中输入到计算机中能被计算机识别的字符集合,比如说0和1

  • 数据元素和数据项与数据有什么关系:数据元素是数据的基本单位,数据元素可以由若干数据项组成,数据项不可分割。

  • 数据结构是什么:是相互之间存在一种或多种关系的数据元素的集合。

  • 什么是数据对象:具有相同性质的数据元素的集合,是数据的子集。

  • 数据结构的三要素是什么:逻辑结构、数据的运算、物理结构

  • 数据结构的逻辑结构都有什么:集合结构、线性结构、树形结构、网状结构

  • 数据的物理结构都有什么结构:顺序存储、链式存储、索引存储、散列存储     

  • 什么是顺序存储:逻辑上相邻的元素物理上也相邻

  • 什么是链式存储:逻辑上相邻的元素,物理上不相邻,用指针反映逻辑关系

  • 什么是索引存储:用索引表记录数据的关键字和地址的关系

  • 什么是散列存储:根据元素的关键字计算存储地址

  • 什么是抽象数据类型:是抽象数据组织及与之相关的操作,定义ADT就是在定义一种数据结构,确定了ADT的存储结构,才能实现这种数据结构

  • 什么是算法:是对特定问题求解步骤的一种描述,程序=数据结构+算法

  • 算法具备哪些特性:有穷性:算法必须在有穷步骤结束,死循环不是算法;确定性:算法对于相同的输入必须有相同的输出;可行性:算法中描述的操作都可以通过已经实现的基本运算执行有限次来实现。;输入:一个或多个输入;输出:一个或多个输出

  • “好算法”的特质:正确性;可读性;健壮性(对错误的反应做出处理);时间空间复杂度低

  • 什么是算法的时间复杂度:事先预估问题规模n算法运行时间T的关系T(n)

  • 常见算法时间复杂度:

 四.线性表 

  • 什么是线性表:具有相同数据类型的n个数据元素的有限序列,除首末元素外,每个元素只有一个前驱和后继。

1.顺序表
  • 什么是顺序表:用顺序存储的方式实现线性表

  • 如何求出顺序表中第i个元素的位置,假设第一个元素的存放位置是:LOC(L)              LOC(Li)=LOC(L)+i*数据元素的大小

  • C++如何求数据元素的大小:sizeof函数,使用范例如下:

  1. cout<<"sizeof(bool)="<<sizeof(bool)<<endl; // 1
  2. cout<<"sizeof(char)="<<sizeof(char)<<endl; // 1
  3. cout<<"sizeof(short)="<<sizeof(short int)<<endl; // 2
  4. cout<<"sizeof(int)="<<sizeof(int)<<endl; // 4
  •  顺序表的顺序存储定义,初始化示例:

  1. #include<bits/stdc++.h>
  2. #define MaxSize 10//定义最大长度
  3. typedef struct
  4. {
  5. int data[MaxSize];//用静态数组存放元素
  6. int length;//顺序表当前的长度
  7. }Sqlist;//顺序表的类型定义
  8. void InitList(Sqlist &L)
  9. {
  10. for(int i=0;i<MaxSize;++i)
  11. {
  12. L.data[i]=0;//将所有数据元素默认为初始值
  13. }
  14. L.length=0;//顺序表初始长度为0
  15. }
  16. int main()
  17. {
  18. Sqlist L;//声明一个顺序表
  19. InitList(L);//初始化顺序表
  20. return 0;
  21. }
  •  顺序表的链式存储定义,初始化如下:

  1. #include<bits/stdc++.h>
  2. #define InitSize 10//默认的最大长度
  3. typedef struct
  4. {
  5. int* data;//指示动态分配数组的指针
  6. int MaxSize;//顺序表的最大容量
  7. int length;//顺序表的当前长度
  8. }Seqlist;
  9. void InitList(Seqlist &L)//分配存储空间
  10. {
  11. int* p=new int[InitSize];//用new申请一片连续的内存空间,但是这是新一片
  12. L.data=p;
  13. L.length=0;
  14. L.MaxSize=InitSize;
  15. }
  16. void IncreaseSize(Seqlist &L,int len)
  17. {
  18. int* p=L.data;
  19. L.data=(int*)new int[L.MaxSize+len];//增大存储空间
  20. for(int i=0;i<L.length;++i)
  21. {
  22. L.data[i]=p[i];//将数据复制到新区域
  23. }
  24. L.MaxSize=L.MaxSize+len;
  25. delete []p;
  26. }
  27. int main()
  28. {
  29. Seqlist L;//声明一个顺序表
  30. InitList(L);//初始化顺序表
  31. IncreaseSize(L,5);
  32. return 0;
  33. }
  • 顺序表有什么特点:随机访问O(1)访问;存储密度高;拓展容量不方便;插入删除不方便

  • 顺序表的插入操作代码实现如下,平均时间复杂度为O(n)

  1. bool InsertList(Sqlist &L,int i,int e)
  2. {
  3. if(i<1||i>L.length+1)//当i<1也就是i=0时,是不正确的;同理当i=L.length+2时是顺序表不连续的,注意代码的健壮性
  4. return false;
  5. if(L.length>MaxSize)
  6. return false;
  7. for(int j=L.length;j>=i;--j)//j必须从L.length开始反向赋值
  8. L.data[j]=L.data[j-1];//后移元素
  9. L.data[i-1]=e;//赋值新元素,第i个位置实际上是线性表的i-1下标
  10. L.length++;
  11. return true;
  12. }

注意!!代码第七行j一定要大于等于i,而不是j大于i,只有j大于等于i的时候,j最后一次循环的值才是i,又因为i其实不是实际意义上的下标,而是比下标多+1。

  • 顺序表的删除操作,是正向赋值,后面的赋值给前面的,时间复杂度O(n)

    1. bool DeleteList(Sqlist& L,int i,int& e)//将删除的值赋给e之后返回
    2. {
    3. if(i<1||i>L.length)//判断线性表范围是否有效
    4. return false;
    5. e=L.data[i-1];//i和下标不一样,i比下标+1
    6. for(int j=i;j<L.length;++j)//和插入不同,这次是正向
    7. {
    8. L.data[j-1]=L.data[j];//后面的覆盖前面的
    9. }
    10. L.length--;//长度-1
    11. return true;
    12. }
  • 顺序表的查找操作

        按位查找O(1)

  1. GetElem(Sqlist L,int i)
  2. {
  3. return L.data[i-1];
  4. }
2.链表
  • 什么是链表:用链式存储方式实现的线性表
  • 用代码定义一个单链表,实现获取第i个元素的函数
  1. typedef struct//定义单链表节点类型
  2. {
  3. int data;//定义数据域
  4. struct LNode* next;//定义指针域
  5. }LNode,*LinkList;
  6. //LNode强调是某个节点的指针
  7. //LinkList强调的是整个链表
  8. LNode* GetElem(LinkList L,int i)
  9. {
  10. int j=1;
  11. if(i==0)//返回第一个元素
  12. return L;
  13. if(i<j)//如果是负数的话要返回空
  14. return NULL;
  15. LNode* p=L->next;
  16. while(p!=NULL&&j<i)
  17. {
  18. p=p->next;
  19. ++j;
  20. }
  21. return p;
  22. }
  • 带头节点的链表初始化和判断是否为空如下,带头节点的链表比不带头节点的链表使用方便。
  1. bool InitList(LinkList& L)
  2. {
  3. L=(LNode*)new LNode;
  4. if(L==NULL)//内存不足,分配失败
  5. return false;
  6. L->next=NULL;//头结点之后暂时没有节点
  7. return true;
  8. }
  9. bool IsEmpty(LinkList L)
  10. {
  11. if(L->next==NULL)
  12. return true;//确实为空
  13. else
  14. return false;
  15. // return (L==NULL); //如果不带头节点,这样返回
  16. }
  •  单链表按位序插入代码如下(带头节点)
  1. //对于列表L在第i个位置插入e,带头节点
  2. bool Insert(LinkList L,int i,int e)
  3. {
  4. if(i<1)
  5. return false;
  6. LNode* p;//定义一个查询指针
  7. int j=0;
  8. p=L;
  9. while(p!=NULL&&j<i-1)//逐渐移动到位序为i的前一个i-1
  10. {
  11. p=p->next;
  12. ++j;
  13. }
  14. if(p==NULL)//发现第i-1个是空
  15. return false;
  16. LNode* s=(LNode*)new LNode;//创建节点
  17. s->data=e;//插入
  18. s->next=p->next;
  19. p->next=s;
  20. return true;
  21. }
  •   单链表按位序删除代码如下(带头节点)
  1. bool ListDelete(LinkList& L,int i,int& e)
  2. {
  3. if(i<1)
  4. return false;
  5. LNode* p;
  6. int j=0;
  7. p=L;
  8. while(p!=NULL&&j<i-1)
  9. {
  10. p=p->next;
  11. ++j;
  12. }
  13. if(p==NULL)//i值不合法
  14. return false;
  15. if(p->next==NULL)//第i个值之后没有其他节点
  16. return false;
  17. LNode* q=p->next;
  18. e=q->data;
  19. p->next=p->next->next;
  20. delete q;//删除中介节点
  21. return true;
  22. }
 4.双链表
  •  双链表的定义
  1. typedef struct DNode{
  2. int data;
  3. struct DNode *prior,*next;
  4. }DNode,*DLinkList;
  • 双链表的初始化和判断是否为空
  1. //初始化双链表
  2. bool InitDLinkList(DLinkList& L)
  3. {
  4. L=(DNode*)new DNode;
  5. if(L==NULL)//分配失败
  6. return false;
  7. DNode->next=NULL;
  8. DNode->prior=NULL;
  9. return true;
  10. }
  11. //判断双链表是否为空(带头节点)
  12. bool Empty(DLinkList L)
  13. {
  14. if(L->next==NULL)//
  15. return true;
  16. else
  17. return false;
  18. }
  • 双链表的插入
  1. //双链表的插入:在p节点之后插入s节点
  2. bool InsertNextDNode(DNode* p,DNode* s)
  3. {
  4. if(p==NULL||s==NULL)//非法参数
  5. return false;
  6. s->next=p->next;//注意接下来移动指针的顺序
  7. if(p->next!=NULL)//如果p节点后有后继节点
  8. p->next->prior=s;
  9. s->prior=p;
  10. p->next-s;
  11. return true;
  12. }
  • 双链表的删除
  1. //删除p的后继节点
  2. bool DeteteNextNode(DNode* p)
  3. {
  4. if(p==NULL)
  5. return false;
  6. DNode* q=p->next;//找到p的后继节点q
  7. if(q==NULL) return false;//如果p没有后继节点
  8. p->next=q->next;
  9. if(q->next!=NULL)//q节点不是最后一个节点
  10. q->next->prior=p;
  11. delete q;
  12. return true;
  13. }
 5.循环链表
  • 循环单链表 
  1. //定义单链表并且初始化
  2. typedef struct LNode//定义单链表节点类型
  3. {
  4. int data;//每个节点存放一个数据元素
  5. struct LNode* next;//指针指向下一个节点
  6. }LNode,*LinkList;
  7. //初始化一个循环单链表
  8. bool InitList(LinkList& L)
  9. {
  10. L=(LNode*)new LNode;//分配一个头节点
  11. if(L==NULL)//内存不足分配失败
  12. return false;
  13. L->next=L;//头节点next指向头节点
  14. return true;
  15. }
  16. //判断循环单链表是否为空
  17. bool Empty(LinkList L)
  18. {
  19. if(L->next==L)
  20. return true;
  21. else
  22. return false;
  23. }
  • 循环双链表
  1. //定义双链表并且初始化
  2. typedef struct DNode//定义双链表节点类型
  3. {
  4. int data;//每个节点存放一个数据元素
  5. struct DNode* next,*prior;//指针指向下一个节点
  6. }DNode,*DLinkList;
  7. //初始化一个循环双链表
  8. bool InitList(DLinkList& L)
  9. {
  10. L=(LNode*)new LNode;//分配一个头节点
  11. if(L==NULL)//内存不足分配失败
  12. return false;
  13. L->next=L;//头节点next指向头节点
  14. L->prior=L;
  15. return true;
  16. }
  17. //判断循环双链表是否为空
  18. bool Empty(DLinkList L)
  19. {
  20. if(L->next==L)
  21. return true;
  22. else
  23. return false;
  24. }
5.顺序表和链表的比较
  • 有什么相同点:都属于线性表
  • 存储结构的优缺点:顺序表支持随机存取、不需要存储指针,存储密度高;但是大片连续空间分配不方便,改变容量不方便。链表改变容量方便,但是不可随机存取。
  • 在创建线性表方面有什么不同:顺序表需要预分配大片连续空间,若之后空间过小,则不方便拓展容量;若分配空间过大,则浪费资源。而链表只需要分配一个头节点,灵活性好。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小桥流水78/article/detail/955494
推荐阅读
相关标签
  

闽ICP备14008679号