数据结构与算法常见面试题1

烈酒焚心 提交于 2020-02-24 20:22:00

1.八大数据结构及其应用场景(数组、栈、链表、树、图、堆、散列表)

  • 1.数组
    数据结构中最基本的一个结构就是线性结构,而线性结构又分为连续存储结构和离散存储结构。连续存储结构其实就是数组。
    在程序设计中,为了处理方便, 把具有相同类型的若干变量按有序的形式组织起来。这些按序排列的同类数据元素的集合称为数组。一个数组可以分解为多个数组元素,这些数组元素可以是基本数据类型或是构造类型。因此按数组元素的类型不同,数组又可分为数值数组、字符数组、指针数组、结构数组等各种类别。
    使用场景
    数组在以下三个情形下很有用:
    1)数据量较小。
    2)数据规模已知。
    3)随机访问,修改元素值。
    如果插入速度很重要,选择无序数组。如果查找速度很重要,选择有序数组,并使用二分查找。
    缺点
    1)需要预先知道数据规模
    2)插入效率低,因为需要移动大量元素。
  • 2.栈
    是只能在某一端插入和删除的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。
    使用场景
    1.十进制与其它进制间的转换 125→1111111
    2.行编辑器 #退格 @清除
    3.平衡符号的判断 {[()()]}
    4.中缀表达式转后缀表达式
    顺序栈
    优点
    1)在输入数据量可预知的情形下,可以使用数组实现栈,并且数组实现的栈效率更高,出栈和入栈操作都在数组末尾完成。
    缺点
    1)如果对数组大小创建不当,可能会产生栈溢出的情况
    链栈
    优点
    1)不会发生栈溢出的情况
    2)输入数据量未知时,使用链栈。通过头插法实现入栈操作,头删法实现出栈操作。出栈和入栈均是O(1)。
    缺点
    1)由于入栈时,首先要创建插入的节点,要向操作系统申请内存,所以链栈没有顺序栈效率高。
  • 3.队列
    一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列是按照"先进先出"或"后进后出"的原则组织数据的。队列中没有元素时,称为空队列。
    只要涉及到先进先出的设计,即采用了队列的思想。Eg:排队叫号系统(先来先服务) 
    如果数据量已知就使用数组实现队列,未知的话就使用链表实现队列。出队和入队均是O(1)。
    消息队列
    可以把消息队列比作是一个存放消息的容器,当我们需要使用消息的时候可以取出消息供自己使用。消息队列是分布式系统中重要的组件,使用消息队列主要是为了通过异步处理提高系统性能和削峰、降低系统耦合性。目前使用较多的消息队列有ActiveMQ,RabbitMQ,Kafka,RocketMQ。
    使用消息队列主要有两点好处:1.通过异步处理提高系统性能(削峰、减少响应所需时间);2.降低系统耦合性。
    通过异步处理提高系统性能(削峰、减少响应所需时间)
    如图,在不使用消息队列服务器的时候,用户的请求数据直接写入数据库,在高并发的情况下数据库压力剧增,使得响应速度变慢。但是在使用消息队列之后,用户的请求数据发送给消息队列之后立即 返回,再由消息队列的消费者进程从消息队列中获取数据,异步写入数据库。由于消息队列服务器处理速度快于数据库(消息队列也比数据库有更好的伸缩性),因此响应速度得到大幅改善。
    通过以上分析我们可以得出消息队列具有很好的削峰作用的功能——即通过异步处理,将短时间高并发产生的事务消息存储在消息队列中,从而削平高峰期的并发事务。 举例:在电子商务一些秒杀、促销活动中,合理使用消息队列可以有效抵御促销活动刚开始大量订单涌入对系统的冲击。
    (2) 降低系统耦合性
    消息队列使利用发布-订阅模式工作,消息发送者(生产者)发布消息,一个或多个消息接受者(消费者)订阅消息。 从上图可以看到消息发送者(生产者)和消息接受者(消费者)之间没有直接耦合,消息发送者将消息发送至分布式消息队列即结束对消息的处理,消息接受者从分布式消息队列获取该消息后进行后续处理,并不需要知道该消息从何而来。对新增业务,只要对该类消息感兴趣,即可订阅该消息,对原有系统和业务没有任何影响,从而实现网站业务的可扩展性设计。
    另外为了避免消息队列服务器宕机造成消息丢失,会将成功发送到消息队列的消息存储在消息生产者服务器上,等消息真正被消费者服务器处理后才删除消息。在消息队列服务器宕机后,生产者服务器会选择分布式消息队列服务器集群中的其他服务器发布消息。
    使用消息队列带来的一些问题
    系统可用性降低: 系统可用性在某种程度上降低,为什么这样说呢?在加入MQ之前,你不用考虑消息丢失或者说MQ挂掉等等的情况,但是,引入MQ之后你就需要去考虑了!
    系统复杂性提高: 加入MQ之后,你需要保证消息没有被重复消费、处理消息丢失的情况、保证消息传递的顺序性等等问题!
    一致性问题: 上面讲了消息队列可以实现异步,消息队列带来的异步确实可以提高系统响应速度。但是,万一消息的真正消费者并没有正确消费消息怎么办?这样就会导致数据不一致的情况了!
  • 4.链表
    是一种物理存储单元上非连续、非顺序的存储结构,它既可以表示线性结构,也可以用于表示非线性结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
    解决的问题
    链表的出现解决了数组的两个问题:
    1)需要预先知道数据规模
    2)插入效率低
    使用场景
    数据库B+树叶子节点就是一个链表结构;hashMap使用链表解决hash冲突等;
    1)数据量较小
    2)不需要预先知道数据规模
    3)适应于频繁的插入操作
    缺点
    1)有序数组可以通过二分查找方法具有很高的查找效率(O(log n)),而链表只能使用顺序查找,效率低下(O(n))。
  • 5.树
    每个结点有零个或多个子结点;没有父结点的结点称为根结点;每一个非根结点有且只有一个父结点;除了根结点外,每个子结点可以分为多个不相交的子树;
    二叉树:每个节点最多含有两个子树的树称为二叉树;
    完全二叉树:对于一颗二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树;
    满二叉树:所有叶节点都在最底层的完全二叉树;
    平衡二叉树(AVL树):当且仅当任何节点的两棵子树的高度差不大于1的二叉树;
    红黑树: 是一种自平衡二叉查找树,它是在1972年由鲁道夫·贝尔发明的,他称之为”对称二叉B树”
    排序二叉树(二叉查找树):也称二叉搜索树、有序二叉树;
    霍夫曼树:带权路径最短的二叉树称为哈夫曼树或最优二叉树;
    B树:一种对读写操作进行优化的自平衡的二叉查找树,能够保持数据有序,拥有多于两个子树。
    B+树:数据库索引
    1.非叶子结点的每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。
    2.所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。
    3.叶子结点本身依关键字的大小自小而大顺序链接(指针)。
    1.单一节点存储更多的元素,使得查询的IO次数更少。
    2.所有查询都要查找到叶子节点,查询性能稳定。
    3.所有叶子节点形成有序链表,便于范围查询。
    二叉查找树
    解决的问题
    1)有序数组具有较高的查找效率(O(log n)),而链表具有较高的插入效率(头插法,O(1)),结合这两种数据结构,创建一种貌似完美的数据结构,也就是二叉查找树。
    使用场景
    1)数据是随机分布的
    2)数据量较大
    3)频繁的查找和插入操作(可以提供O(log n)级的查找、插入和删除操作)
    缺点
    1)如果处理的数据是有序的(升序/降序),那么构造的二叉查找树就会只有左子树(或右子树),也就是退化为链表,查找效率低下(O(log n))。
    平衡树
    解决的问题
    1)针对二叉查找树可能会退化为链表的情况,提出了平衡树,平衡树要求任意节点的左右两个子树的高度差不超过1,避免退化为链表的情况。
    使用场景
    1)无论数据分布是否随机都可以提供O(log n)级别的查找、插入和删除效率
    2)数据量较大
    缺点
    1)平衡树的实现过于复杂。
  • 6.图
    图是由结点的有穷集合V和边的集合E组成。其中,为了与树形结构加以区别,在图结构中常常将结点称为顶点,边是顶点的有序偶对,若两个顶点之间存在一条边,就表示这两个顶点具有相邻关系。
    最小生成树——无向连通图的所有生成树中有一棵边的权值总和最小的生成树
    拓扑排序 ——由偏序定义得到拓扑有序的操作便是拓扑排序。建立模型是AOV网
    关键路径——在AOE-网中有些活动可以并行地进行,所以完成工程的最短时间是从开始点到完成点的最长路径的长度,路径长度最长的路径叫做关键路径(Critical Path)。
    最短路径——最短路径问题是图研究中的一个经典算法问题, 旨在寻找图(由结点和路径组成的)中两结点之间的最短路径。
  • 7.堆
    在计算机科学中,堆是一种特殊的树形数据结构,每个结点都有一个值。通常我们所说的堆的数据结构,是指二叉堆。堆的特点是根结点的值最小(或最大),且根结点的两个子树也是一个堆。
    ①优先级队列
    在某一情况下队列的先进先出并不能满足我们的需求,我们需要优先级高的先出队列,这就类似VIP之类的.
    ②topk问题(构建相反堆找出前k个数) 在大规模数据处理中,经常会遇到的一类问题:在海量数据中找出出现频率最好的前k个数,或者从海量数据中找出最大的前k个数,这类问题通常被称为top K问题。例如,在搜索引擎中,统计搜索最热门的10个查询词;在歌曲库中统计下载最高的前10首歌等。
    ③堆排序(升序 — 构建大堆 降序 — 构建小堆)
    堆排序:先建立一个最大堆。然后将最大堆的a[0]与a[n]交换,然后从堆中去掉这个节点n,通过减少n的值来实现。剩余的节点中,新的根节点可能违背了最大堆的性质,因此需要调用向下调整函数来维护最大堆。
  • 8.散列表(哈希表)
    若结构中存在关键字和K相等的记录,则必定在f(K)的存储位置上。由此,不需比较便可直接取得所查记录。称这个对应关系f为散列函数(Hash function),按这个思想建立的表为散列表。
    解决的问题
    同平衡树一样,哈希表也不要求数据分布是否随机,不过哈希表的实现比平衡树要简单得多。
    使用场景
    1.信息安全领域:
    Hash算法 可用作加密算法。
    如文件校验:通过对文件摘要,可以得到文件的“数字指纹”,你下载的任何副本的“数字指纹”只要和官方给出的“数字指纹”一致,那么就可以知道这是未经篡改的。例如著名的MD5
    2.数据结构领域:
    Hash算法 通常还可用作快速查找。 根据Hash函数 我们可以实现一种叫做哈希表(Hash Table)的数据结构。这种结构可以实现对数据进行快速的存取。
    1)不需要对最大最小值存取。
    2)无论数据分布是否随机,理想情况下(无冲突)可以提供O(1)级别的插入、查找和删除效率。
    3)数据量较大
  1. 数据库的Hash索引。hashMap的hash索引等。
    缺点
    1)由于是基于数组的,数组(哈希表)创建后难以扩展,使用开放地址法的哈希表在基本被填满时,性能下降的非常严重。
    2)不能对最大最小值存取。
  2. 说一下数组和链表的区别?它们分别使用于什么场合?
    数组和链表是两种基本的数据结构,他们在内存存储上的表现不一样,所以也有各自的特点。
    数组
    ①在内存中,数组是一块连续的区域
    ②数组需要预留空间
    在使用前需要提前申请所占内存的大小,这样不知道需要多大的空间,就预先申请可能会浪费内存空间,即数组空间利用率低
    ③.在数组起始位置处,插入数据和删除数据效率低。
    插入数据时,待插入位置的的元素和它后面的所有元素都需要向后搬移
    删除数据时,待删除位置后面的所有元素都需要向前搬移
    ④.随机访问效率很高,时间复杂度可以达到O(1)
    ⑤.数组开辟的空间,在不够使用的时候需要扩容,扩容的话,就会涉及到需要把旧数组中的所有元素向新数组中搬移.
    ⑥.数组的空间是从栈分配的
    链表
    1.在内存中,元素的空间可以在任意地方,空间是分散的,不需要连续
    2.链表中的元素都会两个属性,一个是元素的值,另一个是指针,此指针标记了下一个元素的地址。每一个数据都会保存下一个数据的内存的地址,通过此地址可以找到下一个数据。
    3.查找数据时效率低,时间复杂度为O(N)
    因为链表的空间是分散的,所以不具有随机访问性,如要需要访问某个位置的数据,需要从第一个数据开始找起,依次往后遍历,直到找到待查询的位置,故可能在查找某个元素时,时间复杂度达到O(N)
    4.空间不需要提前指定大小,是动态申请的,根据需求动态的申请和删除内存空间,扩展方便,故空间的利用率较高
    5.任意位置插入元素和删除元素效率较高,时间复杂度为O(1)
    6.链表的空间是从堆中分配的
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!