一. 各种数据结构汇总
(内容深度只涉及到前端水平,毕竟我水平也一般)
1. 线性表
数据元素间是线性关系,数据元素在表中的位置只取决于 其序号
存在唯一的一个被称作“第一个”的数据元素和唯一的一 个被称作“最后一个”的数据元素
除第一个外,每个数据元素均只有一个前驱;除最后一个 外,每个数据元素均只有一个后继
(1)单链表
分为两种
第一种是 头指针指向首元节点,该节点和其他节点没有区别。每一个节点的next指针指向下一个节点。
第二种是头指针指向头节点,再指向首元节点。目的就是为了在头部添加数据时不需要对头指针进行特殊处理。(简单来讲,添加元素就是在对应索引节点的next指针后指向该新加的元素,并且让新加的元素next指向对应索引+1的节点,但是处理头部添加不同,因为对于第一种链表头指针没有next,但是对于第二种就可以一视同仁)。
两种单链表判空的方式也不同,
第一种就是first=> null
第二种就是first=>节点内容为null
(2)单循环列表
应该很好理解,就是尾部next又指到了头部
单循环链表是单链表的变形。
单循环链表最后一个结点的后继指针不为 0 (NULL),而是指向了表的前端。
为简化操作,在单循环链表中往往加入表头结点。
单循环链表的特点是:只要知道表中某一结点的 地址,就可搜寻到所有其他结点的地址。
(3)双向链表
也很好理解,每个节点不仅有next,还有prev指向前一个节点。
2. 栈与队列
(1)栈 (核心就是先入后出)
先入的在栈底,先出的在栈顶。
js的执行调用就是用的栈,对非引用类型的数据存储也是用的栈。一个栈独占一组地址连续的存储单元。通常0下标端设为栈底,栈顶指针top值为-1,表示空栈。
进栈
出栈
细节就是top指针会在入栈时先加后入,在出栈时先减后出。
一个简单计算题(建议直接背)
n个元素依次入栈,可得到多少个合法的出栈序列 (2n)!/[(n+1)!*n! ]
(2)链栈
栈的应用在编码过程中最主要的就是递归。递归就不详细解释了(函数调用其本身,但是需要合适的中止条件)。
(3)队列(核心就是先排先到,先进先出)
进队和出队
判空 q−>front=q−>rear
(4)链队列
3. 树的结构及基本操作(基本一问数据结构就爱问这玩意)
定义:
树是由n(n≥0)个结点组成的有限集合T,非空树满足: 1)有一个称之为根(root)的结点。 2)除根以外的其余结点被分成m(0≤m
术语:
结点的度 结点拥有的子树数目。
叶子(终端)结点 度为0的结点。
分支(非终端)结点 度不为0的结点。
树的度 树的各结点度的最大值。
内部结点 除根结点之外的分支结点。
双亲与孩子(父与子)结点 结点的子树的根称为该 结点的孩子;该结点称为孩子的双亲。
兄弟 属于同一双亲的孩子。
结点的祖先 从根到该结点所经分支上的所有结点。
结点的子孙 该结点为根的子树中的任一结点。
结点的层次 表示该结点在树中的相对位置。根为 第一层,其它的结点依次下推;若某结点在第L层 上,则其孩子在第L+1层上。
堂兄弟 双亲在同一层的结点互为堂兄弟。
树的深(高)度 树中结点的最大层次。
有序树 树中各结点的子树从左至右是有次序的, 不能互换。否则,称为无序树。
路径长度 从树中某结点Ni 出发,能够“自上而下 地”通过树中结点到达结点Nj ,则称Ni 到Nj 存在一 条路径,路径长度等于这两个结点之间的分支数。
树的路径长度 从根到每个结点的路径长度之和。
森林 是m(m≥0)棵互不相交的树的集合。
(1) 二叉树的定义与性质
二叉树的定义
二叉树是n(n≥0)个结点的有限集合,它或为 空树(n=0),或由一个根结点和两棵互不相交的左子树和 右子树的二叉树组成。
二叉树的遍历方法(常考)
简单概括:先序(先根) 根 左 右
中序(中根) 左 根 右
后序(后根) 根 左 右
层遍历 一层一层读就行
二叉树的先序、中序、后序遍历超详解_代码敲上天...的博客-CSDN博客_二叉树的先序,中序,后序遍历
(2)哈夫曼树
构造方式
这个解释一下,其实也不难。首先需要找到两个最小权值。min1.min2分别作为左右节点。然后在原本的数组中去掉这俩值,再找两个最小的min3.min4,组成另一棵树的左右节点。(注意每次使用完一个权值后就从数组中删掉,后面也是)。然后开始分别操作这两棵树 A,B(就是a做了什么,b跟着做一下什么)。找最小值min5,当作左节点,而数A根节点当中右节点,形成数A1,B也进行一样的操作,找到最小值min6,作为左节点,与数B的根节点构成新的数B2,依次进行下去,最后两颗树 An和Bn 做为左节点和右节点,形成最终的哈夫曼树。
4.图(因为基本不考面试,就不做总结了,感兴趣可以自己学一下)
5.查找和排序
(1)二分查找(最常考)
只针对有序表。原理如图
前端实现
/**
*
* @param {Number[]} arr
* @param {Number} target
*/
// 此处以升序表为例 返回目标索引
function HalfSearch(arr, target) {
let left = 0
let right = arr.length
let mid = 0
while (left+1!== right) {
mid = Math.floor((left + right) / 2)
if (arr[mid] < target) {
left = mid
} else if (arr[mid] > target) {
right = mid
} else {
return mid
}
}
return -1
}
console.log(HalfSearch([1, 5, 8, 22, 34, 56, 90], 22));
时间复杂度O(h)=O(log2n)
(2)哈希表
在记录的存储地址和它的关键字之间建立一个确定的 对应关系;这样,理想状态不经过比较,一次存取就能得 到所查元素。
哈希函数的基本构造方法(也挺常考)
1. 直接定址法
2. 数字分析法/基数转换法
3. 平方取中法
4. 随机数法
5. 折叠法
6. 除留余数法
(素数一般指质数。质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。反正我当时忘了~)
(3)排序
1. 冒泡排序(O(n²))
/**
*
* @param {Number[]} arr
*/
function BubbleSort(arr){
for(let i=0;i<arr.length;i++){
for(let j=i+1;j<arr.length;j++){
if(arr[j]<arr[i]){
let temp=arr[j]
arr[j]=arr[i]
arr[i]=temp
}
}
}
return arr
}
console.log(BubbleSort([2,5,78,4,1]));
2. 插入排序