看到有一篇寫前端面試中常見的算法文章扣典,里面的例子很簡單,但也挺有趣慎玖。
重要的是贮尖,其實每個問題,都不止一個解答趁怔,我們可以從各個方面細想一下湿硝,拓展一下思路。
js的數(shù)組是一個很強大的數(shù)據(jù)結(jié)構(gòu)润努,我們可以活用其已實現(xiàn)的原生方法做很多事关斜,比如,這個例子中铺浇,判斷一個字符串是否是回文痢畜。
將字符串拆分成數(shù)組
將字符串拆分成數(shù)組其實也也有多種方法:
split()方法
3letstr_to_array =function(str){
returnstr.split('');
}
Array.prototype.map()
3letstr_to_array =function(str){
returnArray.prototype.map.call(str,function(x){returnx});
}
數(shù)組反轉(zhuǎn)reverse()
拼接成字符串join()
letcheckPalindrom =function(str){
returnstr_to_array(str).reverse().join('');
}
我們可以直接在字符串上調(diào)用數(shù)組的reduceRight()方法將字符串逆轉(zhuǎn)
letrs =Array.prototype.reduceRight.apply('abc',[function(pre,current){
returnpre + current;
},'']);
letcheckPalindrom =function(str){
returnstr == rs(str);
}
我們在學(xué)習(xí)棧這個數(shù)據(jù)結(jié)構(gòu)的時候,老師講的最生動的一個例子就是鳍侣,判斷回文有木有丁稀。
先將字符串中字符依次入棧,然后出棧組成新的字符串倚聚,即為逆轉(zhuǎn)的字符串线衫,然后做比較。
3letunique =function(array){
return[...newSet(array)];
}
我們知道Object對象的鍵是唯一的秉沼,可以利用這個特性為數(shù)組去重桶雀。
letunique =function(array){
letro = {};
letra = [];
array.forEach(item=>{
if(!ro[item]){
ro[item] = item;
ra.push(item);
}
});
returnra;
}
首先我們要先統(tǒng)計字符串中各個字符出現(xiàn)的次數(shù)矿酵,我們可以使用最笨的遍歷方法進行統(tǒng)計:
letcountChar =functioncountChar(str){
letro = {};
for(letcofstr){
if(!ro[c]){
ro[c] =1;
}else{
ro[c] ++;
}
}
returnro;
}
當然,也使用數(shù)組的reduce()方法進行統(tǒng)計矗积,因為這個方法就適合進行統(tǒng)計計算全肮。
letcountChar =functioncountChar(str){
returnArray.prototype.reduce.call(str,function(pre,current){
if(pre[current]){
pre[current] ++;
}else{
pre[current ] =1;
}
returnpre;
},{});
}
然后,從剛才統(tǒng)計出的數(shù)中查找出出現(xiàn)次數(shù)最多的字符
letfindMaxDuplicateChar =function(str){
letchars = countChar(str);
letmax =0;
letchar =null;
for(letcinchars){
if(chars[c] > max){
max = chars[c];
char = c;
}
}
returnchar;
}
原文中呢辜腺,作者教大家要合理運用+,-運算,最后給出如下答案:
functionswap(a , b){
b = b - a;
a = a + b;
b = a - b;
return[a,b];
}
module.exports = swap;
很巧妙乍恐,對吧评疗,確實是合理運用了+,-運算,但是為什么呢茵烈?合理運用*,/運算呢百匆?
a = a * b;
b = a / b;
a = a / b;
合理運用*,/好像也可以啊,對吧呜投。
其實解決問題加匈,我們應(yīng)該從根上去解決,不能簡單的說’合理運用’就敷衍過去了仑荐。
題目是雕拼,不用臨時變量,臨時變量是干嘛的呢粘招?當然是存儲臨時值用的了啥寇,對吧。
那么洒扎,不用臨時變量辑甜,我們可以把臨時值存儲到當前現(xiàn)有的變量中,對吧逊笆。
就好像是創(chuàng)造了一個臨時變量一樣栈戳。上面兩個例子中,都是用兩個變量的差或兩個變量的積作為臨時值难裆,然后存儲到其中一個變量,再由相應(yīng)的運算交換兩個變量的值镊掖。
明白了這個道理后乃戈,我們再合理一下嘛,對吧亩进,利用兩個變量的和作為臨時值:
a = a + b;
b = a - b;
a = a - b;
注意:這里慎用兩個變量的商作為臨時值症虑,因為如果兩個變量除不盡,由于js中除法運算會舍掉余數(shù)归薛,則會發(fā)生問題谍憔。
我們除了使用+,-,*,/四則運算創(chuàng)造‘臨時變量’外匪蝙,還可以使用位運算
a = a ^ b;
b = b ^ a习贫;
a = a ^ b逛球;
這個比上面的四則運算就要稍難理解了,這里運用了位運算中的異或運算苫昌。
對于異或運算的說明颤绕,還有不明白的可以回去翻翻大學(xué)的課本。
第一行 a = a ^ b;即創(chuàng)造了一個臨時值存儲在a中祟身。
b = b ^ a
相當于
b = b ^ (a ^ b) = b ^ b ^ a = a
同理奥务,
a = a ^ b
相當于
a = (a ^ b) ^ (b ^ (a ^ b)) = a^b^b^a^b = a^a^b^b^b = 0^0^b = b
可以直接遍歷數(shù)組,找出最大值和最小值袜硫,然后做差氯葬。但是呢,那樣就沒意思了婉陷,對吧帚称,我們可以直接使用數(shù)組的reduce()方法找出最大值和最小值。
letgetMaxProfit =functiongetMaxProfit(arr){
letmax_min = arr.reduce(function(pre,current){
if(pre.min > current){
pre.min = current;
}
if(pre.max < current){
pre.max = current;
}
returnpre;
},{min:arr[0],max:arr[0]});
returnmax_min.max - max_min.min;
}
當然憨攒,使用reduce()貌似還是有點麻煩啊世杀,js的Math對象不是有max()和min()方法嘛,直接用這兩個方法找出最大值和最小值就好了案渭:
letgetMaxProfit =functiongetMaxProfit(arr){
letmax =Math.max.apply(Math,arr);
letmin =Math.min.apply(Math,arr);
returnmax - min;
}
這里使用了apply方法直接調(diào)用max()和min()來獲取最大值和最小值瞻坝。
我們都知道js中apply和call兩個方法是功能相同的兩個方法,只是傳參方式不同杏瞻。call方法傳遞參數(shù)列表所刀,而apply傳遞參數(shù)對象或數(shù)組。
在es6中新添加了一個...操作符捞挥,用于將數(shù)組展開成列表浮创,具體可參見MDN上的文檔。
因此砌函,我們這里可以使用該操作符斩披,直接調(diào)用max()和min()方法:
letgetMaxProfit =functiongetMaxProfit(arr){
letmax =Math.max(...arr);
letmin =Math.min(...arr);
returnmax - min;
}
20世紀70年代末到80年代末,Digital Equipment的VAX計算機是一種非常流行的機型讹俊。它沒有布爾運算AND和OR指令垦沉,只有bis(位設(shè)置)和bic(位清除)這兩個指令。兩種指令的輸入都是一個數(shù)據(jù)字x和一個掩碼字m仍劈。他們生成一個結(jié)果z厕倍,z是由根據(jù)掩碼m的位來修改x的位得到的。使用bis指令贩疙,就是在m為1的每個位置上讹弯,將z對應(yīng)的位設(shè)置為1.使用bic指令况既,在m為1的每個位置,將z對應(yīng)的位設(shè)置為0组民。
只使用bis和bic指令棒仍,完成按位|和^運算。
//bis和bic聲明
intbis(intx,intm);
intbic(intx,intm);
//完成如下 | 運算 和 ^ 運算
intbool_or(intx,inty){
intresult = ______ ;
returnresult;
}
intbool_xor(intx,inty){
intresult = ______ ;
returnresult;
}
這其實是一道邏輯題目邪乍,由已知的bis運算邏輯和bic運算邏輯降狠,用這兩種運算去實現(xiàn)其他的運算。
bis運算就是在掩碼位為1的位設(shè)置為1庇楞,其他位不變榜配。而bic運算是在掩碼位為1的位置設(shè)置為0,其他不變吕晌。
舉個例子:
x? 11010100
m? 10100101
bis --------
11110101
x? 11010100
m? 10100101
bic --------
01010000
好了蛋褥,那怎么利用這兩種運算指令實現(xiàn)按位|和^運算呢?
仔細分析一下 bis 運算睛驳,所有掩碼位為1的位烙心,結(jié)果都是1,其他為0的位乏沸,還是按照原來的位淫茵,這不就是按位|運算么?
于是蹬跃,第一個有了:
intbool_or(intx,inty){
intresult = bis(x,y) ;
returnresult;
}
那^運算呢匙瘪?
我們都知道,異或運算運算法則為:a⊕b=(?a∧b)∨(a∧?b),a⊕b=(?a∨b)∧(a∨?b)蝶缀。至于為什么丹喻,可以列真值表驗證。
這里我們采用第一個運算法則:a⊕b=(?a∧b)∨(a∧?b)翁都。
bic運算是怎么來的呢碍论?bic(a,b)是將a上對應(yīng)b為1的位變?yōu)?,其他不變柄慰。那么鳍悠,不就是相當于先將b取反,然后和a進行按位與&運算么坐搔,就是:
bic(a,b) = a & (~b)
于是贼涩,再利用異或第一個運算法則:a⊕b=(?a∧b)∨(a∧?b)
我們可以得出:
intbool_xor(intx,inty){
intresult = bis(bic(x,y),bic(y,x));
returnresult;
}
啥也不說了,直接看我github上的代碼吧薯蝎。