大部分玩前端的小夥伴,在算法上都相對要薄弱些,畢竟調樣式、調兼容就夠掉頭髮的了,哪還有多餘的頭髮再去折騰。javascript
確實在前端中須要使用到算法的地方是比較少,但若要往高級方向發展,算法的基本功就很是重要啦。對了,算法在面試中但是必考項啊,因此爲了指望薪資,頭髮仍是得作下犧牲呀。前端
有些小夥伴認爲,刷了那些奇奇怪怪的算法題,可在工做中不多能直接派上用場,嗯,沒錯,因此學算法是件高延遲知足的事情。那麼學算法,到底收穫什麼呢?我以爲經過練習算法,培養咱們解決問題的潛意識才是最重要的。java
學習算法,最直接有效的就是刷題,刷題有不少渠道,我比較推薦 LeetCode,它有國內和國外版,很是方便。如今網上有不少大牛都分享各自刷題的解法,但百讀不如一練嘛,因此我也開個【來刷LeetCode】系列,由淺入深,分享個人解法和思路,由於個人解法確定不是最棒的,因此還會在加上我以爲優秀的解法。node
嗶嗶了這麼多,咱們如今開擼。代碼略多,建議你們先點個贊(我就是來騙讚的~)面試
兩數之和,題目描述以下:算法
給定一個整數數組 nums 和一個目標值 target,請你在該數組中找出和爲目標值的那 兩個 整數,並返回他們的數組下標。 你能夠假設每種輸入只會對應一個答案。可是,你不能重複利用這個數組中一樣的元素。數組
示例:數據結構
給定 nums = [2, 7, 11, 15], target = 9
由於 nums[0] + nums[1] = 2 + 7 = 9
因此返回 [0, 1]函數
這題,最暴力的解法就是逐個循環查找,但時間複雜度是 n*n ,太暴力的不適合咱們。 能夠這麼看,在遍歷第一個值得時候,保留這個值與target的差,而後在下次遍歷中,看看是否是與保留的差值相同,若是相同,那麼就能夠找到咱們想要的結果了。畫個簡單的表格以下:學習
序號 | 當前值 | 差值 |
---|---|---|
0 | 2 | 7 |
1 | 7 | 2 |
這樣一來,就須要記錄差值,散列表這一數據結構就排上用場了,來看看百科關於散列表的介紹:
散列表(Hash table,也叫哈希表),是根據關鍵碼值(Key value)而直接進行訪問的數據結構。也就是說,它經過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度。這個映射函數叫作散列函數,存放記錄的數組叫作散列表。 給定表M,存在函數f(key),對任意給定的關鍵字值key,代入函數後若能獲得包含該關鍵字的記錄在表中的地址,則稱表M爲哈希(Hash)表,函數f(key)爲哈希(Hash) 函數。
而js中的對象就是基於哈希表結構,因此咱們構造一個js對象便可,value是當前遍歷到的值,key是其與目標值的差。
這是個人解法以下:
/** * @param {number[]} nums * @param {number} target * @return {number[]} */
var twoSum = function (nums, target) {
let map = {};
let result = []
for (let index = 0;index <= nums.length;index++) {
const val = nums[index];
if (map[val] !== undefined) {
result.push(map[val], index);
break;
}
const a = target - val;
map[a] = index
}
return result;
};
// nums = [2, 6, 3, 15], target = 9
// twoSum(nums,target)
複製代碼
兩數之和,題目描述以下:
給出兩個 非空 的鏈表用來表示兩個非負的整數。其中,它們各自的位數是按照 逆序 的方式存儲的,而且它們的每一個節點只能存儲 一位 數字。
若是,咱們將這兩個數相加起來,則會返回一個新的鏈表來表示它們的和。
您能夠假設除了數字 0 以外,這兩個數都不會以 0 開頭。
示例:
輸入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
輸出:7 -> 0 -> 8
緣由:342 + 465 = 807
看到這題,個人第一想法就是把鏈表的每一個節點的值合併成一個整形,而後在相加,最後在轉換成鏈表,思路很是簡單暴力。可等我寫完,測試用例一跑,結果以下
緣由是 JavaScript 中Number的精度是16位,超過了就會出現精讀丟失,看來直接轉換而後相加的方式不行啊,不要緊,那就用數組模擬大數相加。完整的步驟以下ListNode
結構變成二維數組ListNode
結構個人解法以下
/** * 將多個ListNode結構變成二維數組,在計算該二維數組各個節點的和 * 如傳入的兩個ListNode對象 {val:2,next:{val:3,next:null}} {val:4,next:{val:5,next:null}} * 轉成以下二維數組 [ [2,3], [4,5] ], 計算兩數組和 ,返回 [6,8] * @param {...any} list */
function addListNode(...list) {
const valList = list.map((node) => {
const list = [];
while (node) {
list.push(Number(node.val));
node = node.next;
}
return list;
})
return arraySum(valList);
}
/** * 計算數組的和 * @param {*} list */
function arraySum(list) {
return list.reduce((result, item) => {
return arrayItemSum(result, item);
}, [])
}
/** * 計算傳入的數組的和 * @param {Array} a * @param {Array} b */
function arrayItemSum(a, b) {
let logArray = a;
let sortArray = b;
if (b.length > a.length) {
logArray = b;
sortArray = a;
}
let addOne = 0; //滿十進一
const result = logArray.reduce((result, val, index) => {
const sum = (result[index] || 0) + val + addOne;
addOne = 1;
const mod = sum % 10;
const div = sum / 10;
if (div < 1) {
result[index] = mod;
addOne = 0;
} else if (div > 1) {
result[index] = mod;
} else {
result[index] = 0;
}
return result;
}, sortArray)
if(addOne){
result.push(1);
}
if (!result[result.length - 1]) {
result.pop(1)
}
return result;
}
/** * 數組構建成 ListNode 結構 * @param {*} numList */
function numToListNode(numList) {
let preNode = undefined;
return numList.reduce((result, val) => {
let node = new ListNode(val);
if (preNode) {
preNode.next = node;
preNode = node
} else {
result = preNode = node
}
return result
}, new ListNode(0))
}
var addTwoNumbers = function (l1, l2) {
return numToListNode(addListNode(l1, l2));
};
複製代碼
看完代碼,你們是否是以爲代碼很是長,尤爲是 arrayItemSum
這個求和的函數,其實,這題考的是鏈表的操做,但被我硬生生的把鏈表拆數組,最後變成了js如何實現大數相加,手動狗頭.jpg
我leetcode上看到個很是優秀的解法,在遞歸中將每一個節點中的val相加,在將餘數傳入遞歸函數中,直到兩個鏈表都遍歷完成
var addTwoNumbers = function(l1, l2) {
const addNumbers = (l1, l2, extra) => {
let sum = (l1 ? l1.val : 0) + (l2 ? l2.val : 0) + extra;
const node = new ListNode(sum % 10);
let nl1 = l1 ? l1.next : null;
let nl2 = l2 ? l2.next : null;
if (nl1 || nl2 || sum > 9) {
node.next = addNumbers(nl1, nl2, Math.floor(sum / 10))
}
return node;
};
return addNumbers(l1, l2, 0);
};
複製代碼
刷leetcode一時苦,一直刷終會爽,加油ヾ(◍°∇°◍)ノ゙