整理了一些面试中常用的算法题,正在不断更新中,大家在面试和工作中有常遇到的算法题,,欢迎评论区共享
示例: 输入: [“flower”,“flow”,“flight”]
输出: “fl”
function getMaxAndIndex( arr ){
var firstStr = arr[0], result = ''
if(!arr.length) return result
for(let i=0;i<firstStr.length;i++){
for(let j=1;j<arr.length;j++){
if(firstStr[i]!=arr[j][i]){
return result
}
}
result += firstStr[i]
}
return result
}
浅拷贝
a. 对象的合并 Object.assign(),第一个参数必须是个空对象
var obj1 = {a: 1, b: 2};
var obj2 = Object.assign({}, obj1);
var obj1 = {a: 1, b: 2};
var obj2 = {...obj1};
深度拷贝
a. 数组或对象深拷贝(利用循环和递归)
function deepClone(obj) {
let objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj === 'object') {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === 'object') {
objClone[key] = deepClone(obj[key])
} else {
objClone[key] = obj[key]
}
}
}
}
return objClone;
}
let a = [20,8,6,19],
b = deepClone(a);
a[1] = 2;
console.log(a, b)
b. 利用jQuery的$.extend方法
//第一个参数不传(false是不能够显示的写出来的)默认为false,是浅拷贝。传true为深拷贝。
$.extend(true,object1, object2)
//newObject 即为深拷贝出来的对象
var newObject = $.extend(true , {} , object);
c. JSON.parse( JSON.stringify())
//通过js的内置对象JSON来进行数组对象的深拷贝
function deepClone(obj) {
var _obj = JSON.stringify(obj),
objClone = JSON.parse(_obj);
return objClone;
}
class EventEmitter {
constructor() {
this.events = {}
}
on(eventName, fn) {
if (!this.events[eventName]) {
this.events[eventName] = []
}
this.events[eventName].push(fn)
}
emit(eventName, ...rest) {
this.events[eventName] && this.events[eventName].forEach(f => f.apply(this, rest))
}
// remove(eventName, fn) {
// if (this.events[eventName]) {
// this.events[eventName].filter(f => f !== fn)
// }
// }
// once(eventName, callback) {
// const fn = () => {
// callback();
// this.remove(eventName, fn)
// }
// this.on(eventName, fn)
// }
}
const event = new EventEmitter()
const handle = (...pyload) => console.log(pyload)
event.on('click', handle)
// event.emit('click', 100, 200, 300, 100)
// event.remove('click', handle)
event.once('dbclick', function () {
console.log('click')
})
event.emit('dbclick', 100);
示例: 输入: nums = [5,7,7,8,8,10], target = 8, 输出:[3,4]
输入: nums = [5,7,7,8,8,10], target = 6, 输出:[-1,-1]
思路:1. 开始位置 indexOf
2. 结束位置:翻转数组,求arr.length-(indexOf+1)
function pop(arr, target){
const tmp = [];
tmp[0] = arr.indexOf(target);
tmp[1] = arr.reverse().indexOf(target);
if(tmp[1] !== -1){
tmp[1] = arr.length - tmp[1]-1;
}
console.log(tmp);
return tmp;
}
const nums = [5,7,7,8,8,10];
const target = 8;
pop(nums, target)
function PromiseAll(promise){
return new Promise((resolve, reject) => {
let index=0;
let result = [];
if(promise.length === 0) resolve(result)
for(let i = 0; i<promise.length; i++){
Promise.resolve(promise[i]).then(data => {
result[i] = data;
if(++index === promise.length){
resolve(result)
}
}, err => {
reject(err)
})
}
})
}
let p1 = new Promise((resolve) => {
resolve('成功了')
})
let p2 = new Promise((resolve) => {
resolve('success')
})
PromiseAll([p1, p2]).then((result) => {
console.log(result) //['成功了', 'success']
}).catch((error) => {
console.log(error)
})
输出结果:(2) ["成功了", "success"]
回文是指把相同的词汇或句子,在下文中调换位置或颠倒过来,产生首尾回环的情趣,叫做回文,也叫回环。比如 mamam redivider .
很多人拿到这样的题目非常容易想到用for 将字符串颠倒字母顺序然后匹配就行了。其实重要的考察的就是对于reverse的实现。其实我们可以利用现成的函数,将字符串转换成数组,这个思路很重要,我们可以拥有更多的自由度去进行字符串的一些操作。
function checkPalindrom(str) {
return str == str.split('').reverse().join('');
}
比如输入: [1,13,24,11,11,14,1,2]
输出: [1,13,24,11,14,2]
需要去掉重复的11 和 1 这两个元素。
let unique = function(arr) {
let hashTable = {};
let data = [];
for(let i=0,l=arr.length;i
输入 : afjghdfraaaasdenas
输出 : a
前面出现过去重的算法,这里需要是统计重复次数。
function findMaxDuplicateChar(str) {
if(str.length == 1) {
return str;
}
let charObj = {};
for(let i=0;i= maxValue) {
maxChar = k;
maxValue = charObj[k];
}
}
return maxChar;
}
另一种写法:
function getMaxAndIndex( arr ){
if(!arr.length) return;
if(arr.length==1) return 1;
var h={}, maxnum=0,maxel='';
for(var i=0;imaxnum){
maxnum=h[a]
maxel=a
}
}
return '出现次数最多的元素为:'+maxel+'出现次数:'+maxnum
}
冒泡排序
思路:它重复地走访要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们位置交换过来.
function bubbleSort(arr) {
for(let i = 0,l=arr.length;i<l-1;i++) {
for(let j=0;j<arr.length-1-i;j++) {
if(arr[j]>arr[j+1]) {
[arr[j],arr[j+1]] = [arr[j+1], arr[j]]
}
}
}
return arr;
}
快速排序
思路:选择一个元素作为基数(通常是第一个元素),把比基数小的元素放到它左边,比基数大的元素放到它右边(相当于二分),再不断递归基数左右两边的序列。
function quickSort(arr){
if(arr.length<=1)return arr;
let leftArr=[],rightArr=[];
for(let i=1;i< arr.length; i++){
if(arr[i]<arr[0]){
leftArr.push(arr[i])
} else {
rightArr.push(arr[i])
}
}
return [].concat(quickSort(leftArr),arr[0],quickSort(rightArr))
}
选择排序
思路:和冒泡排序相似,区别在于选择排序是将每一个元素和它后面的元素进行比较和交换。
function selectSort(arr){
for(let i=0;i< arr.length; i++){
for(let j=i+1;j< arr.length; j++){
if(arr[i]>arr[j])
[arr[i],arr[j]] = [arr[j], arr[i]]
}
}
return arr;
}
插入排序
思路:依然要通过两层循环,外循环便利每个数组项,内循环从外循环的数组项(i)开始往前遍历,如果当前数组项比前一个小,则与前一个调换位置,这样一直循环重复,数组就逐渐归位了;
function insertSort(arr){
for(let i=1;i< arr.length; i++){
for(j=i;j>0;j--){
if(arr[j]<arr[j-1]){
[arr[j], arr[j-1]]= [arr[j-1], arr[j]]
}
}
}
return arr
}
希尔排序
思路:希尔排序将插入排序作为它的子进程,它的核心是一个叫步长(gap)的概念,这个步长可以理解为一个类似疏密程度的概念。它共有3层循环,外层是以步长为循环,一般取数组的一半,循环一次再除一半,中层和里层就是插入排序的操作,不过不是跟前一项比,是跟当前索引减去步长后的那一项比。说到这里就可以理解为什么我说步长是类似疏密程度的概念,当步长不断除于2,变得越来越小,直到零停止循环,这个过程中,插入排序的比较项间越来越近,逐渐数组被排列出来。
function shellSort(arr){
let gap = Math.floor(arr.length/2);
while(gap>0){
for(let i=gap;i< arr.length; i++){
for(let j=i;j>0;j-=gap ){
if(arr[j]<arr[j-gap])
[arr[j], arr[j-gap]] = [arr[j-gap],arr[j]]
}
}
gap = Math.floor(gap/2)
}
return arr
}
桶排序
思路:取 n 个桶,根据数组的最大值和最小值确认每个桶存放的数的区间,将数组元素插入到相应的桶里,最后再合并各个桶。
function bucketSort(arr){
let data = Array.from({length:10}).fill(0);
let newArr = [];
arr.forEach(el=>data[el]++)
for(let i=0;i< data.length; i++){
for(let j=0;j< data[i]; j++){
newArr.push(i)
}
}
return newArr;
}
let tree = { // 树结构
id: 1,
left: { id: 2, left: { id: 4}, right: { id: 5 }},
right: { id: 3, left: { id: 6}, right: { id: 7 }}
}
// 1.根左右 前序递归 1245367
function rootLR(tree) {
console.log("===", tree.id);
if (tree.left) rootLR(tree.left);
if (tree.right) rootLR(tree.right);
}
// 2.左跟右 中序遍历 4251637
function LRootR(tree) {
if (tree.left) LRootR(tree.left);
console.log("===", tree.id);
if (tree.right) LRootR(tree.right);
}
// 3.左右跟 后序遍历 4526731
function LRRoot(tree) {
if (tree.left) LRRoot(tree.left);
if (tree.right) LRRoot(tree.right);
console.log("===", tree.id);
}
// 4.根左右 前序非递归 1245367
function w_rootR(tree) {
let res = [],
arr = [tree]
while (arr.length) {
let tmp = arr.pop();
res.push(tmp.id);
if (tmp.right) arr.push(tmp.right)
if (tmp.left) arr.push(tmp.left)
}
console.log(res);
return res
}
//5 左根右 中序非递归 4251637
function w_LRootR(tree) {
let arr = [],
res = [];
while (true) {
while (tree) {
arr.push(tree);
tree = tree.left;
}
if (!arr.length) break;
let tmp = arr.pop();
res.push(tmp.id);
tree = tmp.right;
}
return res;
}
// 6左右跟 后序非递归遍历 4526731
function w_LRRoot(tree) {
let arr = [tree],
res = [];
while (arr.length) {
let tmp = arr.pop();
res.push(tmp.id);
if (tmp.left) arr.push(tmp.left)
if (tmp.right) arr.push(tmp.right)
}
return res.reverse();
}
输入 a = 2, b = 4 输出 a = 4, b =2
主要是利用 + - 去进行运算,类似 a = a + ( b - a) 实际上等同于最后 的 a = b;
function swap(a , b) {
b = b - a;
a = a + b;
b = a - b;
return [a,b];
}
斐波那契数列,又称黄金分割数列,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列主要考察递归的调用。我们一般都知道定义
fibo[i] = fibo[i-1]+fibo[i-2];
生成斐波那契数组的方法
function getFibonacci(n) {
var fibarr = [];
var i = 0;
while(i
剩余的工作就是利用canvas arc方法进行曲线绘制了
输入 [10,5,11,7,8,9]
输出 6
function getMaxProfit(arr){
var max=arr[0];
var min=arr[0];
var res=0;
for (var i = 1; i < arr.length; i++) {
if(arr[i]>max){
max=arr[i];
}
if(arr[i]
比如给定 长度 8 输出 4ldkfg9j
function getMaxStr(n) {
let str = 'abcdefghijklmnopqrstuvwxyz9876543210';
var tmp=''
for(let i=0;i
自己实现一个函数,查找某个DOM节点下面的包含某个class的所有DOM节点?不允许使用原生提供的 getElementsByClassName querySelectorAll 等原生提供DOM查找函数。
原生JS实现getElementsByClassName()
- 0
- 1
- 2
- 3
- 4
- 5
当我们需要获取指定范围(min,max)内的整数的时候,下面的代码非常适合。
function getRadomNum(min,max){
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function getRadomFromArr(arr){
return arr[Math.floor(Math.random()*arr.length)];
}
var arr=[]
for(var i=1;i<=100;i++){
arr.push(i)
}
console.log(arr)
var arr = [1,2,3,4,5,6,7,'a','dsfs',8,9,'v'];
arr.sort(function(){return Math.random()-0.5});
//注意对象必须是以下格式的才可以通过此方式转化为数组
//获取的DOM集合,以及函数的arguments也可以通过此方式转化为数组
var obj={
0:'qian',
1:'long',
2:'chu',
3:'tian',
length:4
}
var _slice=[].slice;
var objArr=_slice.call(obj);
function isArray(obj){
return Object.prototype.toString.call(obj) === '[object Array]' ;
}
该方法适合一维或者多维数组求最大最小值的情况
function maxAndMin(arr){
return {
max:Math.max.apply(null,arr.join(',').split(',')),
min:Math.min.apply(null,arr.join(',').split(','))
}
}
//方式一 通过将长度设置为0
var arr=[1,2,3,4,5];
arr.length=0;
//方式二 通过splice方法
var arr=[1,2,3,4,5];
arr.splice(0,arr.length);
//方式三 通过将空数组 [] 赋值给数组(严格意义来说这只是将ary重新赋值为空数组,之前的数组如果没有引用在指向它将等待垃圾回收。)
var arr=[1,2,3,4,5];
arr=[];
var num =4.345678;
num = num.toFixed(4); // 4.3457 第四位小数位以四舍五入计算
function getMaxAndIndex( arr ){
var obj = {};
arr.forEach(function(item,index){
if(!obj[item]){
obj[item]= {indexs: [index]}
}else{
obj[item]['indexs'].push(index);
}
});
var num=0;//记录出现次数最大值
var str='';//记录出现次数最多的字符
var reArr;//返回最大值的位置数组
for(var attr in obj){
var temp=obj[attr]['indexs'];
if(temp.length>num){
num=temp.length;
str=attr;
reArr=temp;
}
}
return {
maxStr:str,
indexs:reArr
}
}
##手写 call / apply / bind
// 手写bind
Function.prototype.bind = function (context, ...bindments) {
context = context || window;
const func = this;
return function F(...callments) {
let args = bindments.concat(callments);
if (this instanceof F) return new func(...args)
return func.call(context, ...args)
}
}
// call
Function.prototype.call = function (context, ...callargs) {
context = context || window;
context.func = this;
if (typeof context.func !== 'function') {
throw new TypeError("必须是函数调用call")
}
let res = context.func(...callargs);
delete context.func;
return res;
}
// apply
Function.prototype.apply = function (context, arr) {
context = context || window;
context.func = this;
if (typeof context.func !== 'function') {
throw new TypeError("必须是函数调用apply")
}
let res = context.func(...arr)
delete context.func;
return res;
}