從今天起,作一個牛X的人,早起,健身,修煉算法
從今天起,關心代碼質量,我有一個夢想,朝九晚五,年薪百萬
從今天起,和每個親人通訊,告訴他們個人決心
那成功的天使告訴個人
我想告訴每個人
給每個文件、每個變量取一個溫暖的名字
陌生人,我也爲你祝福
願你有一個燦爛的前程
願你的頭髮再也不減小
願你明天還是公司棟樑
而我,只願朝九晚五,年薪百萬
git
function balance(str) {
let [first, ...others] = str;
let stack = [first]
while(others.length) {
let stact_last = stack[stack.length-1];
let others_first = others.shift();
if(!if_match(stact_last,others_first)) {
stack.push(others_first);
}else{
stack.pop();
}
return stack.length?false:true;
}
}
function if_match(left,right) {
return (left === '[' && right === ']') || (left === '(' && right === ')');
}
複製代碼
思路:
這個解法基於棧(後進先出)。首先,若是隻有一個字符,則必然不平衡。
若是大於一個字符。咱們把字符串
中的每個字符取出並依次放入棧
中,假如最新放入棧
中的字符與棧
的最後一個字符匹配(match方法返回true),則刪掉棧
中的最後一個。
就這樣把字符串
循環一遍後,若是棧
中的字符被刪光,則說明這個字符串
是平衡的。
es6
字符串
中的字符依次放到
棧
中,若是相鄰兩個不匹配,則放在
棧
的頂端;若是匹配,則「消掉」。
斐波那契數列的前兩項是1,後面的每一項都等於前兩項之和,因此用遞歸很容易實現,不過須要注意性能問題。
若是直接遞歸的話會出現大量重複計算,試想遞歸數字10的時候會依次遞歸數字九、八、七、6……,可是當再遞歸數字9的時候會在遞歸數字八、七、6……,其中八、七、6……被屢次遞歸。因此聲明瞭一個catch數組用來緩存,遞歸以前判斷若是該項沒有值纔會進行遞歸。算法
function fibonacciCatch(num) {
let cache = [1, 1];
(function fibonacci(n) {
return typeof cache[n] == 'number' ? cache[n] : (cache[n] = fibonacci(n - 1) + fibonacci(n - 2))
})(num - 1);
return cache;
}
console.log(fibonacciCatch(10)) //[ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ]
複製代碼
下面是另外一種解法,這種解法不存在屢次計算問題,思路是:
首先記錄前兩個值,以後的每個yeild都返回前兩個值之和,固然還要更新前兩個值爲最新的。
對generator不瞭解的同窗能夠參考阮一峯老師的書籍:es6入門-generator數組
function* fibonacci(n) {
let a = 1, b = 1
yield a
yield b
while (true) {
const t = b
b = a + b
a = t
yield b
}
}
let it = fibonacci()
let result = Array.from(Array(10), it.next, it).map(x => x.value)
console.log(result) //[ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ]
複製代碼
笛卡爾乘積是指在數學中,兩個集合X和Y的笛卡尓積(Cartesianproduct)又稱直積,表示爲
X×Y
,第一個對象是X的成員而第二個對象是Y的全部可能有序對的其中一個成員瀏覽器
function cartesion(...arys) {
if (arys.length == 0) return []
if (arys.length == 1) return arys
return arys.reduce((prev, item) => {
let temp = []
for (let i = 0; i < prev.length; i++) {
for (let j = 0; j < item.length; j++) {
temp.push(Array.isArray(prev[i]) ? [...prev[i], item[j]] : [prev[i], item[j]])
}
}
return temp
})
}
console.log(cartesion([1, 2, 3], ['a', 'b'], ['x', 'y']))
//[[1, 'a', 'x'],[1, 'a', 'y'],[1, 'b', 'x'],[1, 'b', 'y'],[2, 'a', 'x'],[2, 'a', 'y'],[2, 'b', 'x'],[2, 'b', 'y'],[3, 'a', 'x'],[3, 'a', 'y'],[3, 'b', 'x'],[3, 'b', 'y']]
複製代碼
思路:
這道題主要是訓練reduce的用法,咱們依次組合兩個數組中的每一項,而後把結果做爲一個總體繼續和接下來的數組進行組合。
這裏還須要注意es6擴展符...
的用法,你們能夠把這個符號去掉再執行一遍程序,看看有什麼不一樣。緩存
var maxProfit = function (prices) {
if (!prices.length) return 0 //越界判斷
var max = 0
for (var i = 0; i < prices.length; i++) {
var diff = prices[i] - prices[i - 1]
if (diff > 0) {
max += diff
}
}
return max
};
複製代碼
思路是:
一、聲明一個max記錄利潤
二、從第二項開始循環數組,判斷明天的價格是否高於今天
三、若是高於前一天,爲了利潤最大,今天應該買入,因此最大利潤max
增長相應的差價bash
function bubbleSort(ary) {
for (let i = 0; i < ary.length; i++) {
for (let j = i + 1; j < ary.length; j++) {
if (ary[i] > ary[j]) {
//交換位置
let temp = ary[i]
ary[i] = ary[j]
ary[j] = temp
}
}
}
return ary
}
console.log(bubbleSort([1, 9, 4, 2, 55, 3, 0, -14])) //[ -14, 0, 1, 2, 3, 4, 9, 55 ]
複製代碼
思路:冒泡排序的思路就是兩個循環,就是讓數組中的每一項都與後面全部的數比較,若是這一項比後面的數字大,則交換位置。
冒泡排序的思路簡單易於理解,可是它的時間複雜度是全部排序中最高的O(n²),這種排序方法基本不會在項目中使用。閉包
function slectionSort(ary) {
let min
for (var i = 0; i < ary.length - 1; i++) {
min = i
for (var j = i + 1; j < ary.length; j++) {
if (ary[j] < ary[min]) {
min = j
}
}
let temp = ary[min]
ary[min] = ary[i]
ary[i] = temp
}
return ary
}
console.log(slectionSort([1, 9, 4, 2, 55, 3, 0, -14])) //[ -14, 0, 1, 2, 3, 4, 9, 55 ]
複製代碼
思路:
選擇排序的思路跟冒泡類似,都是兩個for循環。
冒泡是比較先後兩個交換位置,而選擇排序是先找到後面全部數字最小的,再與當前數字交換位置。
代碼中min記錄了內層循環的最小值,循環結束,交換外層循環當前項與min項
app
function insertSort(ary) {
for(let i=1;i<ary.length;i++){
let j = i;
let temp = ary[i];
//循環找到比它小的是第j-1項
while(j>0 && ary[j - 1]>temp){
ary[j] = ary[j-1]
j--
}
//把第j項賦值爲temp,至關於把temp插入到第j-1項後面了
ary[j] = temp
}
return ary
}
複製代碼
哎,昨天雖然把插入排序寫了,可是沒有時間寫分析了,這是次日補上的,罪過罪過。
插入排序也屬於比較慢的排序,由於也是用了兩個循環。它的思路就是從第二項開始拿數字,而後用這個數字逐個與前面的數字比較,找到比它小的而後放到它後面。dom
歸併排序是一個性能很好的排序算法,Firefox瀏覽器的Array.prototype.sort
就是歸併排序,Chrome瀏覽器用的是快速排序。
function mergeSort(ary) {
if (ary.length == 1) {
return ary
}
let mindle = Math.floor(ary.length / 2)
let left = ary.slice(0, mindle)
let right = ary.slice(mindle)
return merge(mergeSort(left), mergeSort(right))
}
function merge(left, right) {
let result = []
let il = 0
let ir = 0
while (il < left.length && ir < right.length) {
if (left[il] < right[ir]) {
result.push(left[il++])
} else {
result.push(right[ir++])
}
}
while (il < left.length) {
result.push(left[il++])
}
while (ir < right.length) {
result.push(right[ir++])
}
return result
}
console.log(mergeSort([1, 9, 4, 2, 55, 3, 0, -14])) //[ -14, 0, 1, 2, 3, 4, 9, 55 ]
複製代碼
思路:歸併排序的思想是分而治之。我理解就是把一個數組從中間無限分割,直到只剩一個元素,而後把這些數組的item取出來放入最後的數組中。
放的過程就涉及到比較,也就是上面代碼的第一個while
循環,這裏面的代碼保證了永遠把最小的先放入數組中。
以前有一個問題困擾這我,若是兩個數組中有一個先被取光,那那個剩下的豈不是沒有排序?
其實剩下的那個已是排序好的了,由於遞歸的關係,數組是從小逐漸變大的,所一以前的已經排好順序了。
快排是處理大數據集最快的算法之一,它和歸併排序同樣也是一種分而治之的算法。
function fastSort(ary) {
if (ary.length == 0) {
return []
}
let pivot = ary[0]
let left = [], right = []
for (let i = 1; i < ary.length; i++) {
let current = ary[i]
if (current < pivot) {
left.push(current)
} else {
right.push(current)
}
}
return fastSort(left).concat(pivot, fastSort(right))
}
console.log(fastSort([1, 9, 4, 2, 55, 3, 0, -14])) //[ -14, 0, 1, 2, 3, 4, 9, 55 ]
複製代碼
思路:
快排採用的是遞歸的方法,將數據依次分解爲較小的數據集合(left)和較大的數據集合(right),最後把這左中右三個數組組合在一塊兒。
快排應用普遍,可是個人一個算法大牛兄弟曾經偷偷告訴我,當數據少的時候快排相對慢,可使用歸併排序進行優化。
function seqSearch(ary, data){
for(let i = 0; i< ary.length; i++){
if(ary[i] == data){
if(i > 0){
let temp = ary[i]
ary[i] = ary[i-1]
ary[i-1] = temp
}
return true
}
}
return false
}
複製代碼
思路:
這個算法很簡單,若是搜到這個數據返回true,不然返回false。只不過若是搜到的話會把這個數據向前移動一位,這樣隨着搜索次數的增長,經常使用的數據會一直往前移動,從而逐漸減小搜索次數。
function binSearch(ary, data) {
let up = ary.length - 1
let down = 0
while (down <= up) {
let min = Math.floor((up + down) / 2)
if (ary[min] < data) {
down = min + 1
} else if (ary[min] > data) {
up = min - 1
} else {
return data
}
}
return -1
}
複製代碼
思路:
二分搜索的前提條件是數據必須有序,起初設置一個上限和下限,而後經過循環比較中間的數字和數據的大小來縮小查找範圍。
var singleNumber = function(nums) {
nums = nums.sort()
for (var i = 0; i < nums.length; i = i + 2) {
var prev = nums[i]
var next = nums[i + 1]
if (prev != next) {
return prev
}
}
};
console.log(singleNumber([1, 2, 1, 3, 2, 222, 4, 222, 4, 3, 8])) //8
複製代碼
這是letCode上的一道題,思路是:
先對數組排序,而後一次兩項循環數組,比較這兩項若是不一樣,則返回第一項。
function deepClone(obj){
if(obj == null || typeof obj != 'object') return obj
let newObj = obj.constructor()
for(let key in obj.getOwnPropertyDescriptors()){
newObj[key] = deepClone(obj[key])
}
return newObj
}
複製代碼
這是一個遞歸實現,思路:
constructor
建立一個新的對象函數截流:就是防止短期內的屢次函數觸發。例如,咱們想在鼠標滾動的時候調用某個接口,鼠標滾動事件觸發很快會形成短期內屢次調用接口形成性能損耗。
function throttle(fn, interval){
let prevTime = 0
return function(){
let thisTime = new Date()
if(thisTime - prevTime >= interval){
fn.apply(null, arguments)
prevTime = thisTime
}
}
}
let throttleFn = throttle(fn, 100)
複製代碼
思路:
函數截流使用了一個高階函數實現,在閉包裏儲存一個上次函數執行的時間,返回一個新的函數。
判斷上次的時間和此次時間差是否大於傳入的間隔,大於的話執行函數並更新記錄時間。
函數防抖:與截流類似,防抖也是防止短期內函數屢次觸發,只不過截流的目的是過濾掉不符合要求的操做,而防抖是符合要求以後纔會調用函數。例如,咱們監聽輸入框的change事件,可是想在用戶輸入完成時(也就是他中止輸入一小段時間後)調用接口。
function shake(fn, interval) {
let timer = null;
return function(){
clearTimeout(timer);
let t_fn = fn.call(this)
timer = setTimeout(t_fn, interval)
}
}
複製代碼
domDiff
算法,地址在這裏 淺入淺出圖解domDIff。var intersect = function(nums1, nums2) {
let result = []
if(nums1.length>nums2.length){
let temp = nums1
nums1 = nums2
nums2 = temp
}
for (let i = 0; i < nums1.length; i++) {
let item = nums1[i]
let index = nums2.indexOf(item)
if (index > -1) {
let eve = nums2.splice(index, 1)[0]
result.push(eve)
}
}
return result
};
複製代碼
思路:
首先,咱們找到比較短的那個數組(固然這不是必須的),而後循環它,若是其中一項在另外一個數組中也存在,那麼把它記錄下來,同時刪掉另外一項裏面的這一項。最後返回記錄的數組。
例如:輸入: [1,2,3]; 輸出: [1,2,4]; 解釋: 輸入數組表示數字 123。
輸入: [1,2,9]; 輸出: [1,3,0]; 解釋: 輸入數組表示數字 130
function plusOne(digits) {
let result = []
function next(currentAry) {
if (currentAry.length) {
let l = currentAry.pop()
//若是小於9,l+1入列,不然0入列,再遞歸上一位
if (l > 8) {
result.unshift(0)
//判斷上一位有沒有值,若是沒有,給他建立一位,值爲1
!currentAry.length && result.unshift(1)
next(currentAry)
} else {
result.unshift(l + 1)
}
}
}
next(digits)
return digits.concat(result)
}
複製代碼
思路:
這個問題不難,就是把最後的一個數字+1,只不過涉及到若是最後一個是9,那麼就須要進一位+1。
還須要注意一個問題,就是若是第一位是9的狀況,例如92,9這一位的上一位已經沒有值了,因此要本身新建一位。我在代碼中已經標註。
function isValidSudoku(board) {
let columns = []
let box = {}
//循環每一列
for (let i = 0; i < board.length; i++) {
let row = board[i]
let boxRowIndex = Math.floor(i / 3)
let tempAry = []
//循環每一行
for (let j = 0; j < row.length; j++) {
let item = row[j]
//判斷每一行的重複
if (item != '.' && tempAry.indexOf(item) > -1) {
return false
} else {
tempAry.push(item)
}
//判斷每一列的重複
if (typeof columns[j] != 'undefined') {
if (item != '.' && columns[j].indexOf(item) > -1) {
return false
} else {
columns[j].push(item)
}
} else {
columns[j] = []
columns[j].push(item)
}
//判斷每個九宮格的重複
let boxColumnsIndex = Math.floor(j / 3) + ''
let boxIndex = boxRowIndex + boxColumnsIndex
if (typeof box[boxIndex] != 'undefined') {
if (item != '.' && box[boxIndex].indexOf(item) > -1) {
return false
} else {
box[boxIndex].push(item)
}
} else {
box[boxIndex] = []
box[boxIndex].push(item)
}
}
}
return true
}
複製代碼
思路:
首先,這是個二維數組,至少須要兩個循環遍歷。咱們須要判斷三種狀況:
每一行好說,咱們只須要維護一個數組tempAry
,把每一行的每個元素放進去,發現相同就返回false
。而且在循環下一行的時候清空tempAry
。
下面看每一列,和每一行思路差很少,咱們維護一個二維數組columns
,它記錄着每一列的元素,一樣是遇到重複返回false
。
九宮格稍微麻煩一些,咱們須要維護一個對象box
,由於一個九宮格佔用三行散列,全部咱們使用了boxRowIndex
和boxColumnsIndex
的拼接來標記每個九宮格,後面的思路仍是同樣,碰見相同的返回false
。
var reverseString = function(s) {
return s.split('').reverse().join('')
};
複製代碼
這幾天有些忙,落了幾天,只能在letcode上找些簡單的追上進度了,不用解釋了,轉成數組,反轉後再轉回來……
例如:輸入: 123,輸出: 321; 輸入: -123,輸出: -321; 輸入: 120,輸出: 21。
假設咱們的環境只能存儲 32 位有符號整數,其數值範圍是 [−231, 231 − 1]。根據這個假設,若是反轉後的整數溢出,則返回 0。
var reverse = function(x) {
x = x.toString()
let minus = x[0] === '-' ? '-' : ''
let result = x.split('').reverse().join('').replace(/^0+|-$/g, '')
result = Number(minus + result)
return Math.abs(result) < Math.pow(2,31)?result:0
};
複製代碼
思路:
這個很簡單,跟前一天的思路同樣,只不過須要處理「-」和「0」,同時,判斷是否反轉後的數值溢出。
var firstUniqChar = function(s) {
for (let i = 0; i < s.length; i++) {
let ifEqual = false
for (let j = 0; j < s.length; j++) {
if (i != j && s[i] === s[j]) {
ifEqual = true
}
}
if (!ifEqual) {
return i
}
}
return -1
}
複製代碼
var firstUniqChar = function(s) {
let obj = {}
for (let i = 0; i < s.length; i++) {
let item = s[i]
if (obj[item]) {
obj[item].num++
} else {
obj[item] = {
index: i,
num: 1
}
}
}
for (let key in obj) {
if (obj[key].num == 1) {
return obj[key].index
}
}
return -1
};
複製代碼
思路:
這道題有兩種解法,第一種不須要額外空間,可是時間複雜度爲O(n2),第二種時間複雜度爲O(n)可是須要申請額外空間。具體用哪種應該根據實際的場景選擇,例如字符串長度較短,能夠選擇第一種,若是太長,那麼應該選擇第二種。
var data = {
key1: 'str1',
key2: {
key3: 'str3',
key4: 'str4',
key5: {
key6: 'str6'
}
}
}
getKeys(data, 'str1') //'key1'
getKeys(data, 'str3') //'key2 key3'
getKeys(data, 'str6') //'key2 key5 key6'
複製代碼
實現:
function getKeys(data, str) {
let result = []
function search(d) {
for (let key in d) {
if (d[key] == str) {
result.push(key)
return
}
if (typeof d[key] == 'object') {
result.push(key)
search(d[key])
}
}
}
search(data)
return result.join(' ')
}
複製代碼
性能差可是簡單的解法
function shuffle2(a) {
return a.sort(function (a, b) {
return Math.random() > .5 ? 1 : -1
})
}
複製代碼
性能好的解法:
function shuffle(a) {
let len = a.length;
for (let i = 0; i < len - 1; i++) {
let index = parseInt(Math.random() * (len - i));
let top = len - i - 1;
[a[index], a[top]] = [a[top], a[index]]
}
}
複製代碼
例如:
輸入: s = "anagram", t = "nagaram";輸出: true
輸入: s = "rat", t = "car";輸出: false
var isAnagram = function(s, t) {
if (s.length != t.length) return false;
for (let i = 0; i < s.length; i++) {
let item = s[i]
t = t.replace(new RegExp(item), '')
}
return t == ''
};
複製代碼
堅持!堅持!仍是堅持!