最近秋招也做了多多少少的面试题,发现除了基础知识外,算法还是挺重要的。特意整理了一些常见的算法题,添加了自己的理解并实现。

  除此之外,建议大家还可以刷刷《剑指offer》。此外,左神在牛客网上也有算法课程,听了基础班的感觉还不错,起码让我这个算法小白也能快速地理解了很多问题,知识付费的时代,这个真的是良心课程了。就我个人而言的话,平时为了解决一个算法问题,需要花很多时间去看帖子、看讲解,但很难真正转化为自己的思想(主要问题就是没有动手练),大家可以根据自己的需求,进行算法的学习。

  整理了19道js算法题目:

  1.验证一个数是否是素数

  2.斐波那契

  3.求最大公约数

  4.数组去重

  5.删除重复的字符

  6.排序两个已经排好序的数组

  7.字符串反向

  8.字符串原位反转

  9.判断是否是回文

  10.判断数组中是否有两数之和

  11.连字符转成驼峰

  12.加油站问题-贪心算法

  13.用正则实现trim() 清除字符串两端空格

  14.岛问题:判断有几个岛

  15.将数字12345678转化成RMB形式:12,345,678

  16.删除相邻相同的字符串

  17.宣讲会安排

  18.汉诺塔问题

  19.母牛生母牛问题

  1.验证一个数是否是素数

  如果这个数是 2 或 3,一定是素数;

  如果是偶数,一定不是素数;

  如果这个数不能被3~它的平方根中的任一数整除,m必定是素数。而且除数可以每次递增2(排除偶数)

  function isPrime(num){

  if (num === 2 || num === 3) {

  return true;

  };

  if (num % 2 === 0) {

  return false;

  };

  let divisor = 3,limit = Math.sqrt(num);

  while(limit >= divisor){

  if (num % divisor === 0) {

  return false;

  }

  else {

  divisor += 2;

  }

  }

  return true;

  }

  console.log(isPrime(30)); // false

  2.斐波那契

  最简单的做法:递归。

  function fibonacci(n){

  if (n <= 0) {

  return 0;

  }

  if (n == 0) {

  return 1;

  }

  return fibonacci(n-1) + fibonacci(n-2);

  }

  但是递归会有严重的效率问题。比如想要求得f(10),首先需要求f(9)和f(8)。同样,想求f(9),首先需要f(8)和f(7)…这样就有很多重复值,计算量也很大。

  改进:从下往上计算,首先根据f(0)和f(1)计算出f(2),再根据f(1)和f(2)计算出f(3)……以此类推就可以计算出第n项。时间复杂度O(n)。

  function fibonacci(n){

  let ori = [0,1];

  if (n < 2) {

  return ori[n];

  };

  let fiboOne = 1,fiboTwo = 0,fiboSum = 0;

  for (let i = 2; i <= n; i++) {

  fiboSum = fiboOne + fiboTwo;

  fiboTwo = fiboOne;

  fiboOne = fiboSum;

  }

  return fiboSum;

  }

  console.log(fibonacci(5));

  3.求最大公约数

  除数 在a和b的范围内,如果同时a和b处以除数的余等于0,就将此时的除数赋值给res;除数自增,不断循环上面的计算,更新res。

  function greatestCommonDivisor(a, b){

  let divisor = 2,res = 1;

  if (a < 2 || b < 2) {

  return 1;

  };

  while(a >= divisor && b >= divisor){

  if (a%divisor === 0 && b%divisor === 0) {

  res = divisor;

  }

  divisor++;

  }

  return res;

  };

  console.log(greatestCommonDivisor(8, 4)); // 4

  console.log(greatestCommonDivisor(69, 169)); // 1

  解法2:

  function greatestCommonDivisor(a,b){

  if (b === 0) {

  return a;

  } else {

  return greatestCommonDivisor(b,a%b);

  }

  };

  4.数组去重

  对原数组进行遍历

  获取arr[i]的值 j;

  对应到辅助数组 exits 的位置 j 的值,如果没有,则证明arr[i] 的值没有重复,此时将 值j 存入res数组,并将辅助数组 j 位置的值置为 true。

  最后返回res数组。

  function removeDuplicate(arr){

  if (arr === null || arr.length < 2) {

  return arr;

  };

  let res = [],exits = [];

  for(let i = 0; i < arr.length; i++){

  let j = arr[i];

  while( !exits[j] ){

  res.push(arr[i]);

  exits[j] = true;

  }

  }

  return res;

  }

  console.log(removeDuplicate([1,3,3,3,1,5,6,7,8,1])) // [1,3,5,6,7,8]

  5.删除重复的字符

  这一题的解法和上一题类似。

  function removeDuplicateChar(str){

  if (!str || str.length < 2 || typeof str != "string") {

  return;

  };

  let charArr = [],res = [];

  for(let i = 0; i < str.length; i++){

  let c = str[i];

  if(charArr[c]){

  charArr[c]++;

  }

  else{

  charArr[c] = 1;

  }

  }

  for(let j in charArr){

  if (charArr[j] === 1) {

  res.push(j);

  }

  }

  return res.join("");

  }

  console.log(removeDuplicateChar("Learn more javascript dude"));

  // Lnmojvsciptu

  6.排序两个已经排好序的数组

  如果 b数组已经遍历完,a数组还有值 或 a[i] 的值 小于等于 b[i] 的值,则将 a[i] 添加进数组res,并 i++;

  如果不是上面的情况,则将 b[i] 添加进数组res,并 i++;

  function mergeSortedArr(a,b){

  if (!a || !b) {

  return;

  };

  let aEle = a[0],bEle = b[0],i = 1,j = 1,res = [];

  while(aEle || bEle){

  if ((aEle && !bEle) || aEle <= bEle) {

  res.push(aEle);

  aEle = a[i++];

  }

  else{

  res.push(bEle);

  bEle = b[j++];

  }

  }

  return res;

  }

  console.log(mergeSortedArr([2,5,6,9], [1,2,3,29])) // [1,2,2,3,5,6,9,29]

  7.字符串反向

  最简单的方法:

  function reverse(str){

  let resStr = "";

  for(let i = str.length-1; i >= 0; i--){

  resStr += str[i];

  }

  return resStr;

  }

  console.log(reverse("ABCDEFG"));

  方法2

  function reverse2(str){

  if (!str || str.length < 2 || typeof str != "string") {

  return str;

  };

  let res = [];

  for(let i = str.length-1; i >= 0; i--){

  res.push(str[i]);

  }

  return res.join("");

  }

  console.log(reverse2("Hello"));

  将函数添加到String.prototype

  String.prototype.reverse3 = function(){

  if (!this || this.length < 2) {

  return;

  };

  let res = [];

  for(let i = this.length-1; i >= 0; i--){

  res.push(this[i]);

  }

  return res.join("");

  }

  console.log("abcdefg".reverse3());

  8.字符串原位反转

  例如:将“I am the good boy”反转变为 “I ma eht doog yob”。

  提示:使用数组和字符串方法。

  function reverseInPlace(str){

  return str.split(' ').reverse().join(' ').split('').reverse().join('');

  }

  console.log(reverseInPlace('I am the good boy'));

  9.判断是否是回文

  function isPalindrome(str){

  if (!str || str.length < 2) {

  return;

  }

  for(let i = 0; i < str.length/2; i++){

  if (str[i] !== str[str.length-1-i]) {

  return false;

  }

  }

  return true;

  }

  console.log(isPalindrome("madama"))

  10.判断数组中是否有两数之和

  eg:在一个未排序的数组中找出是否有任意两数之和等于给定的数。

  给出一个数组[6,4,3,2,1,7]和一个数9,判断数组里是否有任意两数之和为9。

  这个题解得很巧妙,

  循环遍历数组,let subStract = num - arr[i];

  如果 differ[subStract] 里有值,则返回true;如果没有,将 differ[arr[i]] 置为 true。

  function sumFind(arr,num){

  if (!arr || arr.length < 2) {

  return;

  };

  let differ = {};

  for(let i = 0; i < arr.length; i++){

  let subStract = num - arr[i];

  if (differ[subStract]) {

  return true;

  }

  else{

  differ[arr[i]] = true;

  }

  }

  return false;

  }

  console.log(sumFind([6,4,3,2,1,7], 9)); // true

  11.连字符转成驼峰

  如:get-element-by-id 转为 getElementById

  let str = 'get-element-by-id';

  let arr = str.split('-');

  for(let i=1; i

  arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].substring(1);

  }

  console.log(arr.join('')); // getElementById

  12.加油站问题-贪心算法

  一辆汽车加满油后可行驶n公里。旅途中有若干个加油站。设计一个有效算法,指出应在哪些加油站停靠加油,使沿途加油次数最少。对于给定的n(n <= 5000)和k(k <= 1000)个加油站位置,编程计算最少加油次数。并证明算法能产生一个最优解。

  要求:无锡人流医院哪家好 http://www.bhnnkyy120.com/

  输入:第一行有2个正整数n和k,表示汽车加满油后可行驶n公里,且旅途中有k个加油站。接下来的1 行中,有k+1 个整数,表示第k个加油站与第k-1 个加油站之间的距离。第0 个加油站表示出发地,汽车已加满油。第k+1 个加油站表示目的地。

  输出:输出编程计算出的最少加油次数。如果无法到达目的地,则输出”NoSolution”。

  function greedy(n, k, arr){ // n:加满可以行驶的公里数; k:加油站数量; arr:每个加油站之间的距离数组

  if (n == 0 || k == 0 || arr.length == 0 || arr[0] > n) {

  return "No Solution!"; // arr[0] > n :如果第一个加油站距离太远,也无法到达

  };

  let res = 0, distance = 0; // res:加油次数;distance:已行驶距离

  for(let i = 0; i <= k; i++){

  distance += arr[i];

  if (distance > n) { // 已行驶距离 > 加满可以行驶的公里数

  if(arr[i] > n){ // 如果目前加油站和前一个加油站的距离 > 加满可以行驶的公里数,则无法到达

  return "No Solution!";

  };

  // 可以在上一个加油站加油,行驶到目前的加油站i:

  distance = arr[i];

  res++; // 加油次数+1

  }

  }

  return res;

  }

  let arr = [1,2,3,4,5,1,6,6];

  console.log(greedy(7,7,arr)) // 4

  13.用正则实现trim() 清除字符串两端空格

  String.prototype.trim1 = function(){

  // return this.replace(/\s*/g,""); // 清除所有空格

  return this.replace(/(^\s*)|(\s*$)/g,""); // 清除字符串前后的空格

  };

  console.log(" hello word ".trim1()) // "hello word"

  14.岛问题:判断有几个岛

  一个矩阵中只有0和1两种值,每个位置都可以和自己的上、下、左、右 四个位置相连,如果有一片1连在一起,这个部分叫做一个岛,求一个矩阵中有多少个岛?

  可以看我之前的讲解。Javascript实现岛问题

  15.将数字12345678转化成RMB形式:12,345,678

  思路:将字符串切割成数组再反转,遍历数组,加入辅助数组,当数组长度为3的倍数,再向辅助数组加入 ","。

  function RMB(str){

  let arr = str.split("").reverse();

  let res = [];

  for(let i = 0; i < arr.length; i++){

  res.push(arr[i]);

  if ((i + 1) % 3 === 0) {

  res.push(",");

  }

  }

  return res.reverse().join("");

  }

  console.log(RMB("12345678"))

  16.删除相邻相同的字符串

  function delSrt(str){

  let res = [], nowStr;

  for(let i = 0; i < str.length; i ++){

  if (str.charAt(i) != nowStr) {

  res.push(str.charAt(i));

  nowStr = str.charAt(i);

  }

  }

  return res.join("");

  }

  console.log(delSrt("aabcc11"))

  17.宣讲会安排

  一些项目要占用一个会议室宣讲,会议室不能同时容纳两个项目的宣讲。 给你每一个项目开始的时间和结束的时间(数组,里面是一个个具体的项目),你来安排宣讲的日程,要求会议室进行 的宣讲的场次最多。返回这个最多的宣讲场次。

  步骤:

  先按照会议的end时间升序排序;

  排除了因为正在进行会议而无法进行的会议(now > obj[i].start);

  会议能举行,则 res++,并且更新目前时间now (now = obj[i].end;)。

  function getMostCount(obj){

  if (!obj || obj.length < 1) {

  return;

  };

  obj.sort(sortEndTime);

  let res = 1, now = obj[0].end;

  for(let i = 1; i < obj.length; i++){

  if (now < obj[i].start) {

  res++;

  now = obj[i].end;

  }

  }

  return res;

  }

  // 自定义排序法

  function sortEndTime(obj1,obj2){

  return obj1.end - obj2.end;

  }

  var obj = [

  {start:6,end:8},

  {start:7,end:9},

  {start:11,end:12},

  {start:10,end:14},

  {start:16,end:18},

  {start:17.5,end:21},

  {start:15,end:17},

  {start:22,end:23}

  ];

  console.log("最大场次:" + getMostCount(obj));

  18.汉诺塔问题

  把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。

  思路:

  递归解决:把问题转化为规模缩小了的同类问题的子问题;

  明确递归结束的条件(base case):n == 1

  其他过程:from:来源地;to:目的地;help:辅助。

  function hanoiProcess(n,from,to,help){

  if (n < 1) {

  return;

  }

  if (n == 1) { // 最后一个从from移到to

  console.log("Move 1 from " + from + " to " + to);

  } else{

  hanoiProcess(n-1, from, help, to); // 前n-1个从from移到help上,可以借助to

  console.log("Move "+ n +" from " + from + " to " + to);

  hanoiProcess(n-1, help, to, from); // 再把n-1个从help移到to,可以借助from

  }

  }

  hanoiProcess(3, "左", "右", "中");

  结果:

  Move 1 from 左 to 右

  Move 2 from 左 to 中

  Move 1 from 右 to 中

  Move 3 from 左 to 右

  Move 1 from 中 to 左

  Move 2 from 中 to 右

  Move 1 from 左 to 右

  19.母牛生母牛问题

  母牛每年生一只母牛,新出生的母牛成长三年后也能每年生一只母牛,假设不会死。求N年后,母牛的数量。

  思路:

  因为新生的母牛,只有等到第四年才能生小母牛。所以前4年,只有原来的一头母牛每年生一头。

  第五年以后,除了有前一年的牛数量,还有三年前的牛可以生新的小牛。(最近3年内生的牛还不能生)

  function cow(n){

  if (n < 1) {

  return;

  };

  let count = 0;

  if (n > 4) {

  count = cow(n-1) + cow(n-3);

  } else{

  count = n;

  }

  return count;

  }

  let n = 7;

  console.log(n + " 年后,牛的数量是: " + cow(n))

  // 7 年后,牛的数量是: 13

  如果有更好的解法,感谢大佬赐教!我的解法太普通了,有时间再改进下。

  算法问题先写到这,如果还有更多的面试题,也可以和我交流交流,相互学习呀!

19道常见的JS面试算法题的更多相关文章

  1. python经典面试算法题1.4:如何对链表进行重新排序

    本题目摘自<Python程序员面试算法宝典>,我会每天做一道这本书上的题目,并分享出来,统一放在我博客内,收集在一个分类中. 1.4 对链表按照如下要求重新排序 [微软笔试题] 难度系数: ...

  2. 合并K个有序数组(链表)【字节跳动面试算法题】

    本题是本人字节跳动一面考的算法题原题是有序数组,一时没想到怎么解决数组的问题,但是如果给的是有序链表数组,则可以用下面的方法解决 可以利用最小堆完成,时间复杂度是O(nklogk),具体过程如下: 创 ...

  3. python经典面试算法题1.3:如何计算两个单链表所代表的数之和

    本题目摘自<Python程序员面试算法宝典>,我会每天做一道这本书上的题目,并分享出来,统一放在我博客内,收集在一个分类中. 1.2 如何实现链表的逆序 [华为笔试题] 难度系数:⭐⭐⭐ ...

  4. python经典面试算法题1.2:如何从无序链表中移除重复项

    本题目摘自<Python程序员面试算法宝典>,我会每天做一道这本书上的题目,并分享出来,统一放在我博客内,收集在一个分类中. 1.2 如何实现链表的逆序 [蚂蚁金服面试题] 难度系数:⭐⭐ ...

  5. python经典面试算法题4.1:如何找出数组中唯一的重复元素

    本题目摘自<Python程序员面试算法宝典>,我会每天做一道这本书上的题目,并分享出来,统一放在我博客内,收集在一个分类中. [百度面试题] 难度系数:⭐⭐⭐ 考察频率:⭐⭐⭐⭐ 题目描述 ...

  6. python经典面试算法题1.1:如何实现链表的逆序

    本题目摘自<Python程序员面试算法宝典>,我会每天做一道这本书上的题目,并分享出来,统一放在我博客内,收集在一个分类中. 1.1 如何实现链表的逆序 [腾讯笔试题] 难度系数:⭐⭐⭐ ...

  7. 常见面试算法题JS实现-设计一个有getMin功能的栈

    前言: 已经确定工作了-下周一正式入职,按理说应该是可以好好浪荡一周的,但是内心总是不安,总觉得自己这个水平真的太菜了,还是趁着现在有自己的时间,赶紧多看看书,多学习学习吧orz所以把之前校招买的书, ...

  8. 常见面试算法题JS实现-仅用递归函数和栈操作逆序一个栈

    前言: 因为JAVA和JS语言特性的不同,有些东西在JAVA中可能需要一些技巧和手段才能实现的复杂程序,但是在JS中可能就是天然存在的,所以这套书里面的题目不会全部用JS去实现一遍,因为可能JS的实现 ...

  9. php最常见最经典的算法题

    1.一群猴子排成一圈,按1,2,…,n依次编号.然后从第1只开始数,数到第m只,把它踢出圈,从它后面再开始数,再数到第m只,在把它踢出去…,如此不停的进行下去,直到最后只剩下一只猴子为止,那只猴子就叫 ...

随机推荐

  1. 关于Socket踩过的一些坑

    Socket学习文档  http://developer.51cto.com/developer/javabook/images/3.pdf 1.socket.shutdownOutput(); 文档 ...

  2. OpenResty: 介绍 (摘抄)

    原文链接:https://www.cnblogs.com/duanxz/p/10396160.html Nginx 是俄罗斯人发明的, Lua 是巴西几个教授发明的,中国人章亦春把 LuaJIT VM ...

  3. [RN] React Native 中使用 stickyHeaderIndices 实现 ScrollView 的吸顶效果

    React Native中,ScrollView组件可以使用 stickyHeaderIndices 轻松实现 sticky 效果. 例如下面代码中: <ScrollView showsVert ...

  4. jedis代码操作

    一.jedis快速入门 * Jedis: 一款java操作redis数据库的工具. * 使用步骤: 1. 下载jedis的jar包 2. 使用 //1. 获取连接 Jedis jedis = new ...

  5. webpack 配置多入口文件,输出多出口文件

    const path = require('path') module.exports = { // 入口文件的配置项 entry: { // 入口文件 entry: './src/entry.js' ...

  6. git手册查询

    1.创建版本库 通过git init命令把此目录变成Git可以管理的仓库; 添加文件到Git仓库,分两步 第一步:git add <file>,注意,可反复多次使用,添加多个文件:例如 g ...

  7. 对称加密与非对称加密,及Hash算法

    一 , 概述 在现代密码学诞生以前,就已经有很多的加密方法了.例如,最古老的斯巴达加密棒,广泛应用于公元前7世纪的古希腊.16世纪意大利数学家卡尔达诺发明的栅格密码,基于单表代换的凯撒密码.猪圈密码, ...

  8. Spring Security教程(二)

    上一篇博客中,Spring Security教程(一),我把用户信息和权限信息放到了xml文件中,这是为了演示如何使用最小的配置就可以使用Spring Security,而实际开发中,用户信息和权限信 ...

  9. 学习Linux要知道的知识点总结

    1 如何查看Linux的user和hostname? (1)打开终端查看 终端左侧的root@zlkj:~$,前面的root为user - 用户名,后面的zlkj为hostname - 主机名. (2 ...

  10. linux系统ubuntu中在命令行如何打开图形界面的文件夹

    用linux查看文件列表之类的受到命令行限制,还是不太方便的.在文件夹中打开的话,切换路径又没有linux终端快,于是,需要在命令行窗口中打开文件夹.如何做呢? 来到终端命令行中,cd切换你的路径,使 ...