JS面試中常見的算法題

前言
最近在準備秋招,作過了大大小小的公司的面試題,發現除了基礎知識外,算法仍是挺重要的。特地整理了一些常見的算法題,添加了本身的理解並實現。javascript

除此以外,建議你們還能夠刷刷《劍指offer》(但我還沒刷完😂,任重道遠吶)。此外,左神在牛客網上也有算法課程,聽了基礎班的感受還不錯,起碼讓我這個算法小白也能快速地理解了不少問題,知識付費的時代,這個真的是良心課程了。就我我的而言的話,平時爲了解決一個算法問題,須要花不少時間去看帖子、看講解,但很難真正轉化爲本身的思想(主要問題就是沒有動手練),你們能夠根據本身的需求,進行算法的學習。java

話很少說,下面來看題。面試

目錄算法

前言編程

1.驗證一個數是不是素數數組

2.斐波那契函數

3.求最大公約數學習

4.數組去重this

5.刪除重複的字符.net

6.排序兩個已經排好序的數組

7.字符串反向

8.字符串原位反轉

9.判斷是不是迴文

10.判斷數組中是否有兩數之和

11.連字符轉成駝峯

12.加油站問題-貪心算法

13.用正則實現trim() 清除字符串兩端空格

14.島問題:判斷有幾個島

15.將數字12345678轉化成RMB形式:12,345,678

16.刪除相鄰相同的字符串

17.宣講會安排

18.漢諾塔問題

19.母牛生母牛問題

20.切割金條-貪心算法

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.length; 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)個加油站位置,編程計算最少加油次數。並證實算法能產生一個最優解。
要求:

輸入:第一行有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
20.切割金條-貪心算法
一塊金條切成兩半,是須要花費和長度數值同樣的銅板的。好比長度爲20的金條,無論切成長度多大的兩半,都要花費20個銅板。一羣人想整分整塊金條,怎麼分最省銅板?例如,給定數組{10,20,30},表明一共三我的,整塊金條長度爲 10+20+30=60。金條要分紅10,20,30三個部分。 若是先把長度60的金條分紅10和50,花費60。再把長度50的金條分紅20和30, 花費50。一共花費110銅板。可是若是先把長度60的金條分紅30和30,花費60。再把長度30 金條分紅10和20,花費30。一共花費90銅板。 輸入一個數組,返回分割的最小代價。

這個也能夠看我以前的博文介紹。js實現切割金條問題

若是有更好的解法,感謝大佬賜教!個人解法太普通了,有時間再改進下。

算法問題先寫到這,若是還有更多的面試題,也能夠和我交流交流,相互學習呀!
————————————————
版權聲明:本文爲CSDN博主「Allison-L」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/weixin_...

相關文章
相關標籤/搜索