前端算法必刷题系列[73]

612 阅读4分钟

这是我参与更文挑战的第 26 天,活动详情查看 更文挑战

这个系列没啥花头,就是纯 leetcode 题目拆解分析,不求用骚气的一行或者小众取巧解法,而是用清晰的代码和足够简单的思路帮你理清题意。让你在面试中再也不怕算法笔试。

137. Z 字形变换 (zigzag-conversion)

标签

  • 字符串
  • 简单

题目

leetcode 传送门

这里不贴题了,leetcode打开就行,题目大意:

将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:

P   A   H   N
A P L S I I G
Y   I   R

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"。

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);

示例 1

输入:s = "PAYPALISHIRING", numRows = 3
输出:"PAHNAPLSIIGYIR"

示例 2

输入:s = "PAYPALISHIRING", numRows = 4
输出:"PINALSIGYAHRPI"
解释:
P     I    N
A   L S  I G
Y A   H R
P     I

示例 3

输入:s = "A", numRows = 1
输出:"A"

基本思路

遇到这种问题,就像我们小学做数学找规律题差不多,关键是,找到周期性变化规律

我们有的条件是

  1. 我们有行数参数,规定了一共几行
  2. 我们知道它是 Z 字形走法
  3. 我们要把所有行的元素给拉出来,也就是要求每个元素应该属于哪行, 这个元素跟周期有什么关系。

我们先找到周期,例如

输入:s = "PAYPALISHIRING", numRows = 4
输出:"PINALSIGYAHRPI"
解释:
P     I    N
A   L S  I G
Y A   H R
P     I

我们该怎么算周期? 想想正玄曲线,一个图像,它最小周期怎么看

(1) 就左边一竖,这都不好拆,都没形成完整周期
P        I    N
A      L S  I G
Y    A   H R
P        I    

(2) 这个就看着舒服,没错这才是周期
P        I       N
A   L    S   I   G
Y A      H R
P        I

(3) 多算一个,右边的就少了,同样不能当做周期
P     I       N
A   L    S  I G
Y A      H R
P        I

那周期数跟 numRows 的关系呢,上面有 4 行 numRows = 4,周期我们看是 PAYPAL 6 个,其实应该是 最下面一个重复算两次,要减一,最右边没有够到第一行还要减 1,其实 T = numRows * 2 - 2

下面就是周期性原理,你字符串任意位置都能算出它在周期中的定位,pos = i % T

有了定位,能算出每个元素在哪一行,其实规律在图中也很清楚

  • 在左边向下一竖上, pos 就是行的对应数字
// 左边数字是行数
0 P        I       N
1 A   L    @   I   G
2 Y A      H &
3 P        I

你比如在第二周期里的 @ 它在周期中 pos = 7 % 6 = 1,其实他就是在行数上,没啥好说的

  • 关键是 在右边向上一提上,对应行数就是 T - pos

你比如在第二周期里的 & 它在周期中 pos = 10 % 6 = 4,对应行数就是 6 - 4 = 2 行,在第二行上。

解释够多了,看代码合注释吧

写法实现

var convert = function(s, numRows) {
    if (numRows === 1) {
        return s
    }
    // 存放每一行元素,一共有 numRows 行
    let rowsArr = new Array(numRows).fill('')
    // 这是循环周期
    let T = numRows * 2 - 2;
    // 当前元素在哪一行上
    let curItemRow = 0
    // 遍历把 rowsArr 每一行填上,最后连接每一行就行
    for (let i = 0; i < s.length; i++) {
        // 当前 i 在循环中位置 pos
        let pos = i % T;

        // 在左边向下一竖上, pos 就是行的对应数字
        if (pos < numRows) {
            curItemRow = pos
        } else {
            // 在右边向上一提上, 对应行数就是 T - pos 
            curItemRow = T - pos
        }
        // 当前元素拼接到 rowsArr 的对应行数字符串
        rowsArr[curItemRow] += s[i]
    }
    return rowsArr.join('')
};

let s = "PAYPALISHIRING", numRows = 4
console.log(convert(s, numRows))
// 输出:"PINALSIGYAHRPI"

138. 两个数组的交集 II (intersection-of-two-arrays-ii)

标签

  • 数组
  • 简单

题目

leetcode 传送门

这里不贴题了,leetcode打开就行,题目大意:

给定两个数组,编写一个函数来计算它们的交集。

示例 1:

输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]

示例 2:

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[4,9]

输出结果中每个元素出现的次数,应与元素在两个数组中出现次数的最小值一致。 我们可以不考虑输出结果的顺序。

基本思路

map 法我在 leetcode 官方解法找了张图,看图 + 代码 理解就好。

OIP.qZdjzVs5AV8au-_LVO0n-wHaEo.gif

还有排序法,先排序,再双指针遍历,也很简单看代码就行。

写法实现

hashMap

var intersect = function(nums1, nums2) {
    let [res, map] = [[], new Map()]
    for (item of nums1) {
        if (map.has(item)) {
            let count = map.get(item)
            // 如果已经有了,就对应数量 +1
            map.set(item, count + 1)
        } else {
            map.set(item, 1)
        }
    }
    for (it of nums2) {
        let count = map.get(it)
        if (count > 0) {
            // 说明有交集,取出一个,count - 1
            res.push(it)
            map.set(it, count - 1)
        }
    }
    return res
};

let nums1 = [4,9,5], nums2 = [9,4,9,8,4]
console.log(intersect(nums1, nums2))

排序法

var intersect = function(nums1, nums2) {
    // 先排序
    nums1.sort((a, b) => a - b)
    nums2.sort((a, b) => a - b)
    // 在两个指针分别后移找交集
    let [res, left, right] = [[], 0, 0]
    while (left < nums1.length && right < nums2.length) {
        if (nums1[left] === nums2[right]) {
            res.push(nums1[left])
            left += 1
            right += 1
        } else if (nums1[left] < nums2[right]) {
            left += 1
        } else {
            right += 1
        }
    }
    return res
};

let nums1 = [1,2,2,1], nums2 = [2,2]
console.log(intersect(nums1, nums2))

另外向大家着重推荐下这个系列的文章,非常深入浅出,对前端进阶的同学非常有作用,墙裂推荐!!!核心概念和算法拆解系列

今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 点击此处交个朋友 Or 搜索我的微信号infinity_9368,可以聊天说地 加我暗号 "天王盖地虎" 下一句的英文,验证消息请发给我 presious tower shock the rever monster,我看到就通过,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧

参考