熬夜肝了这一份来自牛客,LeetCode,剑指 Offer大佬整理的前端常用算法面试题.pdf,你也能进大厂

Posted 码民的飘柔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了熬夜肝了这一份来自牛客,LeetCode,剑指 Offer大佬整理的前端常用算法面试题.pdf,你也能进大厂相关的知识,希望对你有一定的参考价值。

前言

1.2021年已过半,“金九银十”笔试即将要开始,整理一些算法题一起学习。
2.我统一使用javascript(V8/Node)解答,都已经调试通过。
3.一起加油!一起进步!

1.排序

以下两个函数是排序中会用到的通用函数,就不一一写了

function checkArray(array) {
  if (!array || array.length <= 2) return }
 function swap(array, left, right) {
  let rightValue = array[right]
  array[right] = array[left]
  array[left] = rightValue 
}

2.时间复杂度

通常使用最差的时间复杂度来衡量一个算法的好坏。
常数时间 O(1) 代表这个操作和数据量没关系,是一个固定时间的操作,比如说四则运算。
对于一个算法来说,可能会计算出如下操作次数 aN + 1 , N 代表数据量。那么该算法的时间复杂度就
是 O(N)。因为我们在计算时间复杂度的时候,数据量通常是非常大的,这时候低阶项和常数项可以忽略
不计。
当然可能会出现两个算法都是 O(N) 的时间复杂度,那么对比两个算法的好坏就要通过对比低阶项和常
数项了。

3.位运算

位运算在算法中很有用,速度可以比四则运算快很多。
在学习位运算之前应该知道十进制如何转二进制,二进制如何转十进制。这里说明下简单的计算方式

  • 十进制 33 可以看成是 32 + 1 ,并且 33 应该是六位二进制的(因为 33 近似 32 ,而 32 是 2
    的五次方,所以是六位),那么 十进制 33 就是 100001 ,只要是 2 的次方,那么就是 1否则都为 0
  • 那么二进制 100001 同理,首位是 2^5 ,末位是 2^0 ,相加得出 33

4.左移 <<

10 << 1 // -> 20

左移就是将二进制全部往左移动, 10 在二进制中表示为 1010 ,左移一位后变成 10100 ,转换为十
进制也就是 20,所以基本可以把左移看成以下公式 a * (2 ^ b)

5.算数右移 >>

10 >> 1 // -> 5

算数右移就是将二进制全部往右移动并去除多余的右边, 10 在二进制中表示为 1010 ,右移一位后变
成 101 ,转换为十进制也就是 5,所以基本可以把右移看成以下公式 int v = a / (2 ^ b)
右移很好用,比如可以用在二分算法中取中间值

13 >> 1 // -> 6

6.最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        # 动态规划
        res = cur = nums[0]
        for i in range(1, len(nums)):
            cur = max(nums[i], cur + nums[i])
            res = max(res, cur)
        return res

动态规划。时间复杂度O(n), 空间复杂度 O(1)。

7.链表

面试题:反转单向链表
题目需要将一个单向链表反转。思路很简单,使用三个变量分别表示当前节点和当前节点的前后节点,虽然这题很简单,但是却是一道面试常考题

以下是实现该算法的代码

var reverseList = function(head)
 { 
 	// 判断下变量边界问题
  	if (!head || !head.next) return head 
	// 初始设置为空,因为第一个节点反转后就是尾部,尾部节点指向 null
  	let pre = null
  	let current = head 
  	let next 
  	// 判断当前节点是否为空 
  // 不为空就先获取当前节点的下一节点 
  // 然后把当前节点的 next 设为上一个节点 
  // 然后把 current 设为下一个节点,pre 设为当前节点
  while(current) { 
  	next = current.next 
  	current.next = pre 
  	pre = current 
  	current = next
 }
  return pre 
 };

8.二叉树的先序,中序,后序遍历

先序遍历表示先访问根节点,然后访问左节点,最后访问右节点。
中序遍历表示先访问左节点,然后访问根节点,最后访问右节点。
后序遍历表示先访问左节点,然后访问右节点,最后访问根节点。

9.递归实现

递归实现相当简单,代码如下

function TreeNode(val) 
{
 this.val = val;
 this.left = this.right = null;
}
var traversal = function(root) {
 if (root)
 { 
 // 先序
 console.log(root);
 traversal(root.left);
 // 中序
 // console.log(root); 
 traversal(root.right);
 // 后序
 // console.log(root);
 }
};

10.树的深度

面试题:树的最大深度(题目需要求出一颗二叉树的最大深度)
以下是算法实现

var maxDepth = function(root)
{
	if (!root) return 0
	return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1
};

11.动态规划

动态规划背后的基本思想非常简单。就是将一个问题拆分为子问题,一般来说这些子问题都是非常相似的,那么我们可以通过只解决一次每个子问题来达到减少计算量的目的。
一旦得出每个子问题的解,就存储该结果以便下次使用。

12.合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
在这里插入图片描述

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

以下是实现该算法的代码

function mergeTwoLists(l1, l2) {
    if (l1 = null && l2 == null) {
        return null;
    }
    if (l1 == null) {
        return l2;
    }
    if (l2 == null) {
        return l1;
    }
    if (l1.val < l2.val) {
        l1.next = mergeTwoLists(l1.next, l2);
        return l1;
    } else {
        l2.next = mergeTwoLists(l1, l2.next);
        return l2;
    }
}

使用递归完成迭代

13.反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
在这里插入图片描述

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
//反转链表
function reverseList(head){
    //1、遍历链表,把链表里面的每个节点的值都拿下来,存在数组里面
    //(在这里我们存的节点的值,而不是存的整个节点,这样是可以节约内存的)
    let arr=[];//arr用于存储链表的节点的值
    let p=head;//p用于遍历链表
    while(p){
        arr.push(p.val);
        p=p.next;
    }
	//2、再次遍历链表,将数组里面的值倒序的赋值给每一个节点的val域就实现了链表的反转
	//(我们没有考虑新建一个链表,而是用了原来的链表,可以节约创建新链表的时间和内存)
    p=head;
    while(p){
        p.val=arr.pop();
        p=p.next;
    }
    return head;
}

1、遍历链表,把链表里面的每个节点的值都拿下来,存在数组里面
2、再次遍历链表,将数组里面的值倒序的赋值给每一个节点的val域就实现了链表的反转

14.二分查找算法(建立在已经排好序的情况下)

function binarySearch(arr, data) {
    var end = arr.length - 1,
        start = 0;

    while (start <= end) {
        var middle = Math.floor((start + end) / 2);
        if (arr[middle] > data) {
            end = middle - 1;
        } else if (arr[middle] < data) {
            start = middle + 1;
        } else {
            return middle;
        }
    }
    return -1;
}
var arr = [1, 2, 3, 4, 5, 6];
console.log(binarySearch(arr, 2));

15.斐波那契数列

相信大家都不陌生,在高中数学中都有接触过,斐波那契数列以如下被以递推的方法定义:

F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)

实现过程
斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:

F(0) = 0, F(1) = 1 F(N) = F(N - 1) + F(N - 2), 其中 N > 1. 给定 N,计算 F(N)。

以下是实现该算法的代码

/**
 * @param {number} N
 * @return {number}
 */
var fib = function(N) {
    if (N<2) return N;
    var frist = 0;
    var second = 1;
    for (var i=2;i<=N;i++) {
        second = frist + second;
        frist = second - frist;
    }
    return second;
};

for循环,用两个变量保存前两项斐波那契数即可。并且变量交换时也无需临时变量,空间复杂度O(N)

16.找出下列正数组的最大差值

输入 [10,5,11,7,8,9]
输出 6

这是通过一道题目去测试对于基本的数组的最大值的查找,很明显我们知道,最大差值肯定是一个数组中最大值与最小值的差。

 function getMaxProfit(arr) {
    var minPrice = arr[0];
    var maxProfit = 0;
    for (var i = 0; i < arr.length; i++) {
        var currentPrice = arr[i];
        minPrice = Math.min(minPrice, currentPrice);
        var potentialProfit = currentPrice - minPrice;
        maxProfit = Math.max(maxProfit, potentialProfit);
    }
    return maxProfit;
}

17.随机生成指定长度的字符串

实现一个算法,随机生成指制定长度的字符窜。

以下是实现算法的代码

比如给定 长度 8  输出 4ldkfg9j
function randomString(n) {  
  let str = 'abcdefghijklmnopqrstuvwxyz9876543210';
  let tmp = '',
      i = 0,
      l = str.length;
  for (i = 0; i < n; i++) {
    tmp += str.charAt(Math.floor(Math.random() * l));
  }
  return tmp;
}
module.exports = randomString;  

18.找到任意两个数字的最大和

找到两个最大的数并返回它们的和。

function topSum(arr){
        if(arr.length<2) return null;
        var first,second;
        if(arr[0]>arr[1]){
            first = arr[0];
            second=arr[1];
        }else{
            first = arr[1];
            second=arr[0];
        }
        for(var i=2;i<arr.length;i++){
            if(arr[i]>first){
                second = first;
                first = arr[i];
            }else if(arr[i]>second){
                second = arr[i];
            }
        }
        return first+second;
    }

19.去掉一组整型数组重复的值

比如输入: [1,13,24,11,11,14,1,2]
输出: [1,13,24,11,14,2]
需要去掉重复的111 这两个元素。

这道问题出现在诸多的前端面试题中,主要考察个人对Object的使用,利用key来进行筛选。

/** 
* unique an array 
**/ 
let unique = function(arr) {   
  let hashTable = {}; 
  let data = []; 
  for(let i=0,l=arr.length;i<l;i++) { 
    if(!hashTable[arr[i]]) { 
      hashTable[arr[i]] = true; 
      data.push(arr[i]); 
    } 
  } 
  return data 
} 
module.exports = unique;  

20.替换空格

请实现一个函数,将一个字符串中的每个空格替换成“%20”。

例如,当字符串为We Are Happy.
则经过替换之后的字符串为We%20Are%20Happy

解题思路:利用简单的正则获取空格,并且替换成%20即可。

部分正则规则:/g 匹配所有符合条件的元素.
\\d:表示数字  ^:表示开头  $:表示结尾  +:匹配多个
例如: ^\\d+$ 匹配的字符串只能是数字

let readline = require('readline');

let rl = readline.createInterface({
    input:process.stdin,
    output:process.stdout
})

rl.on('line',function(input){
    // let result = deal(input);
    let result = input.trim().replace(/ /g,'%20');  //replace(获取值,替换值)  
    console.log(result);
    
    return result;
})

21.实现一个函数,判断输入是不是回文字符串

function isPlalindrome(input) {
  if (typeof input !== 'string') return false;
  return input.split('').reverse().join('') === input;
}

22.腾讯:数组扁平化、去重、排序

面试题

已知如下数组:var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组

let arr =[[1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14]]]], 10]
function newArray ( ) {
  while(arr.some(Array.isArray)){
    arr = [].concat(...arr)
  }
  arr = [...new Set(arr)].sort((a,b)=>{
    return a-b
  })
  return arr
}
newArray()
console.log(arr);

23.编写一个函数计算多个数组的交集

要求:输出结果中的每个元素一定是唯一的

const getIntersection = (...arrs) => {
    return Array.from(new Set(arrs.reduce((total, arr) => {
        return arr.filter(item => total.includes(item));
    })));
}

24.选择排序

选择排序是从数组的开头开始,将第一个元素和其他元素作比较,检查完所有的元素后,最小的放在第一个位置,接下来再开始从第二个元素开始,重复以上一直到最后。
在这里插入图片描述
很简单嘛:外层循环从0开始到length-1, 然后内层比较,最小的放开头

function selectSort(arr) {
    var len = arr.length;
    for(let i = 0 ;i < len - 1; i++) {
        for(let j = i ; j<len; j++) {
            if(arr[j] < arr[i]) {
                [arr[i],arr[j]] = [arr[j],arr[i]];
            }
        }
    }
    return arr
}
  • 外层循环的i表示第几轮,arr[i]就表示当前轮次最靠前(小)的位置;
  • 内层从i开始,依次往后数,找到比开头小的,互换位置即可

25.爬楼梯问题

有一楼梯共M级,刚开始时你在第一级,若每次只能跨上一级或二级,要走上第M级,共有多少种走法?

分析: 这个问题要倒过来看,要到达n级楼梯,只有两种方式,从(n-1)级 或 (n-2)级到达的。所以可以用递推的思想去想这题,假设有一个数组s[n], 那么s[1] = 1(由于一开始就在第一级,只有一种方法), s[2] = 1(只能从s[1]上去 没有其他方法)。

那么就可以推出s[3] ~ s[n]了。

下面继续模拟一下, s[3] = s[1] + s[2], 因为只能从第一级跨两步, 或者第二级跨一步。

function cStairs(n) {
    if(n === 1 || n === 2) {
        return 1;
    } else {
        return cStairs(n-1) + cStairs(n-2)
    }
}

没错,其实就是斐波纳契数列

26.栈实现队列

使用栈实现队列的下列操作:
push(x) – 将一个元素放入队列的尾部。 pop() – 从队列首部移除元素。 peek() – 返回队列首部的元素。 empty() – 返回队列是否为空。
示例:

let queue = new MyQueue();

queue.push(1);
queue.push(2);  
queue.peek();  // 返回 1
queue熬夜肝了这篇Spring Cloud Gateway的功能及综合使用

牛掰!阿里架构师熬夜肝了一份JVM必知必会,哪里不会查哪里

别再迷茫了,熬夜肝了一份适合普通大学生的学习路线

从功能测试进阶自动化测试,熬夜7天整理了这一份2000字超全学习指南附学习路线图

肝了这套Python数据分析教程,进大厂稳了

2021年底粉丝大馈赠,熬夜7天肝出这一份3000字性能测试学习笔记