1、深度优先和广度优先的区别
1) 二叉树的深度优先遍历的非递归的通用做法是采用栈,广度优先遍历的非递归的通用做法是采用队列。
2) 深度优先遍历:对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次。要特别注意的是,二叉树的深度优先遍历比较特殊,可以细分为先序遍历、中序遍历、后序遍历。具体说明如下:
先序遍历:对任一子树,先访问根,然后遍历其左子树,最后遍历其右子树。
中序遍历:对任一子树,先遍历其左子树,然后访问根,最后遍历其右子树。
后序遍历:对任一子树,先遍历其左子树,然后遍历其右子树,最后访问根。
广度优先遍历:又叫层次遍历,从上往下对每一层依次访问,在每一层中,从左往右(也可以从右往左)访问结点,访问完一层就进入下一层,直到没有结点可以访问为止。
3)深度优先搜素算法:不全部保留结点,占用空间少;有回溯操作(即有入栈、出栈操作),运行速度慢。
广度优先搜索算法:保留全部结点,占用空间大; 无回溯操作(即无入栈、出栈操作),运行速度快。
2、数组和链表的区别
1) 数组
是将元素在内存中连续存放,由于每个元素占用内存相同,可以通过下标迅速访问数组中任何元素。
数组必须事先定义固定的长度(元素个数),不能适应数据动态地增减的情况。当数据增加时,可能超出原先定义的元素个数;当数据减少时,造成内存浪费;数组可以根据下标直接存取。
数组的存储区间是连续的,从栈中分配空间,占用内存比较大;插入数据和删除数据效率低。
插入数据时,待插入位置的元素和他后面的所有元素都需要向后搬移
删除数据时,待删除位置后面的所有元素都需要向前搬移。
查找速度快,时间复杂度是o(1); 从头部删除、从头部插入的效率低,时间复杂度是o(n)。
伪数组和数组的区别
伪数组的特点:a、具有length属性;b、按索引方式存储数组;c、不具有数组的方法。
常见的伪数组:a、函数内部的arguments;b、DOM 对象列表(比如通过 document.getElementsByTags 得到的列表);c、Query 对象(比如 $("div") )。
伪数组转成数组的方法:a、Array.prototype.slice.call(伪数组名);b、Array.from();c、 ...运算符如: let newArr = [...伪数组名];。
2)链表
是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
链表动态地进行存储分配,链表从堆中分配空间,空间是分散的,不需要连续,空间不需要提前指定大小,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。(数组中插入、删除数据项时,需要移动其它数据项,非常繁琐)链表必须根据next指针找到下一个元素。
查找速度慢,时间复杂度是o(n);任意位置插入元素和删除元素时间效率较高,时间复杂度是o(1)。
比较
数组的底层是ArrayList,链表的底层是Hashmap。
3、哈希
哈希是通过对数据进行再压缩,提高效率的一种解决方法。但由于通过哈希函数产生的哈希值是有限的,而数据可能比较多,导致经过哈希函数处理后仍然有不同的数据对应相同的值。这时候就产生了哈希冲突。
1)哈希冲突的影响因素
装填因子(装填因子=数据总数 / 哈希表长)、哈希函数、处理冲突的方法。
2)哈希冲突的四种方法
a、开放地址方法
线性探测:按顺序决定值时,如果某数据的值已经存在,则在原来值的基础上往后加一个单位,直至不发生哈希冲突。
再平方探测:按顺序决定值时,如果某数据的值已经存在,则在原来值的基础上先加1的平方个单位,若仍然存在则减1的平方个单位。随之是2的平方,3的平方等等。直至不发生哈希冲突。
伪随机探测:按顺序决定值时,如果某数据已经存在,通过随机函数随机生成一个数,在原来值的基础上加上随机数,直至不发生哈希冲突。
b、拉链法(HashMap的哈希冲突解决方法)
对于相同的值,使用链表进行连接。使用数组存储每一个链表。
优点:拉链法处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,因此平均查找长度较短;
由于拉链法中各链表上的结点空间是动态申请的,故它更适合于造表前无法确定表长的情况;
开放定址法为减少冲突,要求装填因子α较小,故当结点规模较大时会浪费很多空间。而拉链法中可取α≥1,且结点较大时,拉链法中增加的指针域可忽略不计,因此节省空间;
在用拉链法构造的散列表中,删除结点的操作易于实现。只要简单地删去链表上相应的结点即可。
缺点:指针占用较大空间时,会造成空间浪费,若空间用于增大散列表规模进而提高开放地址法的效率。
c、再哈希法
对于冲突的哈希值再次进行哈希函数去散列处理,直至没有哈希冲突。
4、排序算法
在平均情况下,快速排序最快;
在最好情况下,插入排序和起泡排序最快;
在最坏情况下,堆排序和归并排序最快。
5、Map 和 Object 的区别
1)键值key不同
Object 的 key 必须是简单数据类型(整数,字符串或者是 symbol);而在Map的键可以是任意值,包括函数、对象、基本类型。
2)元素的顺序
Map 中的键值是有序的,而添加到对象中的键则不是。因此,当对它进行遍历时,Map 对象是按插入的顺序返回键值。
3)操作方法
a、新建:Object 支持以下几种方法;Map 仅支持一种构建方法:new Map();
b、新增数据(重复则覆盖):Map 可以使用 set(key, val) 操作;Object 新增一个属性可以使用:obj['key']=value或obj.key=value。
c、数据访问:Map 想要访问元素,可以使用 Map 本身的原生方法get (key) ;Object 可以通过.和[ ]来访问。
d、删除数据:Map 有原生的 delete 方法来删除元素;全部删除map.clear();
Object 中没有原生的删除方法,可以使用:deleteobj.id;或obj.id=undefined。
e、获取size:Map 自身有 size 属性,可以自己维持 size 的变化。Object 则需要借助Object.keys()来计算:Object.keys(obj).length。
f、可访问性:判断某个元素是否在 Map 中可以使用has (key);判断某个元素是不是在 Object 中可以用obj.id===undefined;// 或者'id'inobj;
另外Object 可以使用Object.prototype.hasOwnProperty()来判断某个key是否是这个对象本身的属性,从原型链继承的属性不包括在内。
g、Iterating 迭代:Map 自身支持迭代,Object 不支持。Map可直接进行迭代,而Object的迭代需要先获取它的键数组,然后再进行迭代。
何时使用 Map ,何时使用 Object?
a、当所要存储的是简单数据类型,并且 key 都为字符串或者整数或者 Symbol 的时候,优先使用 Object ,因为Object可以使用 字符变量 的方式创建,更加高效;
Map 在存储大量元素的时候性能表现更好,特别是在代码执行时不能确定 key 的类型的情况。
b、在涉及频繁增删键值对的场景下应该使用 Map。
c、JSON 直接支持 Object,但不支持 Map。
6、二叉搜索树
二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根节点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。
删除节点:
情况1:删除的当前节点无左右孩子节点,那么就直接将当前要删除的节点设置为null即可。
情况2:删除的当前节点无左孩子节点,有右孩子节点,那么就将当前要删除的节点设置为右孩子节点即可。
情况3:删除的当前节点无右孩子节点,有左孩子节点,那么就将当前要删除的节点设置为左孩子节点即可。
情况4:删除的当前节点有右孩子节点也有左孩子节点,那么就选出右孩子树中最小的点,设置为当前要删除的节点即可。这种方式既可以保证二叉排序树的性质,由于右孩子树中最小的点,无左孩子节点(如果有左孩子节点,那么就不符合二叉树性质了)。