19道常見的JS面試算法題


  最近秋招也做了多多少少的面試題,發現除了基礎知識外,算法還是挺重要的。特意整理了一些常見的算法題,添加了自己的理解並實現。

  除此之外,建議大家還可以刷刷《劍指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

  如果有更好的解法,感謝大佬賜教!我的解法太普通了,有時間再改進下。

  算法問題先寫到這,如果還有更多的面試題,也可以和我交流交流,相互學習呀!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM