LeetCode热题100(Hot100) 每日题目记录)_JavaScript实现_2022-4-24 周末总结

2022-4-24周末总结

方法:模拟

LeetCode

思路

出现1的时候就记录1的索引,然后和之前出现1的索引位置进行比较,遍历完的最大值就是要找的值。

代码实现
/**
 * @param {number} n
 * @return {number}
 */
const binaryGap = function(n) {
    let temp = n.toString(2)
    let res = 0
    for (let i = 0,preIndex=0; i < temp.length; i++) {
        if (temp[i]==='1'){
            res = Math.max(res,i-preIndex)
            preIndex = i
        }
    }
    return res
};
console.log(binaryGap(22))

2022-4-18

题目:字典排序数解题思路

LeetCode链接

方法:dfs深度优先搜索

字典序样例

比如n=123 那么输出结果为

1,10,100,101109,11,110119,12,120123,13,1419,2,20,21.

整体思路

对于一个整数num,字典排序树的情况对应下边几种情况

  • 先将num*10(在num后边附加一个0),并判断num是否超过了数字n,如果没有则说明符合字典排序树的要求
  • 假如num mod 10===9 或者num+1>n 则说明末尾的数字已经搜索完了,则返回上一位,也就是对num/10向下取整,然后num++ 一直判断直到循环结束。

如果一个数需要取中间的值可以 num >> 1右移一位 向下取整

代码实现
const lexicalOrder = (n) => {
    let res = []
    // 最小的字典数,先放入数组
    let num = 1
    // 控制放入n个数字
    for (var i = 0; i < n; i++) {
        res.push(num)
        // num后边加0还符合要求就直接放入
        if (num * 10 <= n) {
            num *= 10
        } else {
            // 不符合要求就先判断是否,到了对后一个字典数
            // 如果没到就num++ 然后push进答案数组中,假如到了最后,就回退到上一个数字. 比如n=120 此时num=109 则需要让num变成11、或者n=123此时num=123 则需要让num=13
            while (num % 10 === 9 || num + 1 > n) {
                num = Math.floor(num/10)    
            }
            num++
        }
    }
    return res
}

2022-4-19

题目:两数相加

LeetCode链接

方法:直接相加
整体思路

遍历两个链表,直接在对应的数值相加,假如某个链表为空,可以用0代替它的数值相加。根据题意
可以知道相加的规则为:

  1. 相加没有进位 ,这种情况可以直接将值放入新链表当中
  2. 当相加有进位的情况,sum的值mod10 作为链表的新数值加入到链表。并置进位变量carry = Math.floor(sum/10),继续迭代

需要注意当链表结束遍历的时候进位的数值为1则需要在链表的结尾加入值为1的节点

代码实现
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
const addTwoNumbers = function(l1, l2) {
    /**
     * 当时以为js不能新建链表然后再新链表上操作,因为报出了内存溢出的错误,后来发现是后边的判断条件出现了错误
     * @type {ListNode}
     */
        // 创建新链表用于存放结果
    let newNode = new ListNode()
    let temp = newNode
    let carry = 0
    while (l1!==null||l2!==null) {
        let x = l1 === null ? 0:l1.val
        let y = l2 === null ? 0:l2.val
        let sum = x + y + carry
        /**
         * 注意点: sum/10 再js中不会自动向下取整 所以需要Math函数
         * @type {number}
         */
        carry = Math.floor(sum/10)
        sum = sum%10
        temp.next = new ListNode(sum)
        temp = temp.next
        /**
         * 这个判断条件为链表不为空
         */
        if (l1) {
            l1 = l1.next
        }
        if (l2) {
            l2 = l2.next
        }
        if (carry===1) {
            temp.next = new ListNode(carry)
        }
    }
    return newNode.next
};

2022-4-20

题目:文件最长绝对路径

LeetCode链接

方法:栈
整体思路

大致方法:用栈来维护结果字符串的长度,按照层级遍历。

  1. 首先将字符串通过\n进行分割,返回分割后的数组再处理
  2. 遍历数组,数组中的某个元素代表文件目录的某个层级,下边需要处理的是每个层级的绝对路径长度
  3. 当文件在某个层级的时候,其现在的最长文件绝对路径包括现在层级字符串的长度加上前面所有层级字符串的长度,而字符串的长度需要用栈来维护
  4. 较难处理的是层级的变换,当dir的一个子目录遍历完之后,比如此时在subsubdir1,现在的层级为3,但是subdir1已经遍历结束,下一个文件为subdir2就需要回退到第一级目录,需要改变栈维护的字符串长度。

String.prototype.split方法

// 传入一个要分割的字符返回分割后的数组
String.prototype.split()
代码实现
/**
 * @param {string} input
 * @return {number}
 */
const lengthLongestPath = function (input) {
        let res = 0
        let stack = []
        stack.push(0)
        let newString = input.split('\n')
        for (let i = 0; i < newString.length; i++) {
            // 确定当前文件的层级,当没有/t的时候返回-1,说明文件在0级。
            // 需要注意的是lastIndexOf
            let level = newString[i].lastIndexOf('\t') + 1
            /**
             * 较难理解的部分
             */
            while (stack.length - level > 1) {
                stack.pop()
            }
            // 	\t\tsubsubdir1 这个字符串的长度为出去\之后的长度,计算length的时候需要减去层数补上1  也就是说长度为subsubdir1/ 这个/是算在字符串长度中的
            // 后边的加1表示/这个符号
            let length = stack[stack.length - 1] + (newString[i].length - level + 1)
            console.log(newString[i])
            stack.push(length)
            if (newString[i].includes('.')) {
                // 这个-1是为了把/这个符号的长度给去掉,因为是最后一个文件所以没/
                res = Math.max(res, length - 1)
            }
        }
        return res
    };
题目:无重复的最长回文子串长度

LeetCode链接

方法1:滑动窗口+队列
方法2:队列
整体思路

解法1:用数组模拟队列,并遍历字符串s,假如当前遍历的字符串在前边没有出现过则就queue.push()进栈,假如出现过就需要删除队列前边的元素, 不断迭代,计算最大值。

解法2:采用滑动窗口的方式

  1. 创建窗口,这个题目可以用Set集合
  2. 当所求的字符串满足题目要求的时候左指针不动,右指针后移,并满足要求的字符串add进set集合, 通知右指针后移。
  3. 当继续遍历遇到与前边重复的字符串,则需要删除前边元素,继续循环。
代码实现
/**
 * @param {string} s
 * @return {number}
 * 解法1:采用队列的方法动态删除
 */
const lengthOfLongestSubstring = function (s) {
        let queue = []
        let res = 0
        for (const sElement of s) {
            // 或者写成if判断语句
            /*
            if(queue.includes(sElement)) {
                第二个参数为删除元素的个数,为索引值+1
                queue.splice(0,queue.indexOf(sElement)+1)
            }
            */
            while (queue.includes(sElement)) {
                queue.shift()
            }
            queue.push(sElement)
            res = Math.max(queue.length, res)
        }
        return res
    };
/**
 * 解法2:滑动窗口
 * @param s
 * @return {number}
 */
const lengthOfLongestSubstring = function (s) {
    let set = new Set()
    let res = 0
    let right = -1
    for (let i = 0; i < s.length; i++) {
        if (i !== 0) {
            set.delete(s.charAt(i - 1))
        }
        // right初值为-1所以判断条件需要注意
        while (right < s.length - 1 && !set.has(s.charAt(right + 1))) {
            set.add(s.charAt(right + 1))
            right++
        }
        res = Math.max(res, right - i + 1)
    }
    return res
};
/**
 * 解法3:采用map集合滑动窗口
 * @param s
 * @return {number}
 */
const lengthOfLongestSubstring = (s) => {
    let left = 0;
    let long = 0;
    const map = new Map(), len = s.length;
    for (let right = 0; right < len; right++) {
        // 遇到重复字符时还要判定 该重复字符的上一次出现位置是否在 滑动窗口左边界 left 的右边
        if (map.has(s[right]) && map.get(s[right]) >= left) {
            left = map.get(s[right]) + 1; // 都满足,则更新,更新到最近出现的那个重复字符,它的上一个索引的右边
        }
        long = Math.max(long, right - left + 1); // 比较滑动窗口大小与 long 的长度
        map.set(s[right], right); // 无论有没有重复,每次遍历都要更新字符的出现位置
    }
    return long;
}
lengthOfLongestSubstring("bbbbb")

2022-4-21

题目: 山羊拉丁文

LeetCode链接
方法1:直接模拟

整体思路

依照题目给的要求进行模拟

代码实现
/**
 * 自己写的一坨屎山代码,主要还是得多学习内置api
 * @param {string} sentence
 * @return {string}
 */
const toGoatLatin = function (sentence) {
    // sentence = sentence.toLowerCase()
    let o = ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']
    let res = []
    let arr = sentence.split(' ')
    for (let i = 0; i < arr.length; i++) {
        if (o.includes(arr[i][0])) {
            let str = arr[i].concat('ma')
            for (let j = 0; j < i + 1; j++) {
                str = str.concat('a')
            }
            res.push(str)
        } else {
            let str = ''
            for (let j = 0; j < arr[i].length; j++) {
                if (j === arr[i].length - 1) {
                    str += arr[i][0]
                } else {
                    str += arr[i][j + 1]
                }
            }
            str = str.concat('ma')
            for (let j = 0; j < i + 1; j++) {
                str = str.concat('a')
            }
            res.push(str)

        }
    }
    res = res.join(' ')
    return  res
};
/**
 * 看的别人的题解
 * @param s
 * @return {number}
 */
const toGoatLatin = function (sentence) {
    const char = ['a','e','i','o','u']
    let res = []
    let arr = sentence.split(' ')
    for (var i = 0; i < arr.length; i++) {
        if (char.includes(arr[i][0].toLowerCase())){
            res[i] = arr[i] + 'ma' + 'a'.repeat(i+1)
        }else {
            res[i] = arr[i].substring(1) + arr[i].charAt(0) + 'ma' +'a'.repeat(i+1)
        }
    }
    return res.join(' ')
};
题目:搜索二叉树的公共祖先
求解思路
  • 二叉搜索树的性质可以看到:
  1. 如果节点p,q都在root节点的右边,就向右边寻找。
  2. 如果节点p,q都在root节点的左边,就向着左边的 寻找
  3. 如果在root节点的左右两边就可以返回root节点

LeetCode链接

/**
 * 解法1:迭代方式实现
 * @param {TreeNode} root
 * @param {TreeNode} p
 * @param {TreeNode} q
 * @return {TreeNode}
 */
const lowestCommonAncestor = function (root, p, q) {
        while (root) {
            if (p.val < root.val && q.val < root.val) {
                root = root.left
            } else if (q.val > root.val && p.val > root.val) {
                root = root.right
            } else {
                break
            }
        }
        return root
    };
/**
 * 解法2:递归实现
 * @param root
 * @param p
 * @param q
 * @return {*}
 */
const lowestCommonAncestor = function (root, p, q) {
    if (root.val>p.val&&root.val>q.val) {
        return lowestCommonAncestor(root.left,p,q)
    } else if (root.val<p.val&&root.val<q.val){
        return lowestCommonAncestor(root.right,p,q)
    }
    return root
};
题目:二叉树的最近公共祖先

LeetCode链接

整体思路

二叉树的后序遍历,用到了递归。只是那个晚上和吴浪讨论了一下,把代码复现了一遍,还需要继续自己理解以下,再看看
其他的一些题解。

代码实现
/**
 * @param {TreeNode} root
 * @param {TreeNode} p
 * @param {TreeNode} q
 * @return {TreeNode}
 */
var lowestCommonAncestor = function(root, p, q) {
    if(root===p||root===q||root===null) return root
    let left = lowestCommonAncestor(root.left,p,q)
    let right = lowestCommonAncestor(root.right,p,q)
    
    if(left!==null&&right!==null) {
        return root
    }
    if(left!==null&&right===null) {
        return left
    }else if(left===null&&right!==null) {
        return right
    }else {
        return null
    }
};

2022-4-22

题目:旋转函数

LeetCode链接
####方法:寻找规律进行迭代

整体思路

数组nums的和为sumNums,可以模拟一下,F(n-1)与F(n) 之间的关系,发现两者之间相差的数值,这个需要用笔算一下。
F [ 0 ]    =    0 × n u m s [ 0 ]    +    1 × n u m s [ 1 ]    +    . . . + ( n − 1 )    ×    n u m s [ n − 1 ] F [ 1 ]    =    1 × n u m s [ 0 ]    +    2 × n u m s [ 1 ]    +    . . . + ( 0 )    ×    n u m s [ n − 1 ]    =    F [ 0 ]    +    n u m S u m    −    n × n u m s [ n − 1 ] F\left[ 0 \right] \,\,=\,\,0\times nums\left[ 0 \right] \,\,+\,\,1\times nums\left[ 1 \right] \,\,+\,\,... +\left( n-1 \right) \,\,\times \,\,nums\left[ n-1 \right] \\ F\left[ 1 \right] \,\,=\,\,1\times nums\left[ 0 \right] \,\,+\,\,2\times nums\left[ 1 \right] \,\,+\,\,... +\left( 0 \right) \,\,\times \,\,nums\left[ n-1 \right] \,\, \\ =\,\,F\left[ 0 \right] \,\,+\,\,numSum\,\,-\,\,n\times nums\left[ n-1 \right] F[0]=0×nums[0]+1×nums[1]+...+(n1)×nums[n1]F[1]=1×nums[0]+2×nums[1]+...+(0)×nums[n1]=F[0]+numSumn×nums[n1]

代码实现
/**
 * 暴力解法失败了!
 * @param {number[]} nums
 * @return {number}
 */
const maxRotateFunction = function(nums) {
    let maxVal = -Infinity
    for (let i = 0; i < nums.length-1; i++) {
        let sum = 0
        nums.unshift(nums.pop())
        for (let j = 0; j < nums.length; j++) {
            sum+= arr[j]*j
        }
        maxVal = Math.max(maxVal,sum)
    }
    return maxVal
};


/**
 * 寻找迭代关系
 */
const maxRotateFunction = (nums) => {
    let f = 0
    // 求原始nums数值的数值和
    let numSum = nums.reduce((pre,cur) => {
        return pre+cur
    },0)
    // 计算F(0)从而可以迭代出F1的数值
    for (let i = 0; i < nums.length; i++) {
        f+=nums[i]*i
    }
    let maxVal = f
    for (let i = nums.length-1; i >0; i--) {
        // 第一次f为f(0) k从最开始
        // f(1) = f(0) + numSum - nums.length * nums[nums.lenth-k]
        f += numSum - nums.length * nums[i]
        maxVal = Math.max(f,maxVal)
    }
    return maxVal
}
题目:寻找两个正序数组的中位数

LeetCode链接
####方法:直接模拟奇偶数的情况

整体思路

直接暴力模拟简单有效,需要注意的是数组合并后的排序必须指定回调函数,不然数组中存在负数的时候会导致排序出现错误。

js合并数组的方法
  • contact方法 用于合并一个或者多个数组,不会改变原数组的数值,不指定参数的时候会返回一个原来数组的浅拷贝,可以用来复制数组
const array1 = ['a', 'b', 'c'];
const array2 = ['d', 'e', 'f'];
const array3 = array1.concat(array2,[1,2,3]);
// 浅拷贝数组
const array4 = arr1.concat()
console.log(array3);
console.log(array4);
  • arr.push(…arr1) 显而易见,push进另外一个数组
  • […arr,…arr1]
代码实现
if (!nums2.length&&!nums1.length) return 0
    let mergeArr = [...nums1,...nums2]
    // 指定回调函数,保证排序的准确性
    mergeArr.sort((a,b) => {
        return a-b
    })
    if (mergeArr.length&2!==0) {
        return mergeArr[Math.floor(mergeArr.length/2)]
    }else {
        let num1  = mergeArr[Math.floor(mergeArr.length/2)]
        let num2 = mergeArr[Math.floor(mergeArr.length/2)-1]
        return (num1+num2)/2
    }
题目:最长回文子串

LeetCode链接

整体思路

双指针左右搜索遍历,遇到相同的就指针++,代码中写出思路

/**
 * @param {string} s
 * @return {string}
 */
var longestPalindrome = function(s) {
    // 存放最长子串的长度
    let max = 0
    // 用于更新最长子串的变量
    // let str = ''
    let masStr = ''
    for(let i =0;i<s.length;i++) {
        //从当前字符开始计算最长子串的长度
        let str = s[i]
        // 定义个左指针,右指针不定义呢,得先把和当前字符相等的所有数值都找出来,并获得最后一个相等值的索引
        let left = i - 1
        while(s[i+1]===s[i]){
            str += s[i]
            i++
        }
        // 说明和s[i]相等的值找完了,定义右指针
        let right = i + 1
        // 向两侧扩展
        while(s[left]!==undefined&&s[left] ===s[right]) {
            str = s[left] + str + s[right]
            left--
            right++
        }
        if(str.length>max) {
            max = str.length
            maxStr = str
        }
    }
    return maxStr
};

2022-4-23

题目:正则表达式匹配

LeetCode链接

方法:使用正则表达式
整体思路

题目的底层可以用动态规划来实现,可以将p转为正则表达式,用p匹配s,假如可以匹配,就需要看匹配的结果是否等于,s假如p能 匹配出s则返回true,否则返回false。

代码实现
/**
 * 这题目本来是要用动态规划,实现一个正则表达式的匹配,有点难,学动态规划还是得从简单的学起来,所以这个题目就这么糊弄了
 * @param {string} s
 * @param {string} p
 * @return {boolean}
 */
const isMatch = function (s, p) {
        // ${p}是为了操作变量
        /**
         *  let a = 'u'
         *  打算匹配变量a中的字符u要这么写的话匹配的是a无法访问变量/a/g
         *  写成/${p}/
         */
            // eval将字符串转为js表达式
        let reg = eval(`/${p}/g`) // 转换成正则表达式
        let result = '' //存储匹配到的结果
        if (reg.test(s)) {
            result = reg[Symbol.match](s)[0]
            // 边界条件
            if (s.length >= 1 && s.length <= 20 && p.length >= 1 && p.length <= 30) {
                // 可以完全匹配字符串s返回
                if (s == result) {
                    return true
                } else {
                    // 匹配的结果不一样返回false
                    return false
                }
            }
        } else {
            // 根本就没匹配到s直接返回就可以
            return false
        }
    };
题目:Z字型变换

LeetCode链接

方法:使用数组模拟
整体思路

定义一个数组,存放每一行的字符串,遍历原来字符串的时候答案就拼接进去。

  • 第一点定义一个Boolean变量判断此时是向下走还是向上走,定义一个row用于当作数组的索引row的范围为[0,String.length-1]
  • 第二点新Z字型的字符串行数等于题目要求的行数
  • 当isDown为true的时候说明再向下走,row就需要+1,当isDown变为false时候,需要row–
代码实现
/**
 * @param {string} s
 * @param {number} numRows
 * @return {string}
 */
const convert = function (s, numRows) {
        if (numRows === 1) return s
        // 定义初始数组
        let arr = new Array(numRows).fill('')
        for (let i = 0, down = true, row = 0; i < s.length; i++) {
            // 拼接字符串
            arr[row] += s[i]
            if (down) {
                // 在向下走,行数++
                row++
            } else {
                // 在向上走,行数--
                row--
            }
            // 边界条件
            if (row === numRows - 1) {
                down = false
            } else if (row === 0) {
                down = true
            }
        }
        // 数组拼接成字符串返回
        return arr.join('')
    };
题目:回文数

LeetCode链接

方法:直接写,小学题目
整体思路

看代码

代码实现
/**
 * @param {number} x
 * @return {boolean}
 */
const isPalindrome = function(x) {
    let s = x +''
    // 这句代码跟题目没有关系,只是测一下Array.from()方法,里边可以放迭代器对象,然后就会生成数组。
    // 知识点+1
    let arr = Array.from(s)
    let left=0,right = s.length-1;
    for (let i = 0; i <s.length/2; i++) {
        // 这里也可以写成这样,不过还是定义双指针思路清晰一些。
        if (s[left]!==s[s.length-1-left]) {
            return false
        }
        // if (s[left]!==s[right]) {
        //     return false
        // }
        left++
        // right--
    }
    return true
};

你可能感兴趣的:(力扣热题100,javascript,leetcode)