综述:数据结构和算法对于前端人员来说很重要,特别是一线大厂,将常见的问题做一个总结,巩固自己的知识
前端必须了解的数据结构和算法知识:
- 算法的基本概念
- 时间复杂度和空间复杂度的计算
- 线性表数据结构
- 栈和队列数据结构
- 串数据结构
- 树数据结构
- 图数据结构
两种常见的算法问题(各自有非常多的实现方式):
- 查找算法
- 排序算法
1.去重的方法有哪些?越多越好,扩宽自己的知识面?
-
创建新数组去重法(性能不是非常的好)
var arr=[1,1,1,2,3];
var newArr=new Array();
for(var i in arr){
if(newArr.indexOf(arr[i])==-1){ //如果新数组中不存在这个元素
newArr.push(arr[i]);
}
}
console.log(newArr);
-
使用es6中的集合Set + Array.from()方法实现去重
Array.from()方法就是将一个类数组对象或者可遍历对象转换成一个真正的数组,set对象并不是一个完整的数组;
var arr=new Set([1,1,1,2,3]);
var newArr=Array.from(arr);
console.log(newArr); //[1,2,3]
-
es6中的 …[拓展运算符] +Set
拓展运算符:扩展运算符( spread )是三个点(...),将一个数组转为用逗号分隔的参数序列。
var arr=[...new Set([1,1,1,2,3])];
console.log(arr); //1,2,3
var arr=[new Set([1,1,1,2,3])];
console.log(arr); //1,2,3
-
将数组中的值以对象属性的方式传给一个对象,如果对象中没有这个属性则将其添加到对象中,仔细分析代码中的问题
//在数组的原型上添加方法
Array.prototype.unique=function(){
var arr2=[];
var obj={};
for(var i in this){ //这样写返回的数组中就是6个元素,包括函数自身
/*if(!obj.this[i]){
arr2.push(this[i]);
obj.this[i]=1; //为什么通过这种方式是会出错的
}*/
if(!obj[this[i]]){
arr2.push(this[i]);
obj[this[i]]=12; //如果不给默认值有啥影响
}
}
for(var i=0;i<this.length;i++){ //这里返回的就是5个元素
if(!obj[this[i]]){
arr2.push(this[i]);
obj[this[i]]=12; //如果给值为0或者不给值,则就不会满足!obj[this[i]]这个条件;
}
}
return arr2;
};
var arr=[1,2,1,1,3];
console.log(arr.unique()); //[1,2,3];
后来在一个网页中查到,原来对象访问属性有两种方式。有一个对象Obj = {"Name":"Langshen","AGE":"28"}
-
用点访问,
Obj.Name
; -
用中括号访问,
Obj["Name"]
; -
上述两种方式得到的结果都是属性
Name
的值Langshen
;
上面2中访问的方式可以获得同样的效果,是因为对象的属性是已经存在的,这里在延伸一下上述两种方式为对象新建属性时用法的区别。
首先,回到文章的开头。代码o[char]
表示的是o对象的一个属性值。那能不能用o.char
表示呢?答案是否定的。
详细内容参考链接:https://blog.csdn.net/shuren1991/article/details/67639250
2.排序相关方法汇总?快速排序,冒泡排序,选择排序算法非常常见,必须要熟悉其实现思想,十种常见的排序算法实现;
冒泡排序算法实现,时间复杂度为O(n²),时间复杂度是指计算方法执行的次数,因为这里做了两次的遍历:
function bubbleSort(arr) {
var len = arr.length;
for (var i = 0; i < len - 1; i++) {
for (var j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j+1]) { // 相邻元素两两对比
var temp = arr[j+1]; // 元素交换
arr[j+1] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
快速排序算法实现:
参考链接:https://zhuanlan.zhihu.com/p/45418184
/* 快速排序(二分法)
* 1.先从数列中取出一个数作为基准数。
* 2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
* 3.再对左右区间重复第二步,直到各区间只有一个数。
*/
let arr3 = [62,12,23,4,443,5,6,14,21,45,3,75,68,11,22,4,43,56,1,2,84,3,565,1,25,4,3,566,1,276,4,37,56,1,2,4,3,565,1,2,4,377,5,40,49,28]
console.time("time_quickSort")
let nn = 0;
function quickSort(arr){
if(arr.length<2){return arr};
var left=[],right=[],mid=arr.splice(Math.floor(arr.length/2),1);
for(var i=0;i<arr.length;i++){
if(arr[i]<mid){
left.push(arr[i]);
}else {
right.push(arr[i])
}
nn++;
}
return [...quickSort(left),...mid,...quickSort(right)]
}
console.log(quickSort(arr3))
console.log(nn)
console.timeEnd("time_quickSort")
选择排序:
/*
* 选择排序
* 在一个长度为N的无序数组中,
* 在第一趟遍历N个数据,找出其中最小的数值与第一个元素交换,
* 第二趟遍历剩下的N-1个数据,找出其中最小的数值与第二个元素交换,
* ……
* 第N-1趟遍历剩下的2个数据,找出其中最小的数值与第N-1个元素交换,
* 至此选择排序完成。
*/
let arr2 = [62,12,23,4,443,5,6,14,21,45,3,75,68,11,22,4,43,56,1,2,84,3,565,1,25,4,3,566,1,276,4,37,56,1,2,4,3,565,1,2,4,377,5,40,49,28]
console.time("time_selectSort")
function selectSort(arr){
let n = 0;
let newArr = arr;//拷贝一份做修改
for(let i=0;i<newArr.length;i++){
minIndex = i;
for(let j=i+1;j<newArr.length;j++){
if(newArr[j]<newArr[i]){
n++;
let min = newArr[j];
newArr[j] = newArr[i];
newArr[i] = min;
}
}
}
console.log(n)
return newArr;
}
console.log(selectSort(arr2))
console.timeEnd("time_selectSort")
3.将关系型数据库的标准数据结构转化成树状机构的算法?trie树的使用
4.动态规划问题
- 在数组中找到最大和的子数组问题
- 将一个数组分割成N分,使N份 之间的和之间的差最小
- 斐波那契数列的使用,使用递归和使用动态规划方法之间性能是完全不同的
/* 使用递归的方法实现斐波那契数列的方法,时间复杂度为 */
var a=0;
function feibo(n){
if(n<2){
return 1;
}else{
++a
console.log(a)
return feibo(n-1)+feibo(n-2);
}
}
console.log(feibo(10))
/* 使用动态规划的思想,每次存储上次的计算结果,这样时间复杂度为O(n-2) 当n=10时,是8次*/
function dynFib(n) {
let val = [];
for(let i = 0; i <= n; ++i){
val[i]=0;
}
if(n ===1 || n === 2){
return 1;
}
else {
//在数组中对计算的结果进行缓存,避免多次计算,降低算法的时间复杂度
val[1] =1;
val[2] = 2;
for(let i = 3; i <= n; ++i){
console.log(n)
val[i] = val [i-1] +val[i-2] ;
}
}
return val[n-1]
}
console.log(dynFib(10))
5.主要的数据结构有?
数组,集合(set),字典(map),链表,哈希表,队列,栈,树,图
6.主要的算法有?
十大排序算法,搜索算法。