基础数据结构和算法概念,腾讯前端面试题目

84 阅读19分钟

文末

我一直觉得技术面试不是考试,考前背背题,发给你一张考卷,答完交卷等通知。

首先,技术面试是一个 认识自己 的过程,知道自己和外面世界的差距。

更重要的是,技术面试是一个双向了解的过程,要让对方发现你的闪光点,同时也要 试图去找到对方的闪光点,因为他以后可能就是你的同事或者领导,所以,面试官问你有什么问题的时候,不要说没有了,要去试图了解他的工作内容、了解这个团队的氛围。

前端面试题汇总

JavaScript

性能

linux

前端资料汇总

开源分享:docs.qq.com/doc/DSmRnRG…

====

程序 = 数据结构 + 算法

数据结构基本概念


  1. 数据的逻辑结构:反映数据元素之间的关系的数据元素集合的表示。数据的逻辑结构包括集合、线形结构、树形结构和图形结构四种。

  2. 数据的存储结构:数据的逻辑结构在计算机存储空间种的存放形式称为数据的存储结构。常用的存储结构有顺序、链接、索引等存储结构。

在数据结构中,没有前件的结点称为根结点,没有后件的结点成为终端结点

数据结构的基本操作

插入和删除是对数据结构的两种基本操作。此外还有查找、分类、合并、分解、复制和修改等。

线性结构和非线性结构

根据数据结构中各数据元素之间前后件关系的复杂程度,一般将数据结构分为两大类型:线性结构和非线性结构。

  • 线性结构:有且只有一个根结点;每个结点最多有一个前件,最多只有一个后件。

  • 非线性结构: 如果一个数据结构不是线性结构,称之为非线性结构。

本文涉及一下内容:

  • 四种线性结构的存储结构:顺序表、链表、索引、散列

  • 两种常见的线性逻辑结构:队列、栈

  • 非线性逻辑结构:循环队列、双向队列、双向循环队列、树、图

存储结构


顺序表

顺序表是线性表的顺序存储结构,指的是用一组地址连续的存储单元依次存储线性表的数据元素。

顺序表具备如下两个基本特征:

  1. 顺序表中的所有元素所占的存储空间是连续的;

  2. 顺序表中各数据元素在存储空间中是按逻辑顺序依次存放的。

假设顺序表的每个元素需占用 K K K 个存储单元,并以所占的第一个单元的存储地址作为数据元素的存储位置。则顺序表中第 i+1i+1i+1 个数据元素的存储位置 LOC(ai+1) L O C ( a i + 1 ) LOC(a_i+1) 和第 i i i 个数据元素的存储位置 LOC(ai)LOC(ai)LOC(a_i) 之间满足下列关系为:

LOC(ai+1)=LOC(ai)+K L O C ( a i + 1 ) = L O C ( a i ) + K LOC(a_{i+1}) = LOC(a_i)+K

LOC(ai)=LOC(a1)+(i−1)∗K L O C ( a i ) = L O C ( a 1 ) + ( i − 1 ) ∗ K LOC(a_i) = LOC(a_1)+(i-1)*K

其中, LOC(a1) L O C ( a 1 ) LOC(a_1)是顺序表的第一个数据元素 a1 a 1 a_1 的存储位置,通常称做顺序表的起始位置或基地址。顺序存储结构也称随机存取结构。

顺序表常见操作(括号中为算法平均时间复杂度,没有写明的具体复杂度依赖不同算法和运算规则):

插入( O(n) O ( n ) O(n))、删除( O(n) O ( n ) O(n))、查找、排序、分解、合并、复制( O(n) O ( n ) O(n))、逆转( O(n) O ( n ) O(n))

链表

链表指线性表的链式存储结构。一组任意的存储单元存储线性表的数据元素,因此,为了表示每个数据元素 ai a i a_i 与其直接后继数据元素 ai+1 a i + 1 a_{i+1} 之间的逻辑关系,对数据元素 ai a i a_i 来说,除了存储其本身的信息(数据域)之外,还需存储一个变量指示其直接后继的信息(指针域)。这两部分信息组成数据元素 ai a i a_i 的存储映象,称为结点。 N N N 个结点链结成一个链表。该链表就是传统的单向链表。

有时,我们在单链表的第一个结点之前附设一个结点,称之为头结点,它指向表中第一个结点。头结点的数据域可 以不存储任何信息,也可存储如线性表的长度等类的附加信息,头结点的指针域存储指向第一个结点的指针。在单链表中,取得第 I 个数据元素必须从头指针出发寻找,因此,链表是非随机存取的存储结构。

以上提到的链表指针域只包括一个指针,指向下一个数据的地址,如果我们将链表最后一个结点指针域的指针指向链表的头结点地址,就构成了一个环状的存储结构,我们称作循环链表。

当然我们可以给每个结点的指针域再添加一个指针,使其指向前一个数据结点的地址,这样就构成了双向链表,而将头结点的前一个结点指向尾结点,同时将尾结点的下一个结点指向头结点就构成了双向循环链表。

如果链表的尾结点的指针域指向了该链表之前的任意一个结点,我们称该链表为有环链表。环形链表就是其中一个特例

顺序表常见操作(括号中为算法平均时间复杂度,没有写明的具体复杂度依赖不同算法和运算规则):

插入(O(n)O(n)O(n))、删除( O(n) O ( n ) O(n))、查找、排序、分解、合并、复制( O(n) O ( n ) O(n))、逆转( O(n) O ( n ) O(n))

索引

索引存储除建立存储结点信息外,还建立附加的索引表来标识结点的地址。索引表由若干索引项组成。

对于索引的理解最好的例子就是《新华字典》,它建立的2套索引表(拼音、部首)。字典的正文就是从“啊”到“做”的每个字的解释,有上千页,就是是数据。而前面的拼音/部首就是索引表,索引表告诉你某个读音/部首在第几页,这就好比是指向数据地址的指针。而索引表可以有一级的也可以是多级的,比如字典中的部首索引就是两级的。

索引存储结构是用结点的索引号来确定结点存储地址,其优点是检索速度快,缺点是增加了附加的索引表,会占用较多的存储空间。

散列

散列存储,又称哈希(hash)存储,是一种力图将数据元素的存储位置(预留连续存储区域)与关键码之间建立确定对应关系的查找技术。散列法存储的基本思想是由结点的关键码值决定结点的存储地址。散列技术除了可以用于存储外,还可以用于查找。

散列以数据中每个元素的关键字 K K K 为自变量,通过散列函数 H(k)H(k)H(k) 计算出函数值,以该函数值作为一块连续存储空间的的单元地址,将该元素存储到函数值对应的单元中。由于该函数值唯一,所以查找时间复杂度为 O(1) O ( 1 ) O(1)

线性逻辑结构


线性表

线性表满足以下特征:

  1. 有且只有一个根结点 a1 a 1 a_1,它无前件;

  2. 有且只有一个终端结点 an a n a_n,它无后件;

  3. 除根结点与终端结点外,其他所有结点有且只有一个前件,也有且只有一个后件。线性表中结点的个数 n n n 称 为线性表的长度。当 n=0n=0n=0 时称为空表。

栈实际上也是一个线性表,只不过是一种特殊的线性表。栈是只能在表的一端进行插入和删除运算的线性表,通常称插入、删除这一端为栈顶(TOP),另一端为栈底(BOTTOM)。当表中没有元素时称为栈空。 栈顶元素总是后被插入(入栈)的元素,从而也是最先被移除(出栈)的元素;栈底元素总是最先被插入的元素,从而也是最后才能被移除的元素。所以栈是个 后进先出(LIFO) 的数据结构

栈的基本运算有三种:入栈、出栈与读栈顶,时间复杂度都是 O(1) O ( 1 ) O(1)

队列

队列是只允许在一端删除,在另一端插入的顺序表,允许删除的一端叫做队头,用对头指针 front f r o n t front 指向对头元素的下一个元素,允许插入的一端叫做队尾,用队尾指针 rear r e a r rear 指向队列中的队尾元素,因此,从排头指针 front f r o n t front 指向的下一个位置直到队尾指针 rear r e a r rear 指向的位置之间所有的元素均为队列中的元素。

队列的修改是 先进先出(FIFO) 。往队尾插入一个元素称为入队运算。从对头删除一个元素称为退队运算。

队列主要有两种基本运算:入队运算和退队运算,复杂度都是 O(1) O ( 1 ) O(1)

循环队列

在实际应用中,队列的顺序存储结构一般采用循环队列的形式。所谓循环队列,就是将队列存储空间的最后一个 位置绕到第一个位置,形成逻辑上的环状空间。在实际使用循环队列时,为了能区分队满还是队列空,通常需要增加一个标志 S S S。

循环队列主要有两种基本运算:入队运算和退队运算,复杂度都是O(1)O(1)O(1)

  • 入队运算

指在循环队列的队尾加入一个新元素,首先 rear=rear+1 r e a r = r e a r + 1 rear=rear+1, 当 rear=m+1 r e a r = m + 1 rear=m+1 时,置 rear=1 r e a r = 1 rear=1,然后将新元素插入到队尾指针 指向的位置。当 S=1,rear=front S = 1 , r e a r = f r o n t S=1, rear=front,说明队列已满,不能进行入队运算,称为“上溢”。

  • 退队运算

指在循环队列的排头位置退出一个元素并赋给指定的变量。首先 front=front+1 f r o n t = f r o n t + 1 front=front+1, 并当 front=m+1 f r o n t = m + 1 front=m+1 时,置 front=1 f r o n t = 1 front=1, 然后 将排头指针指向的元素赋给指定的变量。当循环队列为空 S=0 S = 0 S=0,不能进行退队运算,这种情况成为“下溢”。

非线性逻辑结构


树是一种简单的非线性结构。树型结构具有以下特点:

  1. 每个结点只有一个前件,称为父结点,没有前件的结点只有一个,称为树的根结点。

  2. 每一个结点可以有多个后件结点,称为该结点的子结点。没有后件的结点称为叶子结点

  3. 一个结点所拥有的后件个数称为结点的度

  4. 树的最大层次称为树的深度。

二叉树

二叉树是一种树型结构,通常采用链式存储结构,满足以下特性:

  1. 它的特点是每个结点至多只有二棵子树(即二叉树中不存在度大于 2 的结点);

  2. 二叉树的子树有左右之分,其次序不能任意颠倒。

二叉树的基本性质

  • 在二叉树的第 i i i 层上至多有 2i−12i−12i-1 个结点

  • 深度为 k k k 的二叉树至多有 2k−12k−12k-1 个结点( k≥1 k ≥ 1 k \geq 1)

  • 在任意一个二叉树中,度为 0 的结点总是比度为 2 的结点多一个

  • 具有 N N N 个结点的二叉树,其深度至少为 ⌊log2N⌋+1⌊log2N⌋+1\lfloor log_2 N \rfloor+1

  • 霍夫曼树的带权路径长度 len=2n+1 l e n = 2 n + 1 len = 2n+1; n n n 为所以叶子权重和。

二叉树的遍历

就是遵从某种次序,访问二叉树中的所有结点,使得每个结点仅被访问一次。分为以下几种:

  1. 前序遍历(DLR): 首先访问根结点,然后遍历左子树,最后遍历右子树。

  2. 中序遍历(LDR): 首先遍历左子树,然后根结点,最后右子树

  3. 后序遍历(LRD): 首先遍历左子树,然后遍历右子树,最后访问根结点。

此外图的遍历也可以用在树上,包括:

  1. 广度优先遍历(层序遍历): 从根结点开开始逐层向下,从左到右遍历。

  2. 深度优先遍历: 从根结点出发沿左子树遍历到叶子结点再逐层向上向遍历右子树。

除此之外还有很多有特点的特殊二叉树:

  • 满二叉树:除最后一层以外,每一层上的所有结点都有两个子结点。
  1. 在满二叉树的第 KKK 层上有 2K−1 2 K − 1 2^{K-1} 个结点,且深度为 M M M 的满二叉树有 2M−12M−12M-1 个结点
  1. 完全二叉树:除最后一层以外,每一层上的结点数均达到最大值;在最后一层上只缺少右边的若干结点。
  1. 具有 N N N 个结点的完全二叉树的深度为 ⌊log2N⌋+1⌊log2N⌋+1\lfloor log_2 N \rfloor+1
  1. 完全二叉树总结点数为 N N N,则叶子结点数为⌈N/2⌉⌈N/2⌉\lceil N/2 \rceil

最常见的完全二叉树就是 **堆** 了。堆满足以下条件

  • 堆中某个结点的值总是不大于或不小于其父结点的值

  • 堆总是一棵完全二叉树

将根结点最大的堆叫做 最大堆大根堆 ,根结点最小的堆叫做 最小堆小根堆

堆具有以下基本操作:

  • 插入: 向堆中插入一个新元素

  • 获取: 获取当前堆顶元素的值

  • 删除: 删除堆顶元素

  • 包含: 判断堆中是否存在某个元素

哈希表

常用的哈希函数

  1. 直接寻址法: H(k) H ( k ) H(k) 是一个线性函数,如果该位置已经有值就向下寻找到第一个空的地方作为散列地址

  2. 平方取中法: 取 k k k 平方以后值的中间几位作为散列地址

  3. 数字分析法: 对于比较规律的 kkk 值,找出其差异较大的部分作为散列地址

  4. 折叠法: 将 k k k 分成很多部分,然后做模二和作为散列地址

  5. 留余数法: H(k)=kH(k)=kH(k)=k % p, p \leq m,其中 p p p 为素数,mmm 为表的长度

  6. 随机数法: 取键字的随机值作为散列地址,关键字长度时使用

实现映射的函数是哈希函数,简单的 hash 可能会发生碰撞(不同输入得到相同输出),为了防止碰撞,考虑以下方法:

  • 链地址法(拉链法): 当发生碰撞时,将发生碰撞的数据元素连接到同一个单链表中,而新元素插入到链表的前端

  • 线性探针法: 线性探针法地址增量 d∈D1={1,2,3,...,m−1} d ∈ D 1 = { 1 , 2 , 3 , . . . , m − 1 } d \in D_1 = \{1,2,3, ... , m-1\}, i i i 为探测次数,mmm 为表的长度,该方法遇到冲突地址会依次探测下一个地址( d=di+1 d = d i + 1 d = d_i + 1)直到有空的地址,若找不到空地址,则溢出。

平均查找长度

  • 线性探针法平均长度推算(其中 m m m 为表中数据长度,nnn 为表长度):

ASLs=∑mi=1dim A S L s = ∑ i = 1 m d i m ASL_s=\frac{\sum_{i=1}^m d_i}{m},查找成功

ASLu=∑ni=1din A S L u = ∑ i = 1 n d i n ASL_u=\frac{\sum_{i=1}^n d_i}{n},查找不成功

注:线性探针法查找成功时 di d i d_i 为每次放入元素时的地址增量,不成功时 di d i d_i 为在表长度内依次查找每个元素到下一个空地址的地址增量(索引在表长度内循环)

  • 链地址法平均长度推算(其中 k k k 为最长链长度,其中 mmm 为表中数据长度, n n n 为表长度):

ASLs=∑ki=1(当前级指针数量×当前级数)mASLs=∑i=1k(当前级指针数量×当前级数)mASL_s=\frac{\sum_{i=1}^k ({当前级指针数量} \times {当前级数})}{m},查找成功

ASLu=∑ni=1当前个位置链长度n A S L u = ∑ i = 1 n 当 前 个 位 置 链 长 度 n ASL_u=\frac{\sum_{i=1}^n {当前个位置链长度}}{n},查找不成功

哈希表相关特性

  • 线性探针法容易“聚集”,影响查找效率,而链地址法不会

  • 链地址法适应表长不确定情况

  • 装填因子(α)=哈希表中的记录数哈希表的长度 装 填 因 子 ( α ) = 哈 希 表 中 的 记 录 数 哈 希 表 的 长 度 装填因子(\alpha)=\frac{哈希表中的记录数}{哈希表的长度}

图有两种定义:

  • 二元组的定义:图 G G G 是一个有序二元组 (V,E)(V,E)(V,E),其中 V V V 称为顶集(Vertices Set),EEE 称为边集(Edges set), E E E 与 VVV 不相交。它们亦可写成 V(G) V ( G ) V(G) 和 E(G) E ( G ) E(G) 。 E E E 的元素都是二元组,用 (x,y)(x,y)(x,y) 表示,其中 x,y∈V x , y ∈ V x,y \in V

  • 三元组的定义: 图 G G G 是指一个三元组(V,E,I)(V,E,I)(V,E,I),其中 V V V 称为顶集,EEE 称为边集, E E E 与 VVV 不相交; I I I 称为关联函数,III 将 E E E 中的每一个元素映射到V×VV×VV \times V。如果 e e e 被映射到 (u,v)(u,v)(u,v),那么称边 e e e 连接顶点 u,vu,vu,v,而 u,v u , v u,v 则称作 e e e 的端点,u,vu,vu,v 此时关于 e e e 相邻。同时,若两条边 i,ji,ji,j 有一个公共顶点 u u u,则称 i,ji,ji,j 关于 u u u 相邻。

图的分类

图有不同的分类规则,具体如下:

分类1

- 有向图: 如果图中顶点之间关系不仅仅是连通与不连通,而且区分两边的顶点的出入(存在出边和入边),则为有向图。

- 无向图: 如果图中顶点之间关系仅仅是连通与不连通,而不区分两边顶点的出入(不存在出边和入边),则为无向图。

单图

分类2

- 有环图: 单向遍历回可以到已遍历的点,比如有环链表

- 无环图: 单向遍历不能回到已遍历的点,比如树

分类3

- 带权图: 图的具有边带有关于该边信息的权值,比如地图中两点间距离

- 无权图: 图的每个边都不具有有关于该边信息的权值,其仅表示是否连通

其他

- 单图: 一个图如果任意两顶点之间只有一条边且边集中不含环,则称为单图

图的表示采用邻接矩阵和类似树的形式(顶点指针域是个指针数组)的形式,其具有以下特点:

  • 无向图的邻接矩阵是对称矩阵

总结

前端资料汇总

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

  • 框架原理真的深入某一部分具体的代码和实现方式时,要多注意到细节,不要只能写出一个框架。

  • 算法方面很薄弱的,最好多刷一刷,不然影响你的工资和成功率😯

  • 在投递简历之前,最好通过各种渠道找到公司内部的人,先提前了解业务,也可以帮助后期优秀 offer 的决策。

  • 要勇于说不,对于某些 offer 待遇不满意、业务不喜欢,应该相信自己,不要因为当下没有更好的 offer 而投降,一份工作短则一年长则 N 年,为了幸福生活要慎重选择!!! 喜欢这篇文章文章的小伙伴们点赞+转发支持,你们的支持是我最大的动力!