看到有一篇寫前端面試中常見的算法文章,里面的例子很簡單,但也挺有趣。
重要的是,其實每個問題,都不止一個解答,我們可以從各個方面細想一下,拓展一下思路。
判斷一個字符串是否回文
利用js數組實現
js的數組是一個很強大的數據結構,我們可以活用其已實現的原生方法做很多事,比如,這個例子中,判斷一個字符串是否是回文。
步驟:
-
將字符串拆分成數組
將字符串拆分成數組其實也也有多種方法:-
split()方法
123let str_to_array = function(str){return str.split('');} -
Array.prototype.map()
123let str_to_array = function (str) {return Array.prototype.map.call(str,function(x){return x});}
-
-
數組反轉
reverse()
- 拼接成字符串
join()
1
2
3
|
let checkPalindrom = function(str){
return str_to_array(str).reverse().join('');
}
|
活用數組的reduceRight()方法
我們可以直接在字符串上調用數組的reduceRight()方法將字符串逆轉
1
2
3
|
let rs = Array.prototype.reduceRight.apply('abc',[function(pre,current){
return pre + current;
},
'']);
|
1
2
3
|
let checkPalindrom = function(str){
return str == rs(str);
}
|
使用棧數據結構
我們在學習棧這個數據結構的時候,老師講的最生動的一個例子就是,判斷回文有木有。
先將字符串中字符依次入棧,然后出棧組成新的字符串,即為逆轉的字符串,然后做比較。
去掉一些整型數組中重復的值
直接使用es6的Set
1
2
3
|
let unique = function(array){
return [...new Set(array)];
}
|
使用Object
我們知道Object對象的鍵是唯一的,可以利用這個特性為數組去重。
1
2
3
4
5
6
7
8
9
10
11
12
|
let unique = function (array) {
let ro = {};
let ra = [];
array.forEach(
item=>{
if(!ro[item]){
ro[item] = item;
ra.push(item);
}
});
return ra;
}
|
統計一個字符串出現最多的字母
首先我們要先統計字符串中各個字符出現的次數,我們可以使用最笨的遍歷方法進行統計:
1
2
3
4
5
6
7
8
9
10
11
|
let countChar = function countChar(str){
let ro = {};
for(let c of str){
if(!ro[c]){
ro[c] =
1;
}
else{
ro[c] ++;
}
}
return ro;
}
|
當然,也使用數組的reduce()方法進行統計,因為這個方法就適合進行統計計算。
1
2
3
4
5
6
7
8
9
10
|
let countChar = function countChar(str){
return Array.prototype.reduce.call(str,function(pre,current){
if(pre[current]){
pre[current] ++;
}
else{
pre[current ] =
1;
}
return pre;
},{});
}
|
然后,從剛才統計出的數中查找出出現次數最多的字符
1
2
3
4
5
6
7
8
9
10
11
12
|
let findMaxDuplicateChar = function (str){
let chars = countChar(str);
let max = 0;
let char = null;
for(let c in chars){
if(chars[c] > max){
max = chars[c];
char = c;
}
}
return char;
}
|
不用臨時變量,交換兩個變量的值
原文中呢,作者教大家要合理運用+
,-
運算,最后給出如下答案:
1
2
3
4
5
6
7
|
function swap(a , b) {
b = b - a;
a = a + b;
b = a - b;
return [a,b];
}
module.exports = swap;
|
很巧妙,對吧,確實是合理運用了+
,-
運算,但是為什么呢?合理運用*
,/
運算呢?
1
2
3
|
a = a * b;
b = a / b;
a = a / b;
|
合理運用*
,/
好像也可以啊,對吧。
其實解決問題,我們應該從根上去解決,不能簡單的說’合理運用’就敷衍過去了。
題目是,不用臨時變量,臨時變量是干嘛的呢?當然是存儲臨時值用的了,對吧。
那么,不用臨時變量,我們可以把臨時值存儲到當前現有的變量中,對吧。
就好像是創造了一個臨時變量一樣。上面兩個例子中,都是用兩個變量的差或兩個變量的積作為臨時值,然后存儲到其中一個變量,再由相應的運算交換兩個變量的值。
明白了這個道理后,我們再合理一下嘛,對吧,利用兩個變量的和作為臨時值:
1
2
3
|
a = a + b;
b = a - b;
a = a - b;
|
注意:這里慎用兩個變量的商作為臨時值,因為如果兩個變量除不盡,由於js中除法運算會舍掉余數,則會發生問題。
我們除了使用+
,-
,*
,/
四則運算創造‘臨時變量’外,還可以使用位運算
1
2
3
|
a = a ^ b;
b = b ^ a;
a = a ^ b;
|
這個比上面的四則運算就要稍難理解了,這里運用了位運算中的異或
運算。
對於異或運算的說明,還有不明白的可以回去翻翻大學的課本。
第一行 a = a ^ b;即創造了一個臨時值存儲在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
找出數組中最大差值
可以直接遍歷數組,找出最大值和最小值,然后做差。但是呢,那樣就沒意思了,對吧,我們可以直接使用數組的reduce()方法找出最大值和最小值。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
let getMaxProfit = function getMaxProfit(arr) {
let max_min = arr.reduce(function(pre,current){
if(pre.min > current){
pre.min = current;
}
if(pre.max < current){
pre.max = current;
}
return pre;
},{
min:arr[0],max:arr[0]});
return max_min.max - max_min.min;
}
|
當然,使用reduce()貌似還是有點麻煩啊,js的Math對象不是有max()和min()方法嘛,直接用這兩個方法找出最大值和最小值就好了啊:
1
2
3
4
5
|
let getMaxProfit = function getMaxProfit(arr){
let max = Math.max.apply(Math,arr);
let min = Math.min.apply(Math,arr);
return max - min;
}
|
這里使用了apply方法直接調用max()和min()來獲取最大值和最小值。
我們都知道js中apply和call兩個方法是功能相同的兩個方法,只是傳參方式不同。call方法傳遞參數列表,而apply傳遞參數對象或數組。
在es6中新添加了一個...
操作符,用於將數組展開成列表,具體可參見MDN上的文檔。
因此,我們這里可以使用該操作符,直接調用max()和min()方法:
1
2
3
4
5
|
let getMaxProfit = function getMaxProfit(arr){
let max = Math.max(...arr);
let min = Math.min(...arr);
return max - min;
}
|
位操作
20世紀70年代末到80年代末,Digital Equipment的VAX計算機是一種非常流行的機型。它沒有布爾運算AND和OR指令,只有bis(位設置)和bic(位清除)這兩個指令。兩種指令的輸入都是一個數據字x和一個掩碼字m。他們生成一個結果z,z是由根據掩碼m的位來修改x的位得到的。使用bis指令,就是在m為1的每個位置上,將z對應的位設置為1.使用bic指令,在m為1的每個位置,將z對應的位設置為0。
只使用bis和bic指令,完成按位|
和^
運算。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//bis和bic聲明
int bis(int x,int m);
int bic(int x,int m);
//完成如下 | 運算 和 ^ 運算
int bool_or(int x,int y){
int result = ______ ;
return result;
}
int bool_xor(int x,int y){
int result = ______ ;
return result;
}
|
這其實是一道邏輯題目,由已知的bis運算邏輯和bic運算邏輯,用這兩種運算去實現其他的運算。
bis運算就是在掩碼位為1的位設置為1,其他位不變。而bic運算是在掩碼位為1的位置設置為0,其他不變。
舉個例子:
1
2
3
4
5
6
7
8
9
10
|
x 11010100
m 10100101
bis --------
11110101
x 11010100
m 10100101
bic --------
01010000
|
好了,那怎么利用這兩種運算指令實現按位 |
和 ^
運算呢?
仔細分析一下 bis 運算,所有掩碼位為1的位,結果都是1,其他為0的位,還是按照原來的位,這不就是按位 |
運算么?
於是,第一個有了:
1
2
3
4
|
int bool_or(int x,int y){
int result = bis(x,y) ;
return result;
}
|
那 ^
運算呢?
我們都知道,異或運算運算法則為:a⊕b=(¬a∧b)∨(a∧¬b)
,a⊕b=(¬a∨b)∧(a∨¬b)
。至於為什么,可以列真值表驗證。
這里我們采用第一個運算法則:a⊕b=(¬a∧b)∨(a∧¬b)
。
bic運算是怎么來的呢?bic(a,b)是將a上對應b為1的位變為0,其他不變。那么,不就是相當於先將b取反,然后和a進行按位與&
運算么,就是:
1
|
bic(a,b) = a & (~b)
|
於是,再利用異或第一個運算法則:a⊕b=(¬a∧b)∨(a∧¬b)
我們可以得出:
1
2
3
4
|
int bool_xor(int x,int y){
int result = bis(bic(x,y),bic(y,x));
return result;
}
|
js實現二叉搜索樹
啥也不說了,直接看我github上的代碼吧。
https://github.com/coolcao/dsa_js/blob/master/src/BSTree.js