20个常见的前端算法题,你全都会吗?

现在面试中,算法出现的频率越来越高了,大厂基本必考

今天给大家带来20个常见的前端算法题,重要的地方已添加注释,如有不正确的地方,欢迎多多指正💕

1、两数之和

题目: 给定一个数组 nums 和一个目标值 target,在该数组中找出和为目标值的两个数

输入: nums: [8, 2, 6, 5, 4, 1, 3] ; target:7

输出: [2, 5]

function twoNumAdd(arr, target) {
  if (Array.isArray(arr)) {
    
    let map = {};
    for (let i = 0; i < arr.length; i++) {
      
      if (map[target - arr[i]] !== undefined) {
        return [target - arr[i], arr[i]];
      } else {
        
        map[arr[i]] = i;
      }
    }
  }
  return [];
}

2、三数之和

题目: 给定一个数组nums,判断 nums 中是否存在三个元素a,b,c,使得 a + b + c = target,找出所有满足条件且不重复的三元组合

输入: nums: [5, 2, 1, 1, 3, 4, 6] ;target:8

输出: [[1, 1, 6], [1, 2, 5], [1, 3, 4]]

function findThree(arr, target) {
  
  arr.sort((a, b) => a - b)
  let result = [];
  for (let i = 0; i < arr.length - 2; i++) {
    
    if (i && arr[i] === arr[i - 1]) continue;
    let left = i + 1;
    let right = arr.length - 1;
    
    
    while (left < right) {
      let sum = arr[i] + arr[left] + arr[right];
      if (sum > target) {
        right--;
      } else if (sum < target) {
        left++;
      } else {
        
        result.push([arr[i], arr[left++], arr[right--]]);
        while (arr[left] === arr[left - 1]) {
          
          left++;
        }
        while (arr[right] === arr[right + 1]) {
          
          right--;
        }
      }
    }
  }
  return result;
}

3、版本号排序

题目: 输入一组版本号,输出从大到小的排序

输入: ['2.1.0.1', '0.402.1', '10.2.1', '5.1.2', '1.0.4.5']

输出: ['10.2.1', '5.1.2', '2.1.0.1', '1.0.4.5', '0.402.1']

function versionSort(arr) {
  return arr.sort((a, b) => {
    let i = 0;
    const arr1 = a.split(".");
    const arr2 = b.split(".");
    while (true) {
      
      const s1 = arr1[i];
      const s2 = arr2[i];
      i++;
      
      if (s1 === undefined || s2 === undefined) {
        return arr2.length - arr1.length;
      }
      if (s1 === s2) continue;
      
      return s2 - s1;
    }
  });
}

4、第一个不重复的字符

题目: 输入一个字符串,找到第一个不重复字符的下标

输入: 'abcabcde'

输出: 6

function findOneStr(str) {
  if (!str) return -1;
  
  let map = {};
  let arr = str.split("");
  arr.forEach(item => {
    let val = map[item];
    
    map[item] = val ? val + 1 : 1;
  });
  
  for (let i = 0; i < arr.length; i++) {
    if (map[arr[i]] == 1) {
      return i;
    }
  }
  return -1;
}

5、字符串所有排列组合

题目: 输入一个字符串,打印出该字符串中,所有字符的排列组合

输入: 'abc'

输出: ['abc', 'acb', 'bca', 'bac', 'cab', 'cba']

 * 利用回溯算法,计算所有字符串的组合
 * @param {array} list - 字符串列表
 * @param {array} result - 最终的结果
 * @param {string} current - 当前的字符串
 * @param {string} temp - 当前固定的字符
*/
function stringGroup(list = [], result = [], current = "", temp = "") {
  current += temp;
  if (list.length === 0) {
    
    return result.push(current);
  }
  for (let i = 0; i < list.length; i++) {
    
    temp = list.shift();
    stringGroup(list, result, current, temp);
    
    list.push(temp);
  }
  
  return [...new Set(result)];
}

6、冒泡排序

时间复杂度为O(n²),稳定排序算法

 

function bubbleSort(arr) {
  let length = arr.length;
  
  for (let i = 0; i < length; i++) {
    
    
    for (let j = 0; j < length - i - 1; j++) {
      
      if (arr[j] > arr[j + 1]) {
        
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      }
    }
  }
  return arr;
}

7、选择排序

时间复杂度为O(n²),不稳定

 

function selectSort(arr) {
  
  let index;
  
  for (let i = 0; i < arr.length - 1; i++) {
    index = i;
    
    
    for (let j = i + 1; j < arr.length; j++) {
      
      if (arr[j] < arr[index]) {
        
        index = j;
      }
    }
    
    if (index !== i) {
      [arr[i], arr[index]] = [arr[index], arr[i]];
    }
  }
  return arr;
}

8、插入排序

时间复杂度为O(n²),稳定

function insertSort(arr) {
  
  for (let i = 1; i < arr.length; i++) {
    let j = i;
    
    let target = arr[j];
    
    while (j > 0 && arr[j - 1] > target) {
      
      arr[j] = arr[j - 1];
      j--;
    }
    arr[j] = target;
  }
  return arr;
}

9、快速排序

时间复杂度为O(nlogn),不稳定

function quickSort(list) {
  
  if (list.length <= 1) return list;
  
  let mid = Math.floor(list.length / 2);
  
  let base = list.splice(mid, 1)[0];
  let left = [];
  let right = [];
  list.forEach(item => {
    if (item > base) {
      right.push(item);
    } else {
      left.push(item);
    }
  });
  
  return quickSort(left).concat(base, quickSort(right));
}

10、归并排序

时间复杂度为O(nlogn),稳定

function MergeSort(array) {
  let len = array.length;

  
  if (len <= 1) {
    return array;
  }
  
  let num = Math.floor(len / 2);
  let left = MergeSort(array.slice(0, num));
  let right = MergeSort(array.slice(num, array.length));
  return merge(left, right);

  function merge(left, right) {
    let [l, r] = [0, 0];
    let result = [];
    
    while (l < left.length && r < right.length) {
      
      if (left[l] < right[r]) {
        result.push(left[l]);
        l++;
      } else {
        result.push(right[r]);
        r++;
      }
    }
    
    result = result.concat(left.slice(l, left.length));
    result = result.concat(right.slice(r, right.length));
    return result;
  }
}

11、列表转成树

题目: 输入一组列表如下,转化成树形结构

输入

[
  { id: 1, title: "child1", parentId: 0 },
  { id: 2, title: "child2", parentId: 0 },
  { id: 3, title: "child1_1", parentId: 1 },
  { id: 4, title: "child1_2", parentId: 1 },
  { id: 5, title: "child2_1", parentId: 2 }
]

输出

tree=[
  {
    "id": 1,
    "title": "child1",
    "parentId": 0,
    "children": [
      {
        "id": 3,
        "title": "child1_1",
        "parentId": 1
      },
      {
        "id": 4,
        "title": "child1_2",
        "parentId": 1
      }
    ]
  },
  {
    "id": 2,
    "title": "child2",
    "parentId": 0,
    "children": [
      {
        "id": 5,
        "title": "child2_1",
        "parentId": 2
      }
    ]
  }
]

function listToTree(data) {
  
  let map = {};
  
  let treeData = [];
  
  for (let i = 0; i < data.length; i++) {
    map[data[i].id] = data[i];
  }
  
  for (let i in map) {
    
    if (map[i].parentId) {
      if (!map[map[i].parentId].children) {
        map[map[i].parentId].children = [];
      }
      
      map[map[i].parentId].children.push(map[i]);
    } else {
      
      treeData.push(map[i]);
    }
  }
  return treeData;
}

12、深度优先遍历

题目: 对树进行遍历,从第一个节点开始,遍历其子节点,直到它的所有子节点都被遍历完毕,然后再遍历它的兄弟节点

输入: 上文第11题生成的tree

输出: [1, 3, 4, 2, 5]

function deepTree(tree, arr = []) {
  if (!tree || !tree.length) return arr;
  tree.forEach(data => {
    arr.push(data.id);
    
    data.children && deepTree(data.children, arr);
  });
  return arr;
}


function deepTree(tree) {
  if (!tree || !tree.length) return;
  let arr = [];
  let stack = [];
  
  for (let i = 0, len = tree.length; i < len; i++) {
    stack.push(tree[i]);
  }
  let node;
  while (stack.length) {
    
    node = stack.shift();
    arr.push(node.id);
    
    if (node.children && node.children.length) {
      stack = node.children.concat(stack);
    }
  }
  return arr;
}

13、广度优先遍历

题目: 以横向的维度对树进行遍历,从第一个节点开始,依次遍历其所有的兄弟节点,再遍历第一个节点的子节点,一层层向下遍历

输入: 上文第11题生成的tree

输出: [1, 2, 3, 4, 5]

function rangeTree(tree) {
  var list = []; 
  var queue = tree; 

  while (queue.length > 0) { 
    var node = queue.shift(); 
    list.push(node); 

    if (node.children && node.children.length > 0) { 
      for (var i = 0; i < node.children.length; i++) {
        queue.push(node.children[i]); 
      }
    }
  }

  return list; 
}


14、树形结构查找节点

题目: 查找树形结构中符合要求的节点

输入
tree: 上文第11题生成的tree
func: data => data.title === "child2_1"

输出:{ id: 5, parentId: 2, title: "child2_1" }

* 查找节点
* @param {array} tree - 树
* @param {function} func - 查找条件
* */
function findTreeNode(tree, func) {
  if (!tree.length) return null;
  for (let i = 0; i < tree.length; i++) {
    
    if (func(tree[i])) return tree[i];
    if (tree[i].children) {
      const res = findTreeNode(tree[i].children, func);
      
      if (res) {
        return res;
      }
    }
  }
  return null;
}


findTreeNode(tree, data => data.title === "child2_1")

15、二叉查找树

题目: 判断一个数组,是否为某二叉查找树的前序遍历结果,二叉查找树特点是所有的左节点比父节点的值小,所有的右节点比父节点的值大

输入: [5, 3, 2, 1, 4, 6, 7, 8, 9]

输出: true

function preOrderOfBST(list) {
  if (list && list.length > 0) {
    
    var root = list[0];
    
    for (var i = 0; i < list.length; i++) {
      if (list[i] > root) {
        break;
      }
    }
    
    for (let j = i; j < list.length; j++) {
      if (list[j] < root) {
        return false;
      }
    }
    var left = true;
    
    if (i > 1) {
      left = preOrderOfBST(list.slice(1, i + 1));
    }
    var right = true;
    
    if (i < list.length) {
      right = preOrderOfBST(list.slice(i, list.length));
    }
    
    return left && right;
  }
}

16、查找二叉树的路径

题目: 查找二叉树和为某一值的路径

输入: 二叉树结构如下,找到和为 11 的所有路径

输出: [[5, 3, 2, 1], [5, 6]]

* 利用回溯算法,找到和为某一值的路径
* @param {object} node - 二叉树
* @param {number} num - 目标值
* @param {array} stack - 栈
* @param {number} sum - 当前路径的和
* @param {array} result - 存储所有的结果
* */
function findPath(node, num, stack = [], sum = 0, result = []) {
  stack.push(node.data);
  sum += node.data;

  
  if (sum === num) {
    
    result.push(stack.slice());
  }
  if (node.left) {
    findPath(node.left, num, stack, sum, result);
  }
  if (node.right) {
    findPath(node.right, num, stack, sum, result);
  }
  
  
  stack.pop();
  return result;
}

17、买卖股票问题

题目: 给定一个整数数组,其中第 i 个元素代表了第 i天的股票价格;
非负整数 fee 代表了交易股票的手续费用,求返回获得利润的最大值

输入: arr: [1, 12, 13, 9, 15, 8, 6, 16]; fee: 2

输出: 22

 * 贪心算法求解
 * @param {array} list - 股票每天的价格列表
 * @param {number} fee - 手续费
 * */
function buyStock(list, fee) {
  
  let min = list[0],
    sum = 0;
  for (let i = 1; i < list.length; i++) {
    
    if (list[i] < min) {
      
      min = list[i];
    } else {
      
      let temp = list[i] - min - fee;
      if (temp > 0) {
        
        sum += temp;
        
        min = list[i] - fee;
      }
    }
  }
  return sum;
}

18、斐波那契数列

题目: 从第3项开始,当前项等于前两项之和: 1 1 2 3 5 8 13 21 ……,计算第n项的值

输入: 10

输出: 89

function fib(n) {
  
  if (n < 2) return 1;
  let dp = [1n, 1n];
  for (let i = 2; i <= n; i++) {
    dp[i] = dp[i - 1] + dp[i - 2];
  }
  return dp[n];
}

19、滑动窗口最大值

题目: 给定一个数组 nums,有一个大小为 k 的滑动窗口,从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口中的k个数字。滑动窗口每次只向右移动一位,求返回滑动窗口最大值

输入: nums: [1,3,-1,-3,5,3,6,7]; k: 3

输出: [3, 3, 5, 5, 6, 7]

function maxSlidingWindow(nums, k) {
  
  const window = [];
  
  const result = [];
  for (let i = 0; i < nums.length; i++) {
    if (i - window[0] > k - 1) {
      
      window.shift(); 
    }
    for (let j = window.length - 1; j >= 0; j--) {
      
      if (nums[window[j]] <= nums[i]) {
        window.pop();
      }
    }
    
    
    
    window.push(i);
    if (i >= k - 1) {
      
      result.push(nums[window[0]]);
    }
  }
  return result;
}

20、最长递增子序列

题目: 一个整数数组 nums,找到其中一组最长递增子序列的值

输入: [3,5,7,1,2,8]

输出: [3,5,7,8]

function lengthOfLIS(nums) {
  if (!nums.length) return 0;
  
  
  let dp = new Array(nums.length).fill(1);
  
  for (let i = 0; i < nums.length; i++) {
    
    for (let j = 0; j < i; j++) {
      if (nums[j] < nums[i]) {
        
        dp[i] = Math.max(dp[i], dp[j] + 1);
      }
    }
  }
  
  
  let max = Math.max(...dp);
  let result = [];
  for (let i = max; i >= 1; i--) {
    
    findArrNode(dp, i, result, nums);
  }
  return result;
}
function findArrNode(dp, value, result, arr) {
  
  let index = dp.lastIndexOf(value);
  
  result.unshift(arr[index]);
  
  dp.length = index + 1;
}

最后

如果你现在正在找工作,可以私信“web”进群领取前端面试小册以及更多阿里、字节大厂面试真题合集,和p8大佬一起交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Web面试那些事儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值