JS算法題之leetcode(1~10)

JS算法題之leetcode(1~10)

前言

一直以來,前端開發的知識儲備在數據結構以及算法層面是有所暫缺的,可能歸根於咱們的前端開發的業務性質,可是我認爲任何的編程崗位都離不開數據結構以及算法。
所以,我做爲一名前端菜雞,打算作一個專欄,就是關於用JavaScript來解答算法題,會持續跟新,但願你們督促。同時,本人才疏學淺,文章內容可能有錯誤的地方,但願各位大神指出,謝謝。
no more bb, show me the code!前端

clipboard.png

題目均來自樂扣(leetcode)node

兩數之和

題目描述

給定一個整數數組 nums 和一個目標值 target,請你在該數組中找出和爲目標值的那 兩個 整數,並返回他們的數組下標。git

示例

給定 nums = [2, 7, 11, 15], target = 9github

由於 nums[0] + nums[1] = 2 + 7 = 9
因此返回 [0, 1]正則表達式

解答

這題不難,遍歷nums,用targer減去當前元素,獲得的元素若是在數組中,那就完事了。不過要注意統一元素不能用兩次算法

var twoSum = function(nums, target) {
    let idx1, idx2;
    nums.forEach((ele, index) => {
        let tempIdx = nums.indexOf(target - ele);
        if(tempIdx !== -1 && tempIdx !== index){
            idx1 = index;
            idx2 = tempIdx;
        }
    });
    return [idx1, idx2]
};

兩數相加

題目描述

給出兩個 非空 的鏈表用來表示兩個非負的整數。其中,它們各自的位數是按照 逆序 的方式存儲的,而且它們的每一個節點只能存儲 一位 數字。
若是,咱們將這兩個數相加起來,則會返回一個新的鏈表來表示它們的和。
您能夠假設除了數字 0 以外,這兩個數都不會以 0 開頭。編程

示例

輸入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
輸出:7 -> 0 -> 8
緣由:342 + 465 = 807數組

解答

這題不難,不過稍微有點複雜,涉及到了鏈表,同時考擦了js大數的運算狀況。
先遍歷兩個鏈表得到對應的數字,而後相加,最後反推算出結果對應的鏈表便可。數據結構

function ListNode(val) {
      this.val = val;
      this.next = null;
    return {
        val: this.val,
        next: null
    }
}

function addBigNumber(a, b) {
  var res = '',
    temp = 0;
  a = a.split('');
  b = b.split('');
  while (a.length || b.length || temp) {
    temp += ~~a.pop() + ~~b.pop();
    res = (temp % 10) + res;
    temp = temp > 9;
  }
  return res.replace(/^0+/, '');
}

var addTwoNumbers = function(l1, l2) {
    let num1 = '', num2 = '', cur;
    cur = l1;

    while(cur){
        num1 += cur.val.toString();
        cur = cur.next;
    }
    cur = l2;
    while(cur){
        num2 += cur.val.toString();
        cur = cur.next;
    }
    num1 = num1.split('').reverse().join('');
    num2 = num2.split('').reverse().join('');
    
    let total;
    if(num1.length > 21 || num2.length > 21){
        total = addBigNumber(num1, num2)
    }
    else{
        total = Number(num1) + Number(num2)
    }

    total = total.toLocaleString().toString().split('').reverse().join('').replace(/,/g, '')
    console.log(num1, num2, total)
    
    let l3 = ListNode(total[0]);
    cur = l3;
    for(let i = 1; i < total.length; i++){
        let node = ListNode(total[i]);
        cur.next = node;
        cur = node;
    }

    return l3;
};

無重複字符的最長子串

題目描述

給定一個字符串,請你找出其中不含有重複字符的 最長子串 的長度。函數

示例

輸入: "abcabcbb"
輸出: 3
解釋: 由於無重複字符的最長子串是 "abc",因此其長度爲 3。

輸入: "bbbbb"
輸出: 1
解釋: 由於無重複字符的最長子串是 "b",因此其長度爲 1。

輸入: "pwwkew"
輸出: 3
解釋: 由於無重複字符的最長子串是 "wke",因此其長度爲 3。

請注意,你的答案必須是 子串 的長度,"pwke" 是一個子序列,不是子串。

解答

維護一個數組用於存放無重複子串,遍歷輸入的字符串,若當前字符不在無重複數組中,則添加,不然,無重複數組清空,並push當前字符。
同時要維護另一個最長無重複子串的數組。

var lengthOfLongestSubstring = function(s) {
    let max = 0, maxArr = [], oldArr= [];
    s.split('').forEach((ele, index) => {
        if(maxArr.indexOf(ele) === -1){
            maxArr.push(ele)
            if(maxArr.length > max){
                max = maxArr.length;
            }
        }
        else{
            maxArr = [ele]
            let tempItem = oldArr.pop();
            while(tempItem != ele){
                maxArr.unshift(tempItem)
                tempItem = oldArr.pop();
            }
        }
        oldArr = [...maxArr]
    })
    return max;
};

尋找兩個有序數組的中位數

題目描述

給定兩個大小爲 m 和 n 的有序數組 nums1 和 nums2。

請你找出這兩個有序數組的中位數,而且要求算法的時間複雜度爲 O(log(m + n))。

你能夠假設 nums1 和 nums2 不會同時爲空。

示例

nums1 = [1, 3]
nums2 = [2]
則中位數是 2.0

nums1 = [1, 2]
nums2 = [3, 4]
則中位數是 (2 + 3)/2 = 2.5

解答

將兩個數組合並而後排序,以後獲取中位數便可。問題在於限定時間複雜度爲 O(log(m + n))的狀況下,如何排序呢?
咱們這裏直接使用sort()方法,該方法底層原理是將多個排序集於一體,根據數組的長度不一樣選擇不一樣的排序方法,加上V8引擎的優化,綜合來講時間複雜度是能知足的。
好像有點偷雞摸狗的感受。。。
sort方法的源碼:Array API源碼,從710行開始看吧

var findMedianSortedArrays = function(nums1, nums2) {
    let num = nums1.concat(nums2);
    num = num.sort((a, b) => a - b);
    let mid = Math.floor(num.length / 2);
    if (num.length % 2 === 0) {
        return (num[mid-1] + num[mid])/2
    } else {
        return num[mid]
    }
};

最長迴文子串

題目描述

給定一個字符串 s,找到 s 中最長的迴文子串。你能夠假設 s 的最大長度爲 1000。

示例

輸入: "babad"
輸出: "bab"
注意: "aba" 也是一個有效答案。

輸入: "cbbd"
輸出: "bb"

解答

這題要用動態規劃來作,先是判斷出全部長度爲1,2,3的子串是否迴文。
長度爲1,一定迴文。
長度爲2或者3,取決於首位字符是否相同。
長度大於3,取決於該子串去掉首位字符以後是否迴文,而且首位字符是否相同。
核心在於 dp[i][j] == dp[i+1][j-1] && s[i] === s[j]

var longestPalindrome = function(s) {
    let dp = [];
    for(let i = 0; i < s.length; i++){
        dp[i] = [];
    }

    let max = -1, str = '';
    for(let k = 0; k < s.length; k++){
        // k爲所遍歷的子串長度 - 1,即左下標到右下標的距離
        for(let i = 0; i + k < s.length; i++){
            let j = i + k;
            // i爲子串開始的左下標,j爲子串開始的右下標
            if(k == 0){
                // 當子串長度爲1時,一定是迴文
                dp[i][j] = true;
            }
            else if(k <= 2){
                // 當子串長度爲2時,兩字符相同則符合迴文,長度爲3,首位字符相同則符合迴文
                if(s[i] == s[j]){
                    dp[i][j] = true;
                }
                else{
                    dp[i][j] = false;
                }
            }
            else{
                // 當子串長度超過3,取決於去掉頭尾以後的子串是否迴文而且首位字符是否相同
                if(dp[i+1][j-1] && (s[i] == s[j])){
                    dp[i][j] = true;
                }
                else{
                    dp[i][j] = false;
                }
            }

            if(dp[i][j] && k > max){
                max = k;
                str = s.substring(i, j + 1)
            }
        }
    }

    return str;
};

Z 字形變換

題目描述

將一個給定字符串根據給定的行數,以從上往下、從左到右進行 Z 字形排列。
好比輸入字符串爲 "LEETCODEISHIRING" 行數爲 3 時,排列以下:

L   C   I   R
E T O E S I I G
E   D   H   N

以後,你的輸出須要從左往右逐行讀取,產生出一個新的字符串,好比:"LCIRETOESIIGEDHN"。
請你實現這個將字符串進行指定行數變換的函數:
string convert(string s, int numRows);

示例

輸入: s = "LEETCODEISHIRING", numRows = 3
輸出: "LCIRETOESIIGEDHN"

輸入: s = "LEETCODEISHIRING", numRows = 4
輸出: "LDREOEIIECIHNTSG"
解釋:

L     D     R
E   O E   I I
E C   I H   N
T     S     G

解答

這題目的結構有點怪,但也是有規律可循的,咱們發現這個」Z「的字符順序是這樣子的:垂直向下,斜向上,而後再垂直向下。
那其實咱們能夠直接將該結構簡化爲一個二維數組,去掉中間的空格,再一行一行地遍歷就能獲取到答案了。
如:

L     D     R
E   O E   I I
E C   I H   N
T     S     G

能夠變成

L D R
E O E I I
E C I H N
T S G

接着再一行一行讀,拼成字符串,即可。

var convert = function(s, numRows) {
    if(numRows == 1){
        return s;
    }

    let arr = [], direction = 'down', line = 0, str = '';
    for(let i = 0; i < numRows; i++){
        arr[i] = [];
    }

    for(let i = 0; i < s.length; i++){
        arr[line].push(s[i]);

        if(line == 0){
            line++;
            direction = 'down'
        }
        else if(line == numRows - 1){
            line--;
            direction = 'up'
        }
        else{
            if(direction == 'down'){
                line++;
            }
            else if(direction = 'up'){
                line--;
            }
        }
    }

    for(let i = 0; i < numRows; i++){
        str += arr[i].join("");
    }

    return str;
};

整數反轉

題目描述

給出一個 32 位的有符號整數,你須要將這個整數中每位上的數字進行反轉。
注意:
假設咱們的環境只能存儲得下 32 位的有符號整數,則其數值範圍爲 [−231,  231 − 1]。請根據這個假設,若是反轉後整數溢出那麼就返回 0。

示例

輸入: 123
輸出: 321

輸入: -123
輸出: -321

輸入: 120
輸出: 21

解答

這題就很簡單了,不過要考慮好邊緣溢出狀況便可。

var MAX = Math.pow(2, 31) - 1
var MIN = -1 * Math.pow(2, 31)

var reverse = function(x) {
    let str = x.toString().split(''), symbolFlag = false;
    if(str[0] == '-'){
        symbolFlag = true;
        str.shift();
    }

    str = str.reverse();

    if(symbolFlag){
        str.unshift('-');
    }
    let num = Number(str.join(''))
    if(num < MIN || num > MAX){
        return 0
    }
    else{
        return num
    }
};

字符串轉換整數 (atoi)

題目描述

請你來實現一個 atoi 函數,使其能將字符串轉換成整數。

首先,該函數會根據須要丟棄無用的開頭空格字符,直到尋找到第一個非空格的字符爲止。
當咱們尋找到的第一個非空字符爲正或者負號時,則將該符號與以後面儘量多的連續數字組合起來,做爲該整數的正負號;假如第一個非空字符是數字,則直接將其與以後連續的數字字符組合起來,造成整數。
該字符串除了有效的整數部分以後也可能會存在多餘的字符,這些字符能夠被忽略,它們對於函數不該該形成影響。

注意:假如該字符串中的第一個非空格字符不是一個有效整數字符、字符串爲空或字符串僅包含空白字符時,則你的函數不須要進行轉換。
在任何狀況下,若函數不能進行有效的轉換時,請返回 0。

說明:
假設咱們的環境只能存儲 32 位大小的有符號整數,那麼其數值範圍爲 [−231,  231 − 1]。若是數值超過這個範圍,qing返回  INT_MAX (231 − 1) 或 INT_MIN (−231) 。

示例

題目很長是吧,不要緊,咱們直接看示例。

輸入: "42"
輸出: 42

輸入: " -42"
輸出: -42
解釋: 第一個非空白字符爲 '-', 它是一個負號。
  咱們儘量將負號與後面全部連續出現的數字組合起來,最後獲得 -42 。

輸入: "4193 with words"
輸出: 4193
解釋: 轉換截止於數字 '3' ,由於它的下一個字符不爲數字。

輸入: "words and 987"
輸出: 0
解釋: 第一個非空字符是 'w', 但它不是數字或正、負號。
所以沒法執行有效的轉換。

輸入: "-91283472332"
輸出: -2147483648
解釋: 數字 "-91283472332" 超過 32 位有符號整數範圍。
所以返回 INT_MIN (−231) 。

解答

這題不難,可是有不少坑。首先咱們採起ASCII編碼的方式來判斷字符爲數字仍是英文仍是別的。
先去空白,去掉空白以後取第一個字符,判斷正負符號,如果英文直接返回0,若數字則不取。
從第二個字符開始遍歷,若不是數字則退出循環。
最後還要考慮溢出狀況。

const MIN = -1 * Math.pow(2, 31);
const MAX = Math.pow(2, 31) - 1;

var myAtoi = function(str) {
    str = str.trim();
    let result = '', symbol = '';
    let idx = 0;

    if(str.charCodeAt(0) === 45){
        idx++;
        symbol = '-';
    }
    else if(str.charCodeAt(0) === 43){
        idx++;
    }
    else if(str.charCodeAt(0) < 48 || str.charCodeAt(0) > 57){
        return 0;
    }

    for(let i = idx; i < str.length; i++){
        if(str.charCodeAt(i) === 46){
            break;
        }
        else if(str.charCodeAt(i) >= 48 && str.charCodeAt(i) <= 57){
            result += str[i];
        }
        else{
            break
        }
    }

    result = symbol.toString() + result.toString();

    if(Number(result) !== Number(result)){
        return 0;
    }
    else if(Number(result) < MIN){
        return MIN;
    }
    else if(Number(result) > MAX){
        return MAX;
    }
    else{
        return Number(result)
    }
};

迴文數

題目描述

判斷一個整數是不是迴文數。迴文數是指正序(從左向右)和倒序(從右向左)讀都是同樣的整數。

示例

輸入: 121
輸出: true

輸入: -121
輸出: false
解釋: 從左向右讀, 爲 -121 。 從右向左讀, 爲 121- 。所以它不是一個迴文數。

輸入: 10
輸出: false
解釋: 從右向左讀, 爲 01 。所以它不是一個迴文數

解答

這題比較簡單,反轉對比便可

var isPalindrome = function(x) {
    let y = x.toString().split("").reverse().join("");
    return x == y
};

正則表達式匹配

題目描述

給你一個字符串 s 和一個字符規律 p,請你來實現一個支持 '.' 和 '*' 的正則表達式匹配。

'.' 匹配任意單個字符
'*' 匹配零個或多個前面的那一個元素
所謂匹配,是要涵蓋 整個 字符串 s的,而不是部分字符串。

說明:
s 可能爲空,且只包含從 a-z 的小寫字母。
p 可能爲空,且只包含從 a-z 的小寫字母,以及字符 . 和 *。

示例

輸入:
s = "aa"
p = "a"
輸出: false
解釋: "a" 沒法匹配 "aa" 整個字符串。

輸入:
s = "aa"
p = "a*"
輸出: true
解釋: 由於 '*' 表明能夠匹配零個或多個前面的那一個元素, 在這裏前面的元素就是 'a'。所以,字符串 "aa" 可被視爲 'a' 重複了一次。

輸入:
s = "ab"
p = ".*"
輸出: true
解釋: "." 表示可匹配零個或多個('')任意字符('.')。

輸入:
s = "aab"
p = "cab"
輸出: true
解釋: 由於 '*' 表示零個或多個,這裏 'c' 爲 0 個, 'a' 被重複一次。所以能夠匹配字符串 "aab"。

輸入:
s = "mississippi"
p = "misisp*."
輸出: false

解答

這題稍微有點複雜,咱們採用了遞歸方法將兩個字符串對比,每次只對比一個字符。
將當前遞歸p的下一個字符是否爲*進行分類比較:
①p的下一個字符是*
若s和p的當前字符相同或者p的當前字符爲.,則結果就取決於:

isMatch(s.slice(1), p) || isMatch(s.slice(1), p.slice(2)) || isMatch(s, p.slice(2))

若p的最後兩個字符爲.*就返回true
若不符合上面兩種狀況就將取決於

isMatch(s,p.slice(2))

②p的下一個字符不爲*
這種狀況就簡單了
若s和p的當前字符相同或者p的當前字符爲.,返回true
不然返回false

var isMatch = function(s, p) {
    if(s.length === 0 && p.length === 0){
        return true;
    }
    if(s.length !== 0 && p.length === 0){
        return false;
    }

    let str = s[0], pattern = p[0];
    let isNextStart = p[1] === "*";

    if(isNextStart){
        if(str && (str === pattern || pattern === ".")){
            return isMatch(s.slice(1), p) || isMatch(s.slice(1), p.slice(2)) || isMatch(s, p.slice(2))
        }
        else if(pattern === "." && p.slice(2).length === 0){
            return true
        }
        else{
            return isMatch(s,p.slice(2));
        }
    }
    else{
        if(str && (str === pattern || pattern === ".")){
            return isMatch(s.slice(1), p.slice(1))
        }
        else{
            return false;
        }
    }
};

總結

本文全部題目均來自樂扣(leetcode),作法不惟一,甚至可能還有所錯誤,但願各位大神指出,弟弟虛心學習。

相關文章
相關標籤/搜索