资讯专栏INFORMATION COLUMN

JS算法题之leetcode(1~10)

SoapEye / 2478人阅读

摘要:先去空白,去掉空白之后取第一个字符,判断正负符号,若是英文直接返回,若数字则不取。回文数题目描述判断一个整数是否是回文数。回文数是指正序从左向右和倒序从右向左读都是一样的整数。

JS算法题之leetcode(1~10) 前言

一直以来,前端开发的知识储备在数据结构以及算法层面是有所暂缺的,可能归根于我们的前端开发的业务性质,但是我认为任何的编程岗位都离不开数据结构以及算法。
因此,我作为一名前端菜鸡,打算做一个专栏,就是关于用JavaScript来解答算法题,会持续跟新,希望大家督促。同时,本人才疏学浅,文章内容可能有错误的地方,希望各位大神指出,谢谢。
no more bb, show me the code!

题目均来自乐扣(leetcode)

两数之和 题目描述

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

示例

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

因为 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),做法不唯一,甚至可能还有所错误,希望各位大神指出,弟弟虚心学习。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/106682.html

相关文章

  • JS算法题之leetcode(11~20)

    摘要:给定一个整数,将其转为罗马数字。字符数值例如,罗马数字写做,即为两个并列的。通常情况下,罗马数字中小的数字在大的数字的右边。给定一个罗马数字,将其转换成整数。注意空字符串可被认为是有效字符串。 JS算法题之leetcode(11~20) showImg(https://segmentfault.com/img/bVbwmfg?w=1790&h=714);这次的十道题目都比较容易,我们简...

    CoderDock 评论0 收藏0
  • JavaScript算法题之–随机数的生成

    摘要:准备面试,多看点题。来自雨夜带刀需求描述从一组有序的数据中生成一组随机并且不重复的数,类似于简单的抽奖程序的实现。 (准备面试,多看点题。来自雨夜带刀s Blog) 需求描述:从一组有序的数据中生成一组随机并且不重复的数,类似于简单的抽奖程序的实现。 先来生成一个有序的数组: var arr = [], length = 100, i = 0; for( ; i < length;...

    tigerZH 评论0 收藏0
  • python面试题之“该死的for循环系列”(一)

    摘要:这是一道魔性面试题,难倒了无数英雄好汉上面代码的执行顺序是这样的从上到下第一个函数就是实现了一个简单的加法运算第二个函数是一个生成器函数,如果调用它会返回一个生成器这一行调用了生成器函数,所以此刻就是一个生成器它的本质还是迭代器然后执行循环 这是一道魔性面试题,难倒了无数英雄好汉…… def add(n,i): return n+i def test(): for i...

    wudengzan 评论0 收藏0
  • JavaScript算法题之–查找不同顺序排列的字符串

    摘要:来自雨夜带刀需求描述从一组数组中找出一组按不同顺序排列的字符串的数组元素。最后用编码和作为对象的来保存编码和一致的字符串。方法方法是将字符串转换成数组后再对数组进行排序,和使用排序后会变成,将拍好序的字符串作为对象的来保存排序一致的字符串。 (准备面试,多看点题。来自雨夜带刀s Blog ) 需求描述:从一组数组中找出一组按不同顺序排列的字符串的数组元素。假如有这样一个数组: [ ...

    zhjx922 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<