2018年年末, 呆了不到一年的公司就這樣解散了。遙想當初3月份剛剛加入公司的時候, 公司剛剛拿到數億的B輪融資。6月份又是數億元的B+輪融資。又是換寫字樓又是在外灘大屏幕上打廣告好不熱鬧,結果12月公司就關門了。真是世事無常啊。javascript
給定一個整數數組 nums 和一個目標值 target,請你在該數組中找出和爲目標值的那 兩個 整數,並返回他們的數組下標。你能夠假設每種輸入只會對應一個答案。可是,你不能重複利用這個數組中一樣的元素。html
給定 nums = [2, 7, 11, 15], target = 9。由於 nums[0] + nums[1] = 2 + 7 = 9。因此返回 [0, 1]java
最簡單的方法是經過兩層for循環進行遍歷, 使用暴力的查找兩個子元素。可是這種方法的時間複雜度爲O(n^2)。在大數據量的測試用例的狀況下執行時間超時。那麼咱們有什麼辦法能夠將時間複雜度降下來嗎?這時咱們能夠用到HashMap。經過HashMap咱們能夠將時間複雜度降爲O(2n)。若是是有序數組的狀況下, 時間複雜度能夠是O(n), 詳見下題。算法
/** * @param {number[]} nums * @param {number} target * @return {number[]} */ var twoSum = function (nums, target) { let hashMap = new Map() // 初始化hashMap for (let i = 0; i < nums.length; i++) { if (!hashMap.has(nums[i])) { hashMap.set(nums[i], i) } } for (let i = 0; i < nums.length; i++) { let diff = target - nums[i] if (hashMap.has(diff) && hashMap.get(diff) !== i) { return [i, hashMap.get(diff)] } } };
本題是 1. 兩數之和的引申問題。若是將無限數組變爲有序數組, 有什麼更好的辦法解決問題嗎?
這一題咱們可使用對撞指針或者說雙指針的思路解決它, 並能夠將時間複雜度控制在O(n)。具體思路見下圖。數組
/** * @param {number[]} numbers * @param {number} target * @return {number[]} */ var twoSum = function(numbers, target) { const len = numbers.length let start = 0 let end = len - 1 while (start < end) { if (numbers[start] + numbers[end] === target) { return [start + 1, end + 1] } else if (numbers[start] + numbers[end] < target) { start += 1 } else { end -= 1 } } return [] };
給定一個字符串,請你找出其中不含有重複字符的 最長子串 的長度。數據結構
輸入: "abcabcbb"。輸出: 3 。解釋: 由於無重複字符的最長子串是 "abc",因此其長度爲 3。測試
輸入: "bbbbb"。輸出: 1。解釋: 由於無重複字符的最長子串是 "b",因此其長度爲 1。大數據
比較簡單的思路是依舊使用雙層for循環。代碼以下。時間複雜度爲O(n^3)。indexOf的複雜度爲O(n)。更好的解決辦法是使用雙指針配合HashMap。雖然使用了額外的空間。可是能夠將時間複雜度爲O(n)。具體思路見下圖。spa
// 暴力循環 if (s.length === 0) return 0 if (s.length === 1) return 1 let maxLen = 0 for1: for (let i = 0; i < s.length; i++) { let str = s[i] for2: for (let j = i + 1; j < s.length; j++) { maxLen = Math.max(maxLen, str.length) if (str.indexOf(s[j]) < 0) { str += s[j] maxLen = Math.max(maxLen, str.length) } else { continue for1 } } } return maxLen
/** * @param {string} s * @return {number} */ var lengthOfLongestSubstring = function (s) { // 使用雙指針解決 + hash const len = s.length let hashMap = new Map() let start = 0 let end = 0 let maxLen = 0 while (end < len) { if (!hashMap.has(s[end])) { hashMap.set(s[end], 1) maxLen = Math.max(maxLen, [...hashMap.keys()].length) end += 1 } else { hashMap.delete(s[start]) start += 1 } } return maxLen };
將一個給定字符串根據給定的行數,以從上往下、從左到右進行 Z 字形排列。設計
好比輸入字符串爲 "LEETCODEISHIRING" 行數爲 3 時,排列以下:
L C I R E T O E S I I G E D H N
以後,你的輸出須要從左往右逐行讀取,產生出一個新的字符串,好比:"LCIRETOESIIGEDHN"。
這道題目個人解決思路是將字符串轉化爲二維數組。輸入時按照N字形輸入。最後逐行讀取二維數組,並將二維數組中空的填充去除。返回最後的結果。經過推導可知。當行數爲 2 時, 斜線上的字母數量爲0。當行數爲 3 時, 斜線上的字母數量爲1。當行數爲 4 時, 斜線上的字母數量爲2。
// 規律以下 L A C I R E T O E S L C I R E T O E S I I G E D H N L D R E O E I I E C I H N T S G
/** * @param {string} s * @param {number} numRows * @return {string} */ var convert = function (s, numRows) { if (numRows === 1) return s let result = [] let matrix = [] let rowCounter = 0 let prevRowCounter = 0 let colCounter = 0 let prevColCounter = 0 const other = numRows - 2 for (let i = 0; i < numRows; i++) { matrix.push([]) } // 填充二維數組 for (let i = 0; i < s.length; i++) { matrix[rowCounter][colCounter] = s[i] if (prevRowCounter <= rowCounter) { prevRowCounter = rowCounter if (rowCounter >= numRows - 1) { rowCounter -= 1 colCounter += 1 } else { rowCounter += 1 } } else { prevRowCounter = rowCounter if (rowCounter <= 0) { rowCounter += 1 } else { rowCounter -= 1 colCounter += 1 } } } for (let i = 0; i < matrix.length; i++) { for (let j = 0; j < matrix[i].length; j++) { if (matrix[i][j] !== undefined) { result.push(matrix[i][j]) } } } return result.join('') };
本題的思路依然是使用對撞指針。咱們在這裏首先須要明確一個概念, 水的面積和高度和寬度有關。高度的取決於兩條邊框中最小的一邊。具體思路見下圖。
// https://leetcode-cn.com/explore/orignial/card/all-about-array/232/two-pointers/969/ /** * @param {number[]} height * @return {number} */ var maxArea = function (height) { if (height.length === 1 || height.length === 0) { return 0 } const len = height.length let start = 0 let end = len - 1 let max = 0 while (start < end) { max = Math.max(max, (Math.min(height[start], height[end]) * (end - start))) if (height[start] <= height[end]) { start += 1 } else { end -= 1 } } return max };
給定一個包含 n 個整數的數組 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?找出全部知足條件且不重複的三元組。
注意:答案中不能夠包含重複的三元組。
例如, 給定數組 nums = [-1, 0, 1, 2, -1, -4], 知足要求的三元組集合爲:[ [-1, 0, 1], [-1, -1, 2] ]
最簡單最暴力的方法是使用三層for循環進行查找。可是時間複雜度爲O(n^3)。咱們的目標是將時間複雜度降爲O(n^2)。咱們只須要將原數組進行排序後, 結合167. 兩數之和 II - 輸入有序數組這道題目的思路(對撞指針)就能夠將時間複雜度控制在O(n^2)。
/** * @param {number[]} nums * @return {number[][]} */ var threeSum = function(nums) { let result = [] let hashMap = new Map() // 容錯處理 if (nums.length < 3) return [] // 容錯處理 if (nums.length === 3) { if (nums[0] + nums[1] + nums[2] === 0) return [nums] return [] } nums = nums.sort((a, b) => a - b) for (let i = 0; i < nums.length - 3; i++) { let start = i + 1 let end = nums.length - 1 let target = 0 - nums[i] while (start < end) { if (nums[start] + nums[end] === target) { let arr = [nums[i], nums[start], nums[end]] let key = arr.join('') if (!hashMap.has(key)) { hashMap.set(key, true) result.push(arr) } end -= 1 start += 1 } else if (nums[start] + nums[end] > target) { end -= 1 } else { start += 1 } } } return result };
給定一個包括 n 個整數的數組 nums 和 一個目標值 target。找出 nums 中的三個整數,使得它們的和與 target 最接近。返回這三個數的和。假定每組輸入只存在惟一答案。
例如,給定數組 nums = [-1,2,1,-4], 和 target = 1。 與 target 最接近的三個數的和爲 2. (-1 + 2 + 1 = 2)。
本題的思路與15. 三數之和基本相似。區別在於咱們須要在循環中記錄的是最小的差值(Math.abs(target - sum))。
/** * @param {number[]} nums * @param {number} target * @return {number} */ var threeSumClosest = function(nums, target) { let diff = Infinity let sums = undefined if (nums.length <= 3) return nums.reduce((a, b) => a + b, 0) nums = nums.sort((a, b) => a - b) for (let i = 0; i < nums.length; i++) { let start = i + 1 let end = nums.length - 1 while (start < end) { if (Math.abs(target - (nums[i] + nums[start] + nums[end])) < diff) { // 最接近的和 sums = nums[i] + nums[start] + nums[end] // 當前最小的差值 diff = Math.abs(target - (nums[i] + nums[start] + nums[end])) } if (nums[i] + nums[start] + nums[end] > target) { end -= 1 } else if (nums[i] + nums[start] + nums[end] < target) { start += 1 } else { return target } } } return sums };
給定一個只包括 '(',')','{','}','[',']' 的字符串,判斷字符串是否有效。
本題的解決辦法須要使用棧這個數據結構(javascript中咱們使用數組進行模擬棧), 具體思路見下圖
/** * @param {string} s * @return {boolean} */ var isValid = function (s) { if (s.length === 0) return true if (s.length === 1) return false let queue = [] for (let i = 0; i < s.length; i++) { if (!queue.length) { queue.push(s[i]) } else { let tail = queue[queue.length - 1] if (s[i] === '}' && tail === '{') { queue.pop() continue } else if (s[i] === ']' && tail === '[') { queue.pop() continue } else if (s[i] === ')' && tail === '(') { queue.pop() continue } else { queue.push(s[i]) } } } if (!queue.length) { return true } else { return false } };
給定一個沒有重複數字的序列,返回其全部可能的全排列。輸入: [1,2,3]。 輸出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]
此題擁有兩種思路字典法與遞歸法。遞歸法較爲容易理解。我採用的也是遞歸法,代碼以下。
/** * @param {number[]} nums * @return {number[][]} */ var permute = function(nums) { let result = [] if (nums.length <= 1) result = [nums] function exchange (title, arr) { for (let i = 0; i < arr.length; i++) { let cloneArr = [...arr] let newFirst = [...title, ...cloneArr.splice(i, 1)] if (cloneArr && cloneArr.length) { exchange(newFirst, cloneArr) } else { result.push(newFirst) } } } for (let i = 0; i < nums.length; i++) { let cloneArr = [...nums] let first = cloneArr.splice(i, 1) exchange(first, cloneArr) } return result };
給定一個整數數組 nums ,找到一個具備最大和的連續子數組(子數組最少包含一個元素),返回其最大和。
示例:
輸入: [-2,1,-3,4,-1,2,1,-5,4],
輸出: 6
解釋: 連續子數組 [4,-1,2,1] 的和最大,爲 6。
若是要在O(n)的時間複雜度的狀況下解開本題, 須要使用動態規劃的思想。可是本人能力有限, 動態規劃不是很懂。這裏只能說一個大概的思路,敬請諒解。
咱們從數組中的第一位開始循環求和
若是sum(和) < 0。接下來的一位next不管大於0仍是小於0, 都應當取代當前的負數sum。由於若是next < 0, sum + next 將會更小, 因此應當捨棄以前的sum。若是next大於0, sum更應當從next開始從新計算。
若是sum(和) > 0。若是接下來的一位next與當前的sum的和大於MAX, next與當前sum的和將成爲新的MAX。不然繼續向下迭代。
/** * @param {number[]} nums * @return {number} */ var maxSubArray = function (nums) { if (nums.length <= 1) return nums[0] let sum = nums[0] let MAX = sum for (let i = 1; i < nums.length; i++) { if (sum >= 0) { if (sum + nums[i] >= MAX) { MAX = sum + nums[i] } sum = sum + nums[i] } else { if (nums[i] >= MAX) { MAX = nums[i] } sum = nums[i] } } return MAX };
所謂的不一樣路徑, 其實就是求排列組合。好比 3 * 7 的網格中。機器人從起點到終點所須要的步驟能夠抽象爲一個數組。[bottom, bottom, right, right, right, right, right, right],全部的路徑,便是這個數組的全部排列組合。
另一種思路, 第一行全部網格的可能路徑數均爲1。第一列全部網格的數可能的路徑均爲1。經過推導能夠獲得以下的表格。終點可能的路徑爲28。
/** * @param {number} m * @param {number} n * @return {number} */ var uniquePaths = function(m, n) { // 思路1 let matrix = [] for (let i = 0; i < n; i++) { let arr = new Array(m).fill(1) matrix.push(arr) } for (let i = 1; i < n; i++) { for (let j = 1; j < m; j++) { matrix[i][j] = matrix[i - 1][j] + matrix[i][j - 1] } } return matrix[n-1][m-1] // 思路二, 可行, 可是會超出時間限制 let arr = [] let hashMap = new Map() for (let i = 0; i < m - 1; i++) { arr.push('m') } for (let i = 0; i < n - 1; i++) { arr.push('n') } if (arr.length <= 1) return 1 function exchange (title, arr) { for (let i = 0; i < arr.length; i++) { let cloneArr = [...arr] let newFirst = [...title, ...cloneArr.splice(i, 1)] if (cloneArr && cloneArr.length) { exchange(newFirst, cloneArr) } else { let key = newFirst.join('') if (!hashMap.has(key)) { hashMap.set(key, true) } } } } for (let i = 0; i < arr.length; i++) { let cloneArr = [...arr] let first = cloneArr.splice(i, 1) exchange(first, cloneArr) } return hashMap.size };
思路1使用遞歸法, 比較簡單不在贅述
思路2與62. 不一樣路徑相似。但不一樣的是出現了障礙物這個變量。因此咱們在初始化表格的第一行與第一列的時候須要格外的注意。假設一個 3 * 7的網格。具體思路見下圖。
假設地圖以下
初始化第一行(若是第一行中的前一個爲障礙物話, 那麼後續可能路徑的均爲0),與第一列後(若是第一列中的前一個爲障礙物話, 那麼後續可能路徑的均爲0), 障礙物由於自己不能行走因此可能路徑數直接設置爲0。
接下來的方法同62. 不一樣路徑同樣。
/** * @param {number[][]} obstacleGrid * @return {number} */ var uniquePathsWithObstacles = function (obstacleGrid) { // 思路1, 使用動態規劃和遞歸 // 沒有經過大數據量的測試用例 let counter = 0 const targetX = obstacleGrid[0].length - 1 const targetY = obstacleGrid.length - 1 /** * @param {number} x 當前矩陣的x座標 * @param {number} y 當前矩陣的y座標 * @param {string} direction 方向 right, bottom */ const pathfinding = (x, y, direction) => { switch (direction) { case 'right': x = x + 1 break case 'bottom': y = y + 1 break default: break } // 遇到障礙物或者越界的狀況下, 思路一條 if (y >= targetY + 1) { return } if (x >= targetX + 1) { return } if (obstacleGrid[y][x] === 1) { return } if (x === targetX && y === targetY) { counter += 1 } else if (x !== targetX && y === targetY) { // 只能向右走 pathfinding(x, y, 'right') } else if (x === targetX && y !== targetY) { // 只能向下走 pathfinding(x, y, 'bottom') } else { // 可能向右走 // 可能向下走 pathfinding(x, y, 'right') pathfinding(x, y, 'bottom') } } pathfinding(0, 0) return counter // 思路二 // 帶有條件的初始化第一行與第一列 // 初始化x方向 // 初始化y方向 const xLen = obstacleGrid[0].length const yLen = obstacleGrid.length for (let i = 0; i < xLen; i++) { if (i - 1 >= 0) { if (obstacleGrid[0][i-1] === 0) { obstacleGrid[0][i] = 0 } else if (obstacleGrid[0][i-1] === 1 && obstacleGrid[0][i] !== 1) { obstacleGrid[0][i] = 1 } else if (obstacleGrid[0][i] == 1) { obstacleGrid[0][i] = 0 } } else { if (obstacleGrid[0][i] === 0) { obstacleGrid[0][i] = 1 } else { obstacleGrid[0][i] = 0 } } } for (let i = 0; i < yLen; i++) { if (i - 1 >= 0) { if (obstacleGrid[i-1][0] === 0) { obstacleGrid[i][0] = 0 } else if (obstacleGrid[i-1][0] !== 0 && obstacleGrid[i][0] !== 1) { obstacleGrid[i][0] = 1 } else if (obstacleGrid[i-1][0] !== 0 && obstacleGrid[i][0] === 1) { obstacleGrid[i][0] = 0 } } } for (let i = 1; i < yLen; i++) { for (let j = 1; j < xLen; j++) { if (obstacleGrid[i][j] === 1) { obstacleGrid[i][j] = 0 } else { obstacleGrid[i][j] = obstacleGrid[i - 1][j] + obstacleGrid[i][j - 1] } } } return obstacleGrid[yLen - 1][xLen - 1] };
給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。若是你最多隻容許完成一筆交易(即買入和賣出一支股票),設計一個算法來計算你所能獲取的最大利潤。
輸入: [7,1,5,3,6,4]
輸出: 5
解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 5 天(股票價格 = 6)的時候賣出,最大利潤 = 6-1 = 5 。注意利潤不能是 7-1 = 6, 由於賣出價格須要大於買入價格。
最大利潤即(最高賣出價格 - 最小買入價格)。咱們只須要找到最小買入價格後, 計算每一天的利潤,取最大值便可
/** * @param {number[]} prices * @return {number} */ var maxProfit = function (prices) { if (prices.length === 0) return 0 // 利潤 let result = 0 let min = prices[0] // 找到最小的谷以後的最大的峯 for (let i = 0; i < prices.length; i++) { if (prices[i] < min) { min = prices[i] } result = Math.max(prices[i] - min, result) } return result };