DS必背合集

发布时间 2023-11-03 22:25:40作者: 3cH0_Nu1L

Data Structure必背合集

一、链表、栈和队列

1、简述说明数据的存储结构:

答:

(1)顺序存储:逻辑上相邻的两个元素的物理位置也相邻。

  优点:能够随机存取。

  缺点:插入删除需要移动大量的元素,不方便。

(2)链式存储:逻辑上相邻的两个元素的物理位置不一定相邻,每个结点用一个指针来找到下一个结点的位置。

  优点:插入和删除很方便

  缺点:随机读取时不方便,需要从第一个结点开始遍历

(3)索引存储:在存储时,还附加建立索引表,索引表中的每一项称为索引项,索引项的一般形式是(关键字,地址)

  优点:检索速度快

  缺点:索引表占用存储空间,并且插入和删除一个数据时,对应的索引项也要插入和删除,会耗费较多的时间

(4)哈希存储:通过函数,根据数据的元素的关键字计算该元素的地址

  优点:检索、增加和删除结点的操作比较快

  缺点:可能会出现元素存储单元的冲突,解决冲突又需要增加时间和空间的开销

2、线性表有两种存储结构:一是顺序表,二是链表,试问:

(1)如果有多个线性表同时共存,并且在处理过程中各表的长度会动态地发生变化,线性表的总数也会自动地改变。在此情况下,应选用哪种存储结构?为什么?

(2)若线性表的总数基本稳定且很少进行插人和删除操作,但要求以最快的速度存取线性表中的元素,那么应采用哪种存取结构?为什么?

答:

(1)应选用链式存储结构。

  由于链式存储结构可以用任意的存储空间来存储线性表中的各数据元素,且其存储空间可以是连续的,也可以不连续;

  此外,这种存储结构对元素进行插人和删除操作时都无须移动元素,修改指针即可,所以很适用于线性表容量变化的情况,这种动态存储结构适合于多个线性表同时共存。

(2)应选用顺序存储结构。

  由于顺序存储结构一旦确定了起始位置,线性表中的任何一个元素都可以进行随机存取,即存取速度较高;

  并且由于线性表的总数基本稳定且很少进行插入和删除操作,所以恰好避开了顺序存储结构的缺陷。

3、若较频繁地对一个线性表进行插入和删除操作,该线性表宜采用何种存储结构?为什么?

答:

  该线性表宜采用链式存储结构。

  因为采用链式存储结构的线性表,进行插入和删除操作只需要改变指针,时间复杂度为 O(1);而采用顺序存储结构的线性表插入和删除时会涉及到数据的大量移动,时间复杂度为 O(n)。

4、循环比递归的效率一定高吗?

答:

循环和递归能够实现相互转换,且各自有自己的优缺点,判断谁的效率高是没有绝对的答案的。

递归:

  优点:代码简洁清晰、容易实现

  缺点:当递归次数很多时,需要增加额外的堆栈处理,有可能产生堆栈溢出的现象

循环:

  优点:结构简单,速度快,效率高

  缺点:不容易理解,编写复杂代码时会比较困难

5、顺序表和链表的比较:

答:

  顺序表和链表可以从四个大的方向去比较。

  1. 存取(读取)方式:顺序表能够随机读取和顺序读取,而链表只能按顺序读取。
  2. 查找:如果是按值查找并且表无序时,顺序表和链表的时间复杂度都是 O(n),如果表有序,则可以用折半查找法,时间复杂度是 O(nlog2n);如果是按序号查找,则顺序表支持随机查找,时间复杂度是 O(1),而链表的时间复杂度是 O(n)
  3. 插入和删除:顺序表插入和删除需要移动大量的元素,时间复杂度是O(n),链表的插入和删除只需要修改指针的位置,时间复杂度是 O(1)
  4. 空间分配:顺序表的空间分配分为静态分配和动态分配,静态内存分配时,很容易导致内存溢出或者是浪费,而动态内存分配时,有时候不存在一大块连续的存储空间,导致分配失败,并且需要移动大量的元素,效率低。而链表是直接在需要的时候申请内存,只要有内存就能够分配,操作灵活、高效。

6、对单链表设置头节点的作用是什么?(至少说出两条好处)

答:

对单链表设置头节点的好处如下:

①对于带头节点的单链表,在单链表的任何节点之前插入节点或删除都是修改前一个节点的指针域,因为任何节点都有前驱节点(若单链表没有头节点,则首节点没有前驱节点,在其前插入节点和删除该节点时操作复杂些)

②对于带头节点的链表,在表空时也存在一个头节点,因此空表与非空表的处理是一样的。

7、头指针和头结点和首元结点的区别:

答:

头指针是指在第一个结点之前的指针,它是一个链表存在的标志,是必须存在必不可少的。

头结点是第一个结点之前的结点,它是为了方面在第一个结点之前进行元素的插入和删除操作,它不是必须的,并且数据域也可以不存放信息。

首元素结点是指链表中存储线性表中第一个元素结点。

8、共享栈

答:

利用栈底位置不变的特性,让两个顺序栈共享同一个一维数组空间,将两个栈的栈底分别设在共享空间的两端,两个栈顶向共享空间延伸。

9、如何区分循环队列是队空还是队满?

答:

有两种区分方式:

  • 牺牲一个单元来区分队空和队满

    队空:队首指针 == 队尾指针;

    队满:(队尾指针+1) % MaxSize == 队首指针

  • 在结构体中增设表示元素数据的内存单元

    队空:元素的个数为 0;

    队满:元素的个数为 MaxSize

10、栈和队列的异同点?

答:

栈与队列的相同点:

①都是线性结构。

②插入操作都是限定在表尾进行。

③都可以通过顺序结构和链式结构实现。

④插入与删除的时间复杂度都是 O(1),在空间复杂度两者相同。

栈与队列的不同点:

①删除数据元素的位置不同,栈的删除操作在表尾进行,队列的删除操作在表头进行。

②应用场景不同,常见栈的应用场景包括括号问题的求解,表达式的转换和求值,函数调用和递归实现,深度优先搜索遍历等;常见的队列的应用场景包括计算机系统中各种资源的管理,消息缓冲器的管理和广度优先搜索遍历等。

③顺序栈能够实现多栈空间共享,而顺序队列不能。

11、利用两个栈 sl,s2 模拟一个队列时,如何用栈的运算实现队列的插入, 删除以及判队空运算。请简述这些运算的算法思想。

答:

栈的特点是后进先出,队列的特点是先进先出。

所以,当用两个栈 s1 和 s2 模拟一个队列时,s1 作为输入栈,逐个元素压栈,以此模拟队列元素的入队。

当需要出队时,将栈 s1 退栈并逐个压入栈 s2 中,s1 中最先入栈的元素在 s2中处于栈顶。

s2 退栈,相当于队列的出队,实现了先进先出。只有栈s2 为空且s1 也为空时,才算是队列空。

12、栈在括号匹配中的算法思想:

答:

①如果是左括号,则入栈

②如果是右括号,则判断当前栈是否为空,如果为空,则不匹配,不为空, 则看是否与栈顶的左括号匹配,如果匹配,则栈顶元素出栈

③最终所有的元素都进栈和出栈完毕,检查栈是否为空,如果不为空,则说明还有多余的左括号没有匹配,因此括号匹配失败,如果为空,则括号匹配成功。

13、栈在后缀表达式求值的算法思想:

答:

依次扫描表达式的每一项

①如果是操作数,则进栈

②如果是运算符,则从栈中退出两个元素,进行运算,且将得到的结果入栈

③表达式的所有项都扫描完后,后栈顶存放的元素就是最终的结果。

14、栈在递归中是怎样运用的?

答:

若在一个函数、一个过程或者一个数据结构的定义中直接或者间接的调用了它自身,则这个函数、这个过程、这个数据结构称为是递定义的,简称为递归。

递归问题只需要少数的代码就能够描述出解题过程中所需要的多次重复计算,大大减少了程序的代码量,递归会让代码的结构简单、清晰,但是大量的递归调用会建立函数的副本,耗费大量的时间和内存。

递归所用到的是系统管理栈,但是通常情况下,每次递归都要保留现场,空间复杂度为 O(n),效率不高,当递归次数过深的时候,容易出现堆栈溢出的现象。

15、在一个算法中需要建立多个栈时可以选用以下三种方案之一,试问这三种方案之间相比各有什么优缺点?

  1. 分别用多个顺序存储空间建立多个独立的顺序栈。
  2. 多个栈共享一个顺序存储空间的共享栈。
  3. 分别建立多个独立的链栈。

答:

方案一:

  优点是每个栈仅用一个顺序存储空间时,操作简单。

  缺点是分配空间小了,容易产生溢出,分配空间大了,容易造成浪费,各栈不能共享空间。

方案二:

  优点是多个栈仅用一个顺序存储空间,充分利用了存储空间,只有在整个存储空间都用完时才会产生溢出。

  缺点是当一个栈满时要向左、右查询有无空闲单元则要移动元素和修改相关的栈底和栈顶指针。当接近栈满时,要查询空闲单元、移动元素和修改栈底、栈顶指针,这一过程计算复杂且十分耗时。

方案三:

  优点是多个链栈一般不考虑栈的溢出。

  缺点是栈中元素要以指针相链接,比顺序存储多占用了存储空间。

16、什么是队列的上溢现象和假溢出现象?解决它们有哪些方法?

答:

在队列的顺序存储结构中,设队头指针为 front,队尾指针 rear,队的容量(存储空间的大小)为 MaxSize。

当有元素加入队列时,若 rear = MaxSize(初始时 rear=0)则发生队列的上溢现象,不能再向队列中加入元素。

所谓队列假溢出现象是指队列中还有剩余空间但元素却不能进人队列,这种现象是由于队列的设计不合理所致。

解决队列上溢的方法有以下几种:

  1. 建立一个足够大的存储空间,但会降低空间的使用效率。
  2. 当出现假溢出时可采用以下几种方法:

    ①采用平移元素的方法:每当队列中加入一个元素时,队列中已有的元素向队 头移动一个位置(当然要有空闲的空间可供移动)。

    ②每当删除一个队头元素时,则依次移动队中的元素,始终使front 指针指向队列中的第一个位置。

    ③采用环形队列方式:把队列看成一个首尾相接的环形队列,在环形队列上进 行插人或删除运算时仍然遵循“先进先出”的原则。