Giter Club home page Giter Club logo

brush_algorithm's Introduction

about OBKoro1

Hi,大家好,我是OBKoro1,大家可以叫koro(扣肉)。

关于我的其他信息有兴趣的朋友可以去前端进阶积累-关于了解。

下面是我在github上的几个开源项目的演示gif,自认为还是比较好用的工具。

开源项目演示

  1. VSCode插件: 用于一键生成文件头部注释并自动更新最后编辑人和编辑时间、函数注释自动生成和参数提取。
  2. 插件可以帮助用户养成良好的编码习惯,规范整个团队风格。
  3. 经过多版迭代后,插件支持所有主流语言,灵活方便,文档齐全,食用简单!
  4. 从2018年5月维护至今, 3K+ Star,关闭issue 300+
  5. 目前拥有250K+的用户,VSCode图表统计日安装用户100多-400多人,

头部注释

函数注释

减少摸鱼的时间和频率的Chrome插件:在上班/学习期间很容易下意识的打开摸鱼网站,插件帮助我们减少摸鱼的时间和频率,提高我们上班和学习的效率,节省时间用于学习提升自己或者享受生活

  • 这是一个可以在任意时间范围自动提交commit的VSCode插件
  • 它可以自由控制每天的commit次数, 随机commit次数,使你的commit提交看起来更加逼真。
  • 它在平常不用运行,需要的时候花十几分钟一键刷commit,填满你的github首页绿色格子

收集和整理了一个大厂前端需要掌握能力的仓库。

其中分为JS基础能力,大厂场景题、大厂面试真题。

希望能够帮助大家提升自己的能力,在面试的时候能够游刃有余,轻松拿到高薪offer。

大厂前端需要掌握的能力

用爱发电,求赞助 😭

开源不易,本插件的开发与维护全都是利用业余时间。

开源工作对我来说就是用爱发电,从18年开始在社区开源到现在,可以说基本没有收益。

如果觉得这个效率工具还不错, 对你有所帮助,就赞助支持一下我的工作吧。

赞助

联系方式:

brush_algorithm's People

Contributors

obkoro1 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

brush_algorithm's Issues

删除一个字母的回文 | 简单级-算法

博客链接

# 删除一个字母的回文

# 描述

给定非空字符串 s,您最多可以删除一个字符。判断是否可以成为回文。

该字符串仅包含小写字符 a-z,字符串的最大长度为 50000。

# 样例:

Given s = "aba" return true

Given s = "abca" return true // delete c

# 题目分析:

  • 如果单单是回文的话,就很简单了:
s === [...s].reverse().join(''); // 翻转字符串与原字符相比
// 实际上这里做了很多步操作,字符转数组 翻转数组 再转字符串,所以这里性能也不是很好
// 如果对性能要求比较高的话,还是通过循环从两侧向中间逐一比较,会更好一点
  • 题目中还有一个要求:删除一个字符,也就是允许一个字符的不同。
  • 下面我们的解法主体思路就是通过循环,从两侧向中间比较,然后解决当出现不同的情况,如何保证只允许出现一个不同

# code:

  1. 出现一处不同 将值传入一个新函数,再进行判断字符串:
const validPalindrome = s => {
  let left = 0;
  let right = s.length - 1;
  while (left < right) {
    if (s[left] !== s[right]) {
      // 左右两边字符都要尝试一下 一边返回true即可
      return (
        isSubPalindrom(s, left + 1, right) || isSubPalindrom(s, left, right - 1)
      );
    }
    left++;
    right--;
  }
  return true; // 循环结束返回true
};
const isSubPalindrom = (s, left, right) => {
  while (left < right) {
    if (s[left] !== s[right]) {
      return false; // 再有不同之处 返回false
    }
    left++;
    right--;
  }
  return true; // 循环结束一直相等返回true
  // 并且left不小于right 直接返回right,说明不同之处只有一个
};
console.log(
  '回文验证:',
  validPalindrome('abaacaaa'),
  validPalindrome('ab'),
  validPalindrome('abc'),
  validPalindrome('aabsjdbaa')
);

这个写好之后,我在想能不能通过递归的形式来解决,为什么要创建第二个函数

  1. 递归解法:
const validPalindrome = (s, left = 0, right = s.length - 1, type = 'first') => {
  if (type === 'first') {
    // 第一次进入允许出现一次不同
    while (left < right) {
      if (s[left] !== s[right]) {
        return (
          validPalindrome(s, left + 1, right, 'second') ||
          validPalindrome(s, left, right - 1, 'second')
        ); // 左右两边都尝试一下 一边返回true即可
      }
      left++;
      right--;
    }
    return true; // 循环结束返回true
  } else {
    // 第二次 再有不同之处 返回false
    while (left < right) {
      if (s[left] !== s[right]) {
        return false;
      }
      left++;
      right--;
    }
    return true; // 循环结束一直相等返回true
    // 并且left不小于right 直接返回right,说明不同之处只有一个
  }
};
console.log(
  '回文验证:',
  validPalindrome('abaacaaa'),
  validPalindrome('ab'),
  validPalindrome('abc'),
  validPalindrome('aabsjdbaa')
);

相对于上个解法这里就是多设置了一个变量,然后将两方区分开来,但是这样递归我还是觉得太傻了,所以在想你能不能通过设置变量来处理?出现两次不同即失败。

  1. 设置一个变量允许一次不同
const validPalindrome = s => {
  let removed = false;
  for (let [i, j] = [0, s.length - 1]; i < j; i++, j--) {
    // 从两侧向中间递减 i- j-1 减到最后 i>j i=j 退出循环
    if (s[i] !== s[j]) {
      // 如果两侧不相同
      if (removed) {
        // 只允许一次不同
        return false;
      }
      if (s[i] === s[j - 1]) {
        // 转数组删除一个不同元素 下次直接return
        // s = [...s].splice(j, 1);
        // s = s.join(''); // 处理过的字符串
        j--;
        removed = true;
      } else if (s[i + 1] === s[j]) {
        // s = [...s].splice(i, 1);
        // s = s.join(''); // 处理过的字符串
        i++;
        removed = true;
      } else {
        // 上面两个else 右边-1 或左边+1相不相等 如果两边也不相等即false
        return false;
      }
    }
  }
  return true;
};
console.log(
  '回文验证:',
  validPalindrome('abaacaaa'),
  validPalindrome('ab'),
  validPalindrome('abc'),
  validPalindrome('aabsjdbaa')
);

# 代码地址

2018.8.12

# 点个Star支持我一下~

博客链接

数组中的最长单词 | 简单级-算法

博客链接

# 数组中的最长单词

# 难度:简单

# 描述:

给一个数组,找出其中所有最长的单词。

# 样例:

[
  "dog",
  "google",
  "facebook",
  "internationalization",
  "blabla"
]

最长的单词集合为 ["internationalization"]

[
  "like",
  "love",
  "hate",
  "yes"
]

最长的单词集合为 ["like", "love", "hate"]

# 思路分析:

主要要注意一下第二个栗子中描述的情况,建议保存当前字符最大的长度,然后及时更新。

# 代码模板:

const Solution = (arr) => {
}

# 想一想再看答案

# 想一想再看答案

# 想一想再看答案

# 代码:

const Solution = (arr) => {
    let store = {
        arr: [], // 保存最长单词的数组
        max: 1 // 字符串最大长度
    }
    arr.forEach(val => {
        // 当前值的长度 超过或等于 最大字符串长度
        if (val.length >= store.max) {
            if (val.length === store.max) {
                // 长度一样 将值添加进去
                return store.arr.push(val); // 退出循环
            }
            store.arr = []; // 最大值比之前的大,清空原数组
            store.arr.push(val); // 添加到数组
            store.max = val.length; // 更新最大值
        }
    });
    return store.arr; // 返回数组
}
let data1 = [
    "like",
    "love",
    "yes",
    "hate"
]
let data2 = [
    "dog",
    "google",
    "facebook",
    "internationalization",
    "blabla"
]
console.log(Solution(data1), Solution(data2))

# 点个Star支持我一下~

博客链接

丑数 | 中等级-算法

博客链接

# 丑数

# 难度:中等

# 描述:

设计一个算法,找出只含素因子 2,3,5 的第 n 小的数。

符合条件的数如:1, 2, 3, 4, 5, 6, 8, 9, 10, 12...

# 样例:

如果 n = 9, 返回 10

# 思路分析:

这类题目就是找规律,找到规律就好写了。

我再提供多一些数据:[1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24]

# 代码模板:

/**
 * @param n: An integer
 * @return: the nth prime number as description.
 */
const nthUglyNumber = function(n) {
  // write your code here
};

# 想一想再看答案

# 想一想再看答案

# 想一想再看答案

# 代码:

// [1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24]
const nthUglyNumber = function(n) {
  let arr = [1];
  let min,
    nex2,
    nex3,
    nex5,
    i2 = i3 = i5 = 0;
  for (let i = 1; i < n; i++) {
    // 除了第一个数,每个数都是2、3、5的倍数,把它们的倍数找出来,数字较小添加进去
    nex2 = arr[i2] * 2;
    nex3 = arr[i3] * 3;
    nex5 = arr[i5] * 5;
    min = Math.min(nex2, nex3, nex5);
    // 增加他们的倍数 为下次计算做准备
    if (min === nex2) i2++;
    if (min == nex3) i3++;
    if (min == nex5) i5++;
    arr.push(min);
  }
  return arr[arr.length - 1];
  // return arr
};
console.log('输出', nthUglyNumber(9), nthUglyNumber(15));

# 点个Star支持我一下~

博客链接

103 二叉树的锯齿形层次遍历 | 中等级-算法

博客链接

# 103 二叉树的锯齿形层次遍历

# 题目链接

# 难度:中等

# 思路分析:

广度或者深度,存的顺序要一致和取的顺序按照遍历的方向

#

#

#

#

#

#

#

#

#

#

#

#

#

#

# 代码:

广度优先: 核心是左侧一直在前面 右侧一直在后面 取的时候按照遍历顺序从前或者从后取

var zigzagLevelOrder = function(root) {
  if (root == null) return [];
  var arr = [root];
  var res = [];
  var go = true;
  while (arr.length > 0) {
    var n = arr.length;
    var now = [];
    if (go) {
      while (n > 0) {
        // 从左侧往右
        var node = arr.shift();
        now.push(node.val);
        // 下一轮是 从右往左
        // 从左往右循环一定要用push unshift右侧的值会在前面
        if (node.left != null) arr.push(node.left); // 左侧在前面
        if (node.right != null) arr.push(node.right); // 右侧在后面
        n--;
      }
      res.push(now); // 添加当前层级
    } else {
      while (n > 0) {
        // 从右往左 先取后面的
        var node = arr.pop();
        now.push(node.val);
        // 下一轮是从左往右
        // TODO: 从右往左一定要用unshift 用push左边的树的子节点可能会在后面
        if (node.right != null) arr.unshift(node.right); // 右侧在后面
        if (node.left != null) arr.unshift(node.left); // 左侧在前面
        n--;
      }
      res.push(now);
    }
    go = !go; // 更改方向
  }
  return res;
};

# 鼓励我一下:

觉得还不错的话,给我的项目点个star

# 点个Star支持我一下~

博客链接

102 二叉树的层序遍历 | 中等级-算法

博客链接

# 102 二叉树的层序遍历

# 题目链接

# 难度:中等

# 思路分析:

深度优先和广度优先

#

#

#

#

#

#

#

#

#

#

#

#

#

#

# 代码:

深度优先:

/**
 * @param {TreeNode} root
 * @return {number[][]}
 */
// dfs深度优先
var levelOrder = function(root) {
  const res = [];
  const levelNum = 0; // 当前层级
  dfs(root, levelNum);
  function dfs(node, step) {
    if (node) {
      // 当前节点是否有值
      if (res[step]) {
        // 该层级已添加过节点 在当前层级中继续添加
        res[step].push(node.val);
      } else {
        // 当前层级未添加过节点 创建一个数组 添加节点
        res.push([node.val]);
      }
      // 循环下个节点 增加层级
      dfs(node.left, step + 1);
      dfs(node.right, step + 1);
    }
  }
  return res;
};

广度优先:

var levelOrder = function(root) {
  const ret = [];
  if (!root) return ret;
  const q = []; // 栈
  q.push(root);
  while (q.length !== 0) {
    const currentLevelSize = q.length; // 记录下一层级的数量
    ret.push([]); // 层级初始化
    // 遍历广度 同一层级的元素都取出来
    for (let i = 1; i <= currentLevelSize; ++i) {
      const node = q.shift(); // 取出同一层级的元素
      ret[ret.length - 1].push(node.val); // 添加广度同一节点
      // 下个层级
      if (node.left) q.push(node.left);
      if (node.right) q.push(node.right);
    }
  }
  return ret;
};

# 鼓励我一下:

觉得还不错的话,给我的项目点个star

# 点个Star支持我一下~

博客链接

字符串密钥格式 | 简单级-算法

博客链接

# 字符串密钥格式

# 难度:简单

# 描述:

  1. 给定字符串 S(非空),字符串 S 仅由字母数字字符(a-z 和/或 A-Z 和/或 0-9)和短划线( - )组成。

  2. 给定正整数 K,我们希望重新格式化字符串,使得每个组包含正好的 K 个字符,但第一个组可能比 K 短,但仍必须包含至少一个字符。

  3. 必须在两个组之间插入短划线,并且所有小写字母都应转换为大写

# 样例:

Input: S = "5F3Z-2e-9-w", K = 4

Output: "5F3Z-2E9W"

Input: S = "2-5g-3-J", K = 2

Output: "2-5G-3J"

# 思路分析:

处理字符串通常需要转成数组来处理,仔细观察输出和规则,总结规律。

# 代码:

  1. 去掉-,等下用join连接。

  2. 字符串长度不能被K整除的话,需取余,将不能整除的部分拿出来。

  3. 然后每隔几个K每割一下字符串,这里用了正则,返回一个数组。

  4. 再跟之前被拿出来的部分,合并成一个数组。

  5. join将数组转成字符串。

const licenseKeyFormatting = function(S, K) {
  S = S.replace(/-/g, ''); // 去掉所有的-
  let total = [...S].length; // 字符串总数
  let num = total % K; // 取余
  let strArr = []; // 字符串剩余的放在这个数组中
  // 字符串余数
  if (num !== 0) {
    var str = '';
    var arr = [...S];
    var i = 0;
    for (let item of arr.keys()) {
      i++;
      str += arr[item]; // 有多少个余数就将多少个字符 添加到字符串中
      if (i === num) break;
    }
    arr.splice(0, num); // 删除已被添加的字符
    S = arr.join(''); // S重新变为字符串 用于下面操作
    strArr[0] = str; // 添加到数组 等下用于连接
  }
  let spliceNum = `\\w{${K}}`; // 几个字符串为一个间隔
  let reg = new RegExp(spliceNum, 'gim');
  let strArr2 = S.match(reg); // 切割字符串返回数组
  strArr = strArr.concat(strArr2); // 连接余数数组和切割的数组
  S = strArr.join('-').toUpperCase(); // 连接字符串 并转为大写
  return S;
};
console.log(
  licenseKeyFormatting('5F3Z-2e-9-w', 4),
  licenseKeyFormatting('2-5g-3-J', 2)
);

# 点个Star支持我一下~

博客链接

爬楼梯 | 简单级-算法

博客链接

# 爬楼梯

# 难度:简单

# 描述:

假设你正在爬楼梯,需要 n 步你才能到达顶部。但每次你只能爬一步或者两步,你能有多少种不同的方法爬到楼顶部?

# 样例:

比如 n=3,1+1+1=1+2=2+1=3,共有 3 种不同的方法

返回 3

# 思路分析:

这类题我们首先要来找其中的规律,找到了里面的规律,剩下的就好办了。

我再列举出几个结果:

0 =0 0种方法
1 = 1 种方法
2 = 1+1 =2 2种方法
3=1+1+1=1+2=2+1 3种方法
4 = 1*4 = 1+2+1 = 1+1+2 = 2+1+1 = 2+2 5种方法
5 = 1*5 = 2+1+2 =2+2+1 = 1+2+2 =1+2+1+1 = 1+1+2+1 = 2+1+1+1 = 1+1+1+2 8种方法

想一下他们的规律,试着自己做出来。

# 代码模板:

/**
 * @param n: An integer
 * @return: An integer
 */
const climbStairs = function(n) {};

# 想一想再看答案

# 想一想再看答案

# 想一想再看答案

# 规律:

这道题的规律实际上跟之前做的查找斐波纳契数列中第 N 个数中的规律有点类似。

斐波纳契数列中第 N 个数的规律

前 2 个数是 0 和 1,第 i 个数是第 i-1 个数和第 i-2 个数的和。

本题中的规律是

除了 1 阶楼梯和 2 阶楼梯是一种和两种方法之外,第 n 阶的楼梯的方法是第 i-1 阶楼梯和第 i-2 阶楼梯所用方法的和。

# 代码:

解题的核心就是逐步推导,推导出n前面的两个值

  1. 数组:
const climbStairs = function(n) {
  let dp = [0, 1, 2]; // 初始数组 前面三个没有规律
  for (let i = 3; i <= n; i++) {
    dp[i] = dp[i - 1] + dp[i - 2]; // 从3开始都是可以由前面两个元素相加推导出来
  }
  return dp[n];
};
console.log(climbStairs(3), climbStairs(4), climbStairs(5));
  1. 递归:
const climbStairs = function(n) {
  function item(n) {
    // 循环退出条件
    if (n === 1) return 1;
    if (n === 2) return 2;
    return item(n - 1) + item(n - 2); // 将递归到1个楼梯和两个楼梯 最后反推到n个楼梯
  }
  return item(n);
};
console.log(climbStairs(3), climbStairs(4), climbStairs(5));
  1. 交换变量:

实际上我们只需要 n 之前的两个值,就可以求出 n 所用的方法,所以我们没必要将 n 之前的所有值都推导出来。

所以我们只需要保存这两个值,然后再求出第三个值就可以了。

const climbStairs = function(n) {
  // 前两个值的返回结果
  if (n === 1) return 1;
  if (n === 2) return 2;
  let a = 1, // 1阶楼梯
    b = 2, // 2阶楼梯
    c;
  for (let i = 3; i <= n; i++) {
    c = a + b; // n的结果
    // 为了后续推导,不断保存前两个值
    a = b;
    b = c;
  }
  return c;
};
console.log(climbStairs(3), climbStairs(4), climbStairs(5));

实际上,我们也可以使用 ES6 的交换变量方法,而不用声明第三个变量:

const climbStairs = function(n) {
  // 前两个值的返回结果
  if (n === 1) return 1;
  if (n === 2) return 2;
  let a = 1,
    b = 2;
  for (var i = 3; i <= n; i++) {
    [a, b] = [b, b + a];
  }
  return b;
};
console.log(climbStairs(3), climbStairs(4), climbStairs(5));

# 点个Star支持我一下~

博客链接

无重复字符的最长子串 | 中等级-算法

博客链接

# 无重复字符的最长子串

# 难度:中等

# 描述:

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

# 样例:

  • 输入: "abcabcbb"

输出: 3

解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

  • 输入: "bbbbb"

输出: 1

解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

  • 输入: "pwwkew"

输出: 3

解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。

  • 输入: "dvdf"

输出: 3

解释: 因为无重复字符的最长子串是 "vdf",所以其长度为 3。

  • 输入: "asjrgapa"

输出: 6

解释: 因为无重复字符的最长子串是 "sjrgap",所以其长度为 6。

  • 输入: "aabaab!bb"

输出: 3

解释: 因为无重复字符的最长子串是 "ab!",所以其长度为 3。

  • 输入: "abcb"

输出: 3

解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

  • 输入: "asljlj"

输出: 4

解释: 因为无重复字符的最长子串是 "aslj",所以其长度为 4。

  • 输入: "qwnfenpglqdq"

输出: 8

解释: 因为无重复字符的最长子串是 "fenpglqd",所以其长度为 8。

# 思路分析:

关键在于在出现重复字符时,如何更新不重复字符的index

# 代码模板:

/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function (s) {
}

#

#

#

#

#

#

#

# 代码:

  1. 用对象储存字符的位置, 出现重复字符时更新不重复字符的index。
var lengthOfLongestSubstring = function (s) {
    let obj = {}; // 用于储存字符出现的位置
    let res = 0; // 最大值
    let j = 0; // 不重复字符的index
    for (let i = 0; i < s.length; i++) {
        // 当前值是否在对象中存储过
        const value = obj[s[i]]
        if (value !== undefined) {
            // 更新上一次重复值的index
            // value + 1 跳过之前重复的字符
            // j: 之前不重复的index 重复字符 需要全部跳过
            j = Math.max(value + 1, j)
    <span class="token punctuation">}</span>
    <span class="token comment">// 每个字符都计算一下最长不重复值 保存最大值</span>
    <span class="token comment">// 不重复最长长度 = 当前index - 上一次重复值的index + index从0开始 长度从1开始</span>
    res <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">max</span><span class="token punctuation">(</span>res<span class="token punctuation">,</span> i <span class="token operator">-</span> j <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 存/更新 字符串index</span>
    obj<span class="token punctuation">[</span>s<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">]</span> <span class="token operator">=</span> i
<span class="token punctuation">}</span>
<span class="token keyword">return</span> res<span class="token punctuation">;</span>

};

  1. 从左到右,一个字符一个字符搜索,看是否重复。
var lengthOfLongestSubstring = function (s) {
    var i = 0, // 不重复字符的index
        res = 0; // 更新无重复字符的长度
    for (j = 0; j < s.length; j++) {
        // 查找:不重复字符-当前index之间 有没有出现当前字符
        let index = s.slice(i, j).indexOf(s[j])
        if (index === -1) {
            // 更新无重复字符的长度:当前index-不重复字符的index + 长度从1开始算
            res = Math.max(res, j - i + 1);
        } else {
            // 更新i = 不重复字符的index
            // 不重复字符的index = 原不重复的字符index + i-j中出现重复字符的index + 跳过该重复字符
            i = i + index + 1;
        }
    }
    return res;
};

# 点个Star支持我一下~

博客链接

前端算法

博客链接

# 前端算法

这个部分主要记录了一些算法题, 文档会将算法按照难度分级,代码中都有详细注释,且会提供多种解法

# 提高自身编程能力和逻辑能力

曾经咨询过一个大佬,大佬告诉我:

刷算法题对于提高自身编程能力和逻辑能力是一种相当有效的途径。

一两道题可能还没有感觉,刷的多了,解题思路多了,就会出现质的突破。

# 面试看看

面试前也可以看一下本文档, 毕竟蛮多公司喜欢考算法题的,中小型公司出的算法题都不会太难,大部分可以在文档中找到。

希望大家看完能够有所收获, 如果觉得本文档还不错的话,记得给个Star鼓励一下我吧~

# 来社区关注我,不错过最新文章:

# 点个Star支持我一下~

博客链接

最大子数组 | 简单级-算法

博客链接

# 最大子数组

# 难度:简单

# 描述:

给定一个整数数组,找到一个具有最大和的子数组,返回其最大和。

# 样例:

给出数组[−2,2,−3,4,−1,2,1,−5,3],符合要求的子数组为[4,−1,2,1],其最大和为 6

# 思路分析:

本题只要找出最大和即可,保存两个值,一个为元素之间相加的值(需比较元素相加的值与当前元素的大小),一个为最大值。

# 代码:

/**
 * @param nums: A list of integers
 * @return: A integer indicate the sum of max subarray
 */
const maxSubArray = function(nums) {
  let max = nums[0]; // 初始化最大值
  let newMax = nums[0]; // 数组元素相加的缓存值
  for (let i = 1; i < nums.length; i++) {
    newMax = Math.max(newMax + nums[i], nums[i]); // 相加是否大于当前值
    max = Math.max(newMax, max); // 与最大值相比
  }
  return max;
};

第二种方法更难理解点,可以扩展一下思路:

/**
 * @param nums: A list of integers
 * @return: A integer indicate the sum of max subarray
 */
const maxSubArray = function(nums) {
  var nSum = nums[0]; // 数组元素相加的缓存值
  var nMax = nSum; // 初始化最大值
  for (var i = 1; i < nums.length; i++) {
    var curNum = nums[i]; // 当前元素
    if (curNum >= 0) nSum = nSum > 0 ? nSum + curNum : curNum;
    // 如果两个值都大于0 两个值相加。否则就取后一个大于0的
    else nSum = nSum < curNum ? curNum : nSum + curNum; // 如果新加的值小于0 判断结果是否大于新加的值 小于的话就改为新加的值
    nMax = Math.max(nMax, nSum); // 最大值与数组元素相加值比较
  }
  return nMax;
};

# 最大和的数组:

如果你想把最大和的数组都找出来,你需要保存数组的开始下标和结束下标,这里我演示了第一个方法,下面那个方法也是一样:

const maxSubArray = function(nums) {
  let max = {
    num: nums[0],
    start: 0,
    end: 1 // 结束的下标 后面要切割数组 需比当前下标+1.把当前值也切割
  };
  let newMax = {
    num: nums[0],
    start: 0,
    end: 1
  };
  for (let i = 1; i < nums.length; i++) {
    if (newMax.num + nums[i] > nums[i]) {
      // 相加大于当前值 缓存改值和结束下标
      newMax.num = newMax.num + nums[i];
      newMax.end = i + 1;
    } else {
      // 小于当前值 重置start end
      newMax.num = nums[i];
      newMax.start = i;
      newMax.end = i + 1;
    }
    // 最大值被超过 把值赋给最大值
    if (max.num < newMax.num) {
      max.num = newMax.num;
      max.start = newMax.start;
      max.end = newMax.end;
    }
  }
  let arr = nums.slice(max.start, max.end); // 找出最大和的子数组
  return max.num; // 子数组的最大和
};

# 点个Star支持我一下~

博客链接

199 二叉树的右视图 | 中等级-算法

博客链接

# 199 二叉树的右视图

# 题目链接

# 难度:中等

# 思路分析:

深度优先、广度优先

#

#

#

#

#

#

#

#

#

#

#

#

#

#

# 代码:

深度优先:

var rightSideView = function(root) {
  if (!root) return [];
  let res = []; // 结果
  let start = 0; // 树的深度 从0开始
  dfs(root, start, res);
  return res;
};
function dfs(root, step, res) {
  if (root) {
    if (step === res.length) {
      res.push(root.val); // 数组长度等于树的深度时 添加当前值 因为右侧先遍历 有右侧先添加右侧
    }
    dfs(root.right, step + 1, res);
    dfs(root.left, step + 1, res);
  }
}

广度优先:

var rightSideView = function(root) {
  if (!root) return [];
  let queue = [root]; // 队列 把树顶加入队列
  let arr = []; // 用来存储每层最后个元素值
  while (queue.length > 0) {
    let len = queue.length; // 当前层的广度
    while (len) {
      let node = queue.shift(); // 依次取出当前层队列的元素 从左到右
      if (len === 1) arr.push(node.val); // 当是 当前一层的最后一个元素时,把值加入arr
      if (node.left) queue.push(node.left); // 先添加左侧的
      if (node.right) queue.push(node.right); // 最后添加右侧的 等到最后一个元素时即可添加右侧的值
      len--;
    }
  }
  return arr;
};

# 鼓励我一下:

觉得还不错的话,给我的项目点个star

# 点个Star支持我一下~

博客链接

找到和为零的子数组 | 简单级-算法

博客链接

# 找到和为零的子数组

# 难度:简单

# 描述:

给定一个整数数组,找到和为零的子数组。你的代码应该返回满足要求的子数组的起始位置和结束位置

# 样例:

给出 [-3, 1, 2, -3, 4],返回[0, 2] 或者 [1, 3]

# 代码模板:

/**
 * @param nums: A list of integers
 * @return: A list of integers includes the index of the first number and the index of the last number
 */
const subarraySum = function(nums) {};

# 想一想再看答案

# 想一想再看答案

# 想一想再看答案

# 代码:

const subarraySum = function(nums) {
  let [total, res] = [0, []]; // 和,结果
  for (let i = 0; i < nums.length; i++) {
    res = [];
    res[0] = i; // 保存起始位置
    // 需要第二层循环 可能第一个元素直到最后和也不是0 那么需要从第二个元素再找 以此类推
    for (let j = i; j < nums.length; j++) {
      total = total - nums[j]; // 不断减去每个元素 直到结果为0
      if (total === 0) {
        res[1] = j; // 保存第二个下标
        return res; // 返回起始和结束的下标
      }
    }
  }
  return false; // 没找到
};
console.log(subarraySum([-3, 1, 2, -3, 4]));

# 点个Star支持我一下~

博客链接

命名变量看的想打人。。。

// 2. 遍历 保存计算值
// const fibonacci = (n) => {
// let a = 0, b = 1, c, d = [0];
// for (let i = 1; i < n; i++) {
// c = a + b;
// a = b;
// b = c;
// d.push(a); // 恢复数列
// }
// return a
// }

93 复原 IP 地址 | 中等级-算法

博客链接

# 93 复原 IP 地址

# 题目链接

# 难度:中等

# 思路分析:

编程题,递归

#

#

#

#

#

#

#

#

#

#

#

#

#

#

# 代码:

递归

var restoreIpAddresses = function(s) {
  if (s.length > 12) return [];
  let result = [];
  fn(s, [], result);
  return result;
};
// 递归
function fn(remain, temp, result) {
  // 第四段
  if (temp.length === 3) {
    if (regular(remain)) {
      // 合法即为正确的值
      result.push([...temp, remain].join("."));
    }
    return;
  }
  // 每段长度都可能为1/2/3
  for (let i = 1; i < 4; i++) {
    // 合法才可继续
    if (regular(remain.substr(0, i))) {
      const strArr = [...temp, remain.substr(0, i)]; // 字符段
      const str = remain.substr(i); // 剩下的字符串
      fn(str, strArr, result);
    }
  }
}
// 验证合法性
function regular(s) {
  if (!s.length) return false;
  return 0 <= +s && +s <= 255 && (s.length > 1 ? !!+s[0] : true);
}

# 鼓励我一下:

觉得还不错的话,给我的项目点个star

# 点个Star支持我一下~

博客链接

找出数组重复次数最多的元素 | 入门级-算法

博客链接

# 找出数组重复次数最多的元素

# 描述:

给定一个字符串数组, 每一个元素代表一个 IP 地址,找到出现频率最高的 IP。

注:给定数据只有一个频率最高的 IP

# 样例:

lines = ['192.168.1.1', '192.118.2.1', '192.168.1.1'];
return '192.168.1.1';

# 题目分析:

说了一堆,其实就是找出数组重复次数最多的元素

思路:

用对象来处理,将元素赋值到属性上,判断之前有没有这个属性。

数组去重

虽然对象属性同样可以用来做数组去重,但是会将 number,NaN,undefined,null,变为字符串形式,因为对象的属性名就是一个字符串

# 代码:

/**
 * @param ipLines: ip  address
 * @return: return highestFrequency ip address
 */
const highestFrequency = function(ipLines) {
  var [obj, max, name] = [{}, 1, ''];
  ipLines.forEach(value => {
    if (obj[value]) {
      // 已经有值了 就把值+1
      obj[value]++;
      if (obj[value] > max) { // 判断重复次数有没有超过当前最高的
        max = obj[value]; // 重复次数
        name = value; // 当前元素
      }
    } else {
      // 没有值 就初始化一个值
      obj[value] = 1;
    }
  });
  return name;
};

# 点个Star支持我一下~

博客链接

统计数字 | 中等级-算法

博客链接

# 统计数字

# 难度:中等

# 描述:

计算数字 k 在 0 到 n 中的出现的次数,k 可能是 0~9 的一个值

# 样例:

n=12,k=1

在 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],我们发现 1 出现了 5 次 (1, 10, 11, 12)

返回 5

# 思路分析:

因为一个数可能会出现两次k,转成字符串来操作,遍历字符来匹配k。

如果可以的话,使用正则是更优解。

# 代码模板:

/**
 * @param k: An integer
 * @param n: An integer
 * @return: An integer denote the count of digit k in 1..n
 */
const digitCounts = function(k, n) {
  // write your code here
};

# 想一想再看答案

# 想一想再看答案

# 想一想再看答案

# 代码:

  1. 遍历 n 的范围,遍历数字
const digitCounts = function(k, n) {
  let count = 0;
  k = k.toString();
  // 遍历n的范围
  for (let i = 0; i <= n; i++) {
    let str = i.toString(); // 存字符
    for (let m = 0; m < str.length; m++) {
      //  遍历字符 计算出现两个k的情况
      if (str[m] === k) {
        count++;
      }
    }
  }
  return count;
};
console.log('输出:', digitCounts(1, 12)); // 5
  1. 将范围连接成一个字符串,遍历字符串
const digitCounts = function(k, n) {
  let sum = 0,
    s = [...Array(n + 1).keys()].join(''); // 将数字范围转成数组再链接成字符串
  // 比如 s = "0123456789101112", n = 12
  k = k.toString(); // 转字符用于判断
  for (var i = s.length - 1; i >= 0; i--) {
    // 遍历字符串 判断是否与k相等
    if (s[i] === k) {
      sum++;
    }
  }
  return sum;
};
console.log('输出:', digitCounts(1, 12)); // 5
  1. 最优:遍历范围,正则匹配
const digitCounts = function(k, n) {
  let num = 0;
  let reg = new RegExp(`${k}`, 'g'); // 正则 全局匹配k
  for (let i = 0; i <= n; i++) {
    let res = i.toString().match(reg); // 搜索i,返回一个匹配的数组
    if (res) {
      num = num + res.length; // 匹配的数量
    }
  }
  return num;
};
console.log('输出:', digitCounts(1, 12)); // 5

# 点个Star支持我一下~

博客链接

检测2的幂次 | 简单级-算法

博客链接

# 检测2的幂次

# 难度:简单

# 描述:

检测整数 n 是否是 2 的幂次

# 样例:

n=8,返回 true;

n=10,返回 false.

# 思路分析:

使用Math.pow()来检测当前值是否为2的幂次

# 代码模板:

/**
 * @param n: An integer
 * @return: True or false
 */
const checkPowerOf2 = function (n) {
}

# 想一想再看答案

# 想一想再看答案

# 想一想再看答案

# 代码:

const checkPowerOf2 = function (n) {
    var i = 0
    // 一步步增加2的幂次 并对比
    while (i < 32) {
        let multiple = Math.pow(2, i); // 当前幂次
        if (n === multiple) {
            return true // 是2的幂次
        }
        if (multiple > n) return false; // n小于当前幂次 return false 取消无 用遍历 缩短运行时间
        i++
    }
    return false
}
console.log(checkPowerOf2(8),checkPowerOf2(10));

# 点个Star支持我一下~

博客链接

33 搜索旋转排序数组 | 中等级-算法

博客链接

# 33 搜索旋转排序数组

# 题目链接

# 难度:中等

# 思路分析:

二分查找

#

#

#

#

#

#

#

#

#

#

#

#

#

#

# 代码:

二分查找:

/**
 * @description
 * TODO: 利用二分查找方式
 * 1.利用将数组从中间分开
 * 此时肯定存在前半部分或是后半部分是有序的(重要)
 * 2.对有序部分执行二分查找
 * 3.如果目标值不可能存在于有序部分
 * 4.则将目标查找数组选择在无序部分
 * 5.继续进行1进行判断
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
  if (!nums || nums.length === 0) return -1;
  let start = 0;
  let end = nums.length - 1;
  let mid;
  while (start <= end) {
    mid = Math.ceil((start + end) / 2);
    // 首尾中全部验证
    if (nums[mid] === target) return mid;
    if (nums[start] === target) return start;
    if (nums[end] === target) return end;
    // 说明前半部分有序
    if (nums[start] < nums[mid]) {
      // 说明目标值存在于有序部分,将末尾设置为mid
      // 判断目标值是否在前半部分
      // 更新索引 继续执行二分查找
      if (nums[start] < target && target < nums[mid]) {
        end = mid - 1;
      } else {
        // 说明目标值存在于后半段
        start = mid + 1;
      }
    } else {
      // 说明后半部分有序
      // 判断目标值是否在后半部分
      // 更新索引 继续执行二分查找
      if (nums[mid] < target && target < nums[end]) {
        start = mid + 1;
      } else {
        end = mid - 1;
      }
    }
  }
  return -1;
};

# 鼓励我一下:

觉得还不错的话,给我的项目点个star

# 点个Star支持我一下~

博客链接

中位数 | 简单级-算法

博客链接

# 中位数

# 难度:简单

# 描述:

给定一个未排序的整数数组,找到其中位数。

中位数是排序后数组的中间值,如果数组的个数是偶数个,则返回排序后数组的第 N/2 个数。

# 样例:

给出数组[4, 5, 1, 2, 3], 返回 3

给出数组[7, 9, 4, 5],返回 5

# 思路分析:

  1. 升序排序数组
  2. 模拟几个数组的返回值,找到里面的规律,找出数组中对应元素。

# 代码模板:

/**
 * @param nums: A list of integers
 * @return: An integer denotes the middle number of the array
 */
const median = function(nums) {};

# 想一想再看答案

# 想一想再看答案

# 想一想再看答案

# 代码:

  1. 判断奇数偶数,找到对应的下标
/**
 * @param nums: A list of integers
 * @return: An integer denotes the middle number of the array
 */
const median = function(nums) {
  nums.sort((a, b) => {
    return a - b; // 升序排序
  });
  var num = nums.length; // 保存数组长度
  if (num % 2 !== 0) {
    // 判断奇数偶数
    return nums[(num + 1) / 2 - 1]; // 奇数转偶数
  } else {
    return nums[num / 2 - 1]; // 减一 对应数组下标
  }
};
  1. 奇数上舍入,找到下标

两种写法一样,但无疑第二种写法更为优雅。

/**
 * @param nums: A list of integers
 * @return: An integer denotes the middle number of the array
 */
const median = function(nums) {
  nums.sort((v1, v2) => v1 - v2);
  return nums[Math.ceil(nums.length / 2) - 1]; // 
};

# 点个Star支持我一下~

博客链接

摆动序列 | 中等级-算法

博客链接

# 摆动序列

# 难度:中等

# 摆动序列

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。

例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3)是正负交替出现的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。

# 描述:

给定一个整数序列,返回作为摆动序列的最长子序列的长度

通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。

# 样例:

# 示例1:

输入: [1,7,4,9,2,5]
输出: 6 
解释: 整个序列均为摆动序列。

# 示例2:

输入: [1,17,5,10,13,15,10,5,16,8]
输出: 7
解释: 这个序列包含几个长度为 7 摆动序列,其中一个可为[1,17,10,13,10,16,8]

# 示例3:

输入: [1,2,3,4,5,6,7,8,9]
输出: 2

# 思路分析:

  1. 整数序列可以删除
  2. 序列要不断上升和下降才有效。

# 代码模板:

const wiggleMaxLength = function (nums) {
}

#

#

#

#

#

#

#

# 代码:

  1. 缓存上次的摆动方向, 只关注下一个正确的摆动方向。

当方向正确序列的长度就可以增加了,中间的连续上升/下降不用管。

const wiggleMaxLength = function (nums) {
    if (nums.length < 2) {
        return nums.length; // 小于2 直接返回
    }
    let length = 1; // 默认每个数字为1
    let flag = "begin"; // 初始摆动方向
    for (var i = 0; i < nums.length - 1; i++) {
        switch (flag) {
            case "begin":
                if (nums[i] < nums[i + 1]) {
                    flag = "up"; // 摆动方向
                    length++; // 初始两个值为摆动序列
                } else if (nums[i] > nums[i + 1]) {
                    flag = "down";
                    length++; // 初始两个值为摆动序列
                }
                break;
            case "up":
                if (nums[i] > nums[i + 1]) {
                    // 找到下一组下一个值比本身小的值
                    flag = "down";
                    length++;
                }
                break;
            case "down":
                if (nums[i] < nums[i + 1]) {
                    // 找到下一组下一个值比本身大的值
                    flag = "up";
                    length++;
                }
                break;
        }
    }
    return length;
}
  1. 将上升和下降视为一组,当正确摆动过一次(上升和下降各出现一次)时,序列的长度+1。

连续摆动因为另一个变量没有变化,所以就会得到相同的结果,相当于跳过。

const wiggleMaxLength = function (nums) {
    let len = nums.length
    if (len < 2) return len // 小于2 返回它本身的长度 大于2的数量 进入比较
    let up = 1
    let down = 1
    for (let i = 1; i < len; i++) {
        // 当出现连续 下降/上升时,另一个用于阶加的变量没有变化,所以会跳过连续 下降/上升
        if (nums[i] > nums[i - 1]) {
            up = down + 1
        } else if (nums[i] < nums[i - 1]) {
            down = up + 1
        }
<span class="token punctuation">}</span>
<span class="token comment">// 取下降和上升的最大值</span>
<span class="token keyword">return</span> Math<span class="token punctuation">.</span><span class="token function">max</span><span class="token punctuation">(</span>up<span class="token punctuation">,</span> down<span class="token punctuation">)</span>

};

# 鼓励我一下:

觉得还不错的话,给我的项目点个star

# 点个Star支持我一下~

博客链接

原地删除数组元素 | 简单级-算法

博客链接

# 原地删除数组元素

# 难度:简单

# 描述:

给定一个数组和一个值,在原地删除与值相同的数字,返回新数组的长度。

元素的顺序可以改变,并且对新的数组不会有影响。

# 样例:

给出一个数组 [0,4,4,0,0,2,4,4],和值 4

返回 4 并且 4 个元素的新数组为[0,0,0,2]

# 代码模板:

const removeElement = (arr, ele) => {};

# 想一想再看答案

# 想一想再看答案

# 想一想再看答案

# 代码:

  1. 保存遍历次数,匹配元素,然后删除

切勿直接使用数组的length属性,因为被删除后length属性会减少,导致遍历提前结束,删除不彻底。

const removeElement = (arr, ele) => {
  let num = arr.length; // 保存遍历的次数
  for (let index = 0; index < num; index++) {
    let find = arr.indexOf(ele);
    if (find !== -1) {
      arr.splice(find, 1); // 原地删除
    } else {
      return arr.length; // 找不到即退出
    }
  }
};
  1. 遍历数组,匹配元素,赋值为null/undefined,再过滤掉
const removeElement = (arr, ele) => {
  for (let index of arr.keys()) {
    let find = arr.indexOf(ele);
    if (find !== -1) {
      arr[find] = null;
    } else {
      return arr.filter(x => x).length; // 将假值过滤掉
    }
  }
};
  1. 直接过滤

在写出上个方法之后,想到可以直接过滤掉,最简洁。但还是把另外两个方法放上来,当个思路参考一下!

const removeElement = (arr, ele) => {
  return arr.filter(x => x !== ele).length; // 使用过滤将值不等于ele的直接过滤出来,返回长度
};

# 点个Star支持我一下~

博客链接

奇偶分割数组 | 简单级-算法

博客链接

# 奇偶分割数组

# 难度:简单

# 描述:

分割一个整数数组,使得奇数在前偶数在后。

# 样例:

给定 [1, 2, 3, 4],返回 [1, 3, 2, 4]。

# 增加一下难度:

给定乱序数组:[2, 5, 1, 6, 3, 4],返回[1, 3, 5, 2, 4, 6]

# 思路分析:

排序好的数组:找到奇数进行操作。

乱序的数组:使用sort方法进行排序+提取奇数

# 代码模板:

const partitionArray = arr => {};

# 想一想再看答案

# 想一想再看答案

# 想一想再看答案

# 代码:

  1. 排序好的数组找到奇数进行操作
const partitionArray = arr => {
  let num = arr.length - 1;
   // 其实如果是乱序数组,可以在这里使用sort将数组排序好再走下面那部分也可以
  // 倒序遍历
  for (let i = num; i >= 0; i--) {
    if (arr[i] % 2 !== 0) {
      let item = arr.splice(i, 1); // 将当前值取出来
      arr.unshift(item[0]); // 添加到首位
    }
  }
  return arr;
};
console.log('输出', partitionArray([1, 2, 3, 4]));
  1. 乱序数组,排序+取奇数偶数

这种方法无疑是更好的解决方法,事实上涉及排序最好都是使用sort进行排序,对 sort 不熟的,可以看下之前写的这篇数组 API 解析合集

const partitionArray = arr => {
  return arr.sort((a, b) => {
    if (a % 2 !== 0 && b % 2 !== 0) {
      // 当两个数都是奇数的情况下 按大小排序
      return a - b;
    } else if (a % 2 === 0 && b % 2 === 0) {
      // 当两个数都是偶数的情况下也是按大小排序
      return a - b;
    } else if (a % 2 !== 0) {
      // 当a是奇数 要排在b的前面
      return -1;
    } else if (b % 2 !== 0) {
      // 当b是奇数 排在a的前面
      return 1;
    }
  });
};
console.log(
  '输出',
  partitionArray([1, 2, 3, 4]),
  partitionArray([2, 5, 1, 6, 3, 4])
);

# 点个Star支持我一下~

博客链接

146LRU 缓存机制 | 中等级-算法

博客链接

# 146LRU 缓存机制

# 题目链接

# 难度:中等

# 思路分析:

编程题。

链表,数组,对象都可以。

#

#

#

#

#

#

#

#

#

#

#

#

#

#

# 代码:

链表:

/**
 * @param {number} capacity
 */
var LRUCache = function(capacity) {
  this.map = new Map(); // map默认记住插入的顺序
  this.max = capacity; // 最大数量
};

/**

  • @param {number} key
  • @return {number}
    /
    LRUCache.prototype.get = function(key) {
    const value = this.map.get(key) || -1;
    if (value !== -1) {
    this.map.delete(key); // 删除更新插入顺序
    this.map.set(key, value);
    }
    return value;
    };
    /
    *
  • @param {number} key
  • @param {number} value
  • @return {void}
    */
    LRUCache.prototype.put = function(key, value) {
    if (this.map.has(key)) {
    this.map.delete(key); // 删除更新插入顺序
    }
    this.map.set(key, value);
    if (this.max < this.map.size) {
    const mapKeys = this.map.keys(); // 获取遍历值
    const oldKey = mapKeys.next().value; // map插入顺序 默认第一个即最早插入的值
    this.map.delete(oldKey); // 删除最早的值
    }
    };

对象/map

// TODO: map: 用时88.91, 内存100

/**

  • @param {number} capacity
    */
    var LRUCache = function(capacity) {
    this.map = new Map(); // map默认记住插入的顺序
    this.max = capacity; // 最大数量
    };

/**

  • @param {number} key
  • @return {number}
    /
    LRUCache.prototype.get = function(key) {
    const value = this.map.get(key) || -1;
    if (value !== -1) {
    this.map.delete(key); // 删除更新插入顺序
    this.map.set(key, value);
    }
    return value;
    };
    /
    *
  • @param {number} key
  • @param {number} value
  • @return {void}
    */
    LRUCache.prototype.put = function(key, value) {
    if (this.map.has(key)) {
    this.map.delete(key); // 删除更新插入顺序
    }
    this.map.set(key, value);
    if (this.max < this.map.size) {
    const mapKeys = this.map.keys(); // 获取遍历值
    const oldKey = mapKeys.next().value; // map插入顺序 默认第一个即最早插入的值
    this.map.delete(oldKey); // 删除最早的值
    }
    };

数组

//  数组: 用时36, 内存100;

/**

  • @param {number} capacity
    */
    var LRUCache = function(capacity) {
    this.keyArr = []; // key最近使用的值
    this.data = {}; // 存储数据
    this.max = capacity; // 最大数量
    };

/**

  • @param {number} key
  • @return {number}
    */
    LRUCache.prototype.get = function(key) {
    const index = this.findIndex(key);
    if (index !== -1) {
    this.updateNew(key, index);
    return this.data[key];
    }
    return -1;
    };
    // 更新key的新鲜值
    LRUCache.prototype.updateNew = function(key, index) {
    this.keyArr.splice(index, 1);
    this.keyArr.unshift(key); // 更新key的新鲜值
    };

// 寻找key的位置
LRUCache.prototype.findIndex = function(key) {
return this.keyArr.findIndex((item) => {
return item === key;
});
};

/**

  • @param {number} key
  • @param {number} value
  • @return {void}
    */
    LRUCache.prototype.put = function(key, value) {
    const index = this.findIndex(key);
    if (index !== -1) {
    this.updateNew(key, index);
    this.data[key] = value;
    } else {
    this.data[key] = value;
    this.keyArr.unshift(key);
    }
    if (this.max < this.keyArr.length) {
    this.keyArr.pop(); // 删除最后一个值
    }
    };

# 鼓励我一下:

觉得还不错的话,给我的项目点个star

# 点个Star支持我一下~

博客链接

46 全排列 | 中等级-算法

博客链接

# 46 全排列

# 题目链接

# 难度:中等

# 思路分析:

回溯

#

#

#

#

#

#

#

#

#

#

#

#

#

#

# 代码:

回溯

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var permute = function(nums) {
  let res = [];
  dfs([]);
  function dfs(path) {
    if (path.length === nums.length) {
      const item = [...path]; // 复制path 引用类型 指针相同
      res.push(item); // 一条路径完成
      return;
    }
    // 遍历决策树
    for (let num of nums.values()) {
      if (path.includes(num)) continue; // 已存在的元素不再添加 防止重复
      path.push(num); // 每个节点 都选择一遍它的路径
      dfs(path); // 穷尽它的路径 回溯
      path.pop(); // 撤销选择的节点 回归原先的状态 回溯
    }
  }
  return res;
};

# 鼓励我一下:

觉得还不错的话,给我的项目点个star

# 点个Star支持我一下~

博客链接

322 零钱兑换 | 中等级-算法

博客链接

# 322 零钱兑换

# 题目链接

# 难度:中等

# 思路分析:

动态规划

#

#

#

#

#

#

#

#

#

#

#

#

#

#

# 代码:

自底而上动态规划 从头开始找最优解

var coinChange = function(coins, amount) {
  let dp = new Array(amount + 1).fill(Infinity); // 初始无限大 再比较的时候 会使用零钱次数
  dp[0] = 0;
  // 所有零钱种类
  for (let coin of coins) {
    // 总金额
    for (let i = 1; i <= amount; i++) {
      if (i - coin >= 0) {
        // 总金额大于零钱
        // dp[i - coin] + 1 当前零钱需要的次数
        // dp[i] 其他零钱种类的最少次数
        // 如果前面的找不到最优解 会变为Infinity
        dp[i] = Math.min(dp[i], dp[i - coin] + 1);
      }
    }
  }
  console.log("res", dp);
  // 如果没找到 返回-1
  return dp[amount] === Infinity ? -1 : dp[amount];
};

# 鼓励我一下:

觉得还不错的话,给我的项目点个star

# 点个Star支持我一下~

博客链接

判断字符串的循环移动 | 简单级-算法

博客链接

# 判断字符串的循环移动

# 难度:简单

# 描述:

可以检验某个单词是否为另一个单词的子字符串。给定 s1 和 s2,请设计一种方法来检验 s2 是否为 s1 的循环移动后的字符串。

# 样例:

s1 = waterbottle; s2 = erbottlewat; 返回true;

s1 = apple; s2 = ppale; 返回false;

# 思路分析:

将其中一个字符串转成数组来操作,然后再转成字符,回头来比较字符串。

# 代码模板:

/**
 * @param s1: the first string
 * @param s2: the socond string
 * @return: true if s2 is a rotation of s1 or false
 */
const isRotation = function(s1, s2) {};

# 想一想再看答案

# 想一想再看答案

# 想一想再看答案

# 代码:

// 将最后的值拿出来 再放到第一位上去
const isRotation = (s, t) => {
  if (s.length === t.length && s && t) {
    for (let i = 0; i < s.length; i++) {
      t = [...t]; // 转数组
      let pop = t.pop(); // 拿最后一个元素
      t.unshift(pop); // 添加到第一个元素
      t = t.join(''); // 转字符
      if (t === s) return true; // 比较
    }
  }
  return false; // 字符串长度相等 并且有值
};
console.log(
  '输出:',
  isRotation('waterbottle', 'erbottlewat'),
  isRotation('apple', 'ppale')
);

# 点个Star支持我一下~

博客链接

搜索二维矩阵 | 简单级-算法

博客链接

# 搜索二维矩阵

# 难度:简单

# 描述:

写出一个高效的算法来搜索 m × n 矩阵中的值。

这个矩阵具有以下特性:

  1. 每行中的整数从左到右是从小到大排序的。
  2. 每行的第一个数大于上一行的最后一个整数。

# 样例:

[
  [1, 3, 5, 7],
  [10, 11, 16, 20],
  [23, 30, 34, 50]
]

给出 target = 3,返回 true

# 题目分析:

双循环找出是否有这个值,根据第二个特性,我们可以跳过一些第二层循环,算法更具效率。

# 代码:

/**
 * @param matrix: matrix, a list of lists of integers
 * @param target: An integer
 * @return: a boolean, indicate whether matrix contains target
 */
const searchMatrix = function(matrix, target) {
  for (let key of matrix.keys()) { // 遍历外层数组
    let value = matrix[key]; // 拿到每行元素
    // 判断target是否在当前行中,跳过其他不必要循环
    if (target <= value[value.length - 1]) { 
      for (let item of value.keys()) { // 遍历行中元素 
        if (target === value[item]) { // 找到值
          return true;
        } else if (target < value[item]) {  // 值超过target即找不到(因为是排序的)
          return false;
        }
      }
    }
  }
  return false;  // 没有找到即返回false
};

# 点个Star支持我一下~

博客链接

第一个只出现一次的字符 | 简单级-算法

博客链接

# 第一个只出现一次的字符

# 难度:简单

# 描述:

给出一个字符串,找出第一个只出现一次的字符。

# 样例:

对于 aabc, b为第一个只出现一次的字符.

对于 abaccdeff, b为第一个只出现一次的字符.

# 思路分析:

可以用对象保存字符出现的次数。

# 代码模板:

const firstUniqChar = function(str) {};

# 想一想再看答案

# 想一想再看答案

# 想一想再看答案

# 代码:

  1. 将值删除,用 indexOf 查找还有没有相同字符,并查找之前删过的字符
const firstUniqChar = function(str) {
  str = [...str];
  let num = str.length; // 保存遍历次数
  let obj = {}; // 保存被删元素
  for (let i = 0; i < num; i++) {
    let item = str.splice(0, 1)[0]; // 删除第一个值
    if (str.indexOf(item) === -1 && obj[item] === undefined) {
      // 当前数组中没有 并且对象中也没有
      return item; // 找到
    } else {
      obj[item] = item; // 出现的字符串,用对象保存起来。
    }
  }
};
console.log('输出:', firstUniqChar('abaccdeff'), firstUniqChar('aabc'));
  1. indexOf 的第二个参数,从当前值往后搜索,并查找之前已经查过的字符

    想起了indexOf的第二个参数,省了一步删除的操作。

const firstUniqChar = function(str) {
  str = [...str];
  let obj = {};
  for (let [index, key] of str.entries()) {
    if (str.indexOf(key, index + 1) === -1 && obj[key] === undefined) {
      // 跳过这个元素,当后面没有 并且前面也没有
      return key; // 找到
    } else {
      obj[key] = key; // 前面出现过 存起来
    }
  }
};
console.log('输出:', firstUniqChar('abaccdeff'), firstUniqChar('aabc'));
  1. 记录字符出现的次数,遍历字符串,第一个只出现一次的字符,就是要找的值。
const firstUniqChar = function(str) {
  var obj = {}; // 用对象
  for (var i = 0; i < str.length; i++) {
    var code = str.charCodeAt(i);
    // 记录出现的次数
    if (obj[code] == undefined) {
      obj[code] = 1;
    } else {
      obj[code]++;
    }
  }
  for (var i = 0; i < str.length; i++) {
    // 遍历字符串出现的顺序(保证第一次出现重复),当出现为1时,即找到
    if (obj[str.charCodeAt(i)] == 1) {
      return str.charAt(i);
    }
  }
  return null;
};

# 点个Star支持我一下~

博客链接

姓名去重 | 简单级-算法

博客链接

# 姓名去重

# 描述

给一串名字,将他们去重之后返回。两个名字重复是说在忽略大小写的情况下是一样的。

# 说明:

你可以假设名字只包含大小写字母和空格。

# 样例:

给出:

[
  'James',
  'james',
  'Bill Gates',
  'bill Gates',
  'Hello World',
  'HELLO WORLD',
  'Helloworld'
];

返回:

['james', 'bill gates', 'hello world', 'helloworld'];

# 这题很简单,自己想一下!

# 这题很简单,自己想一下!

# 这题很简单,自己想一下!


# 题目分析:

  • 思路就是:去重和转小写

# code:

题目基本就像下面这样解了,其他的不过是循环的方法,去重的方法不同,事实上都大同小异。

const nameDeduplication = names => {
  names.forEach((value, index) => {
    names[index] = value.toLowerCase(); // 全部转小写
  });
  return [...new Set(names)]; // 去重
};

再讲一个坑吧:

题目描述的时候说是输出是这样的:

    ["james", "bill gates", "hello world", "helloworld"]

当我把代码提交之后,告诉我,期望答案是这样的:

    ["bill gates", "hello world", "helloworld", "james"]

一般来说不会这么坑:

    return [...new Set(names)].sort(); // 我在后面加了一个sort方法就符合他们的预期答案了。。

# 题目比较简单,就不放代码(上面就是)了。

2018.8.16

# 点个Star支持我一下~

博客链接

相亲数 | 简单级-算法

博客链接

# 相亲数

# 难度:简单

# 描述:

一对整数是相亲数是说他们各自的所有有效因子(除了自己以外的因子)之和等于另外一个数。比如(220, 284)就是一对相亲数。

  • 220 的所有因子:1+2+4+5+10+11+20+22+44+55+110 = 284
  • 284 的所有因子:1+2+4+71+142 = 220

给出整数 k,求 1~k 之间的所有相亲数对。

# 样例:

给出 300, 返回 [[220, 284]]

# 思路分析:

因素:给出一个数,能整除该数的的除数都是这个数的因素。

计算出每个数的因素和,将其存起来,然后再去判断是否为相亲数

# 代码模板:

const amicablePair = k => {};

# 想一想再看答案

# 想一想再看答案

# 想一想再看答案

# 代码:

事实上我们要做的只有两步:

  1. 遍历 1~k 求出每个数的因素和,并用对象存储起来,遍历对象,判断相亲数
const amicablePair = k => {
  let obj = {};
  // 遍历整个范围,包括k
  for (let i = 1; i <= k; i++) {
    let arr = [];
    // 求每个数的因数
    for (let j = 1; j < i; j++) {
      // 能整除 没有余数的 除数都是因数
      if (i % j === 0) {
        arr.push(j);
      }
    }
    // 计算因数和
    let total = arr.reduce((a, b) => {
      return a + b;
    }, 0);
    obj[i] = total; // 储存每个数的相亲数
  }
  let res = []; // 结果
  for (let key in obj) {
    // 顺序 当属性的值比属性大时才进入 此处也可防止重复添加
    if (obj[key] > key) {
      // 判断相亲数
      if (key === `${obj[obj[key]]}`) {
        res.push([key, obj[key]]); // 按顺序添加
      }
    }
  }
  return res;
};
console.log('输出', amicablePair(300));
  1. 在求因素和的时候,直接判断是否有对应的相亲数
const amicablePair = k => {
  let res = [];
  // 遍历整个范围,包括k
  for (let i = 1; i <= k; i++) {
    let total = totalFn(i);
    // i的因素和需要比i小 才能push 防止重复添加
    if (total > i) {
      let total2 = totalFn(total); // 求 i的因素和 的因素和
      // 判断相亲数:i是否与i的因素和的因素和相等
      if (i === total2) {
        res.push(i, total);
      }
    }
  }
  // 计算一个数的因素和
  function totalFn(num) {
    let arr = [];
    // 求每个数的因数
    for (let j = 1; j < num; j++) {
      // 能整除 没有余数的 除数都是因数
      if (num % j === 0) {
        arr.push(j);
      }
    }
    // 计算因数和
    let total = arr.reduce((a, b) => {
      return a + b;
    }, 0);
    return total;
  }
  return res; // 结果
};

# 点个Star支持我一下~

博客链接

合并排序数组 | 简单级-算法

博客链接

# 合并排序数组

# 难度:简单

# 描述:

合并两个排序的整数数组 A 和 B 变成一个新的排序数组

# 样例:

给出A=[1,2,3,4]B=[2,4,5,6],返回 [1,2,2,3,4,4,5,6]

# 题目分析:

注意 A 和 B 本来就是排序好的数组,最简单的就是用sort排序了。

# sort排序

  1. 把两个数组合并成一个数组
  2. 用 sort 升序进行排序。
const mergeSortedArray = function(A, B) {
  let newArr = A.concat(B); // 合并数组
  return newArr.sort((a, b) => {
    return a - b; // sort排序
  });
};

# 先对比完一个数组:

  1. 初始两个变量分别对应一个数组,进入循环

  2. i 和 j 不会同时递增,只在对应数组元素打败另一数组元素时才会递增,只要打败一个即可,因为两个数组一开始就是排序好的

  3. i 和 j 必须有一个超过对应数组长度(这样至少有一个数组的元素被逐一比较过)

  4. 如果一个数组那边超过长度,会退出循环,但是可能由一方的长度还有剩余(比如一个元素打败另一数组的所有元素),所以我们需要将长度有剩余的数组剩下的元素全都 push 到新数组中(因为一开始就排序好的,后面出场的只会更强)

const mergeSortedArray = function(A, B) {
  var i, j;
  var arr = [];
  for (i = 0, j = 0; i < A.length && j < B.length; ) {
    // i或者j必须有一个超过对应数组长度 才退出循环 所以至少有一个数组的元素被逐一比较
    if (A[i] < B[j]) {
      // 下面两种写法是一样的
      arr.push(A[i]);
      i++;
    } else {
      arr.push(B[j++]); // 这里会先把j赋值给B[j], 然后再j++
    }
  }

// 上面至少有一个数组已经比较了每个元素 如果还有一方长度有剩余 直接push进来就可以(AB一开始就是排序好的数组)
while (i < A.length) {
arr.push(A[i++]);
}
while (j < B.length) {
arr.push(B[j++]);
}
return arr;
};

# 点个Star支持我一下~

博客链接

两个字符串是变位词 | 简单级-算法

博客链接

# 两个字符串是变位词

# 难度:简单

# 描述:

写出一个函数 anagram(s, t) 判断两个字符串是否可以通过改变字母的顺序变成一样的字符串。

# 样例:

给出 s = "abcd",t="dcab",返回 true. 给出 s = "aacd", t = "acdd", 返回 false. 给出 s = "abcd", t = "dcaba", 返回 false. 给出 s = "abcd", t = "abce", 返回 false.

# 思路分析:

想出了两种解法:分别是用对象和用数组。

要注意出现重复字符串的情况:aaccdd这类的。

# 代码模板:

const anagram = function (s, t) {
}

# 想一想再看答案

# 想一想再看答案

# 想一想再看答案

# 代码:

  1. 用对象来接字符,将重复的字符的数量,比较第二个字符串的数量和值
const anagram = function (s, t) {
    if (s.length === t.length) {
        var obj = {};
        for (let key of s) {
            if (obj[key] === undefined) {
                obj[key] = 1 // 初始化
            } else {
                obj[key] = obj[key]+1 // 相同的字符 增加数量
            }
        }
        for (let key of t) {
            if (obj[key] === undefined) {
                return false // 出现没有值的情况 直接返回false
            } else {
                obj[key] = obj[key]-1 // 将值又减掉 最后全为0 才是正确
            }
        }
        for (let key in obj) {
            if(obj[key] !== 0) return false
        }
        return true  // 每个字符一样 数量也相同 返回true 
    } 
    return false // 数量不同 返回false
}
console.log(anagram('abcd', 'dcab'), anagram('aacd', 'acdd'), anagram('abcd', 'dcaba'), anagram('abcd', 'abce'))
  1. 一个字符串用来匹配,第二个字符串转数组,将找到的字符值设为undefined
const anagram = function (s, t) {
    if(s.length === t.length){
        t = [...t] // 一个字符串用来匹配
        for(let key of s){
            let index = t.indexOf(key);
            if(index !== -1){
                t[index] = undefined // 找到那个值 设为undefined 下次有重复的 就不会再找到
            }else{
                return false // 没找到即为false
            }
        }
        return true // 数量相等 会全都删光
    }else{
        return false // 数量不等即为false
    }
}
console.log(anagram('abcd', 'dcab'), anagram('aacd', 'acdd'), anagram('abcd', 'dcaba'), anagram('abcd', 'abce'))

# 点个Star支持我一下~

博客链接

爬楼梯 2 | 简单级-算法

博客链接

# 爬楼梯 2

# 难度:简单

# 描述:

一个小孩爬一个 n 层台阶的楼梯。他可以每次跳 1 步, 2 步 或者 3 步。实现一个方法来统计总共有多少种不同的方式爬到最顶层的台阶

本题跟爬楼梯一毛一样,只是多了可以一次跳三步,所以尽量自己做出来

# 样例:

n = 3,1 + 1 + 1 = 2 + 1 = 1 + 2 = 3 = 3,共有 4 种方法

# 思路分析:

这类题我们首先要来找其中的规律,找到了里面的规律,剩下的就好办了。

我再列举出几个结果:

1: 1 // 1种方法
2: 1+1=2 // 2种方法
3: 1 + 1 + 1 = 2 + 1 = 1 + 2 = 3 = 3 // 4种方法
4: 1+1+1+1=2+2=1+3 =1+2+1=2+1+1 = 1+1+2= 3+1  // 7种方法
51 * 5 =1+2+2 =2+1+2 =2+2+1 = 1+1+3 =1+3+1 = 3+1+1 = 3+2 = 2+3 = 1+1+1+2 =1+2+1+1 = 2+1+1+1 = 1+1+2+1  // 13种方法

查找样例中的规律:爬楼梯

# 代码模板:

/**
 * @param n: An integer
 * @return: An integer
 */
const climbStairs2 = function(n) {};

# 想一想再看答案

# 想一想再看答案

# 想一想再看答案

# 规律

除了前 3 阶楼梯是没有规律的,第 n 阶的楼梯的方法是第 i-1 、第 i-2 和第 i-3 阶楼梯所用方法的和。

规律都给你总结好了,再给你个机会自己做出来。

# 代码:

解题的核心就是逐步推导,推导出 n 前面的两个值

  1. 递归

因为做过一遍,最先想起来的就是递归。

const climbStairs2 = n => {
  let everyStairs = k => {
    // 循环退出条件
    if (k === 1) return 1;
    if (k === 2) return 2;
    if (k === 3) return 4;
    return everyStairs(k - 1) + everyStairs(k - 2) + everyStairs(k - 3); // 三个值相加求出k所用的方法
  };
  return everyStairs(n);
};
console.log('输出', climbStairs2(3), climbStairs2(4), climbStairs2(5));
  1. 交换变量

实际上我们只需要 n 之前的三个值,就可以求出 n 所用的方法,所以我们没必要将 n 之前的所有值都推导出来。

const climbStairs2 = k => {
  if (k === 1) return 1;
  if (k === 2) return 2;
  if (k === 3) return 4;
  let [a, b, c] = [1, 2, 4]; // 前三阶楼梯
  for (let i = 3; i <= n; i++) {
    [a, b, c] = [b, c, a + b + c]; // 交换变量 更新前两个值,推导k的值
  }
  return c;
};
console.log('输出', climbStairs2(3), climbStairs2(4), climbStairs2(5));
  1. 数组形式:
const climbStairs2 = function(n) {
  let dp = [0, 1, 2, 4]; // 初始数组 前面三个没有规律
  // 从第4阶楼梯开始推导   
  for (let i = 4; i <= n; i++) {
    dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]; // 从3开始都是可以由前面3个元素相加推导出来
  }
  return dp[n];
};

# 点个Star支持我一下~

博客链接

两数之和 | 简单级-算法

博客链接

# 两数之和

# 难度:简单

# 描述:

给一个整数数组,找到两个数使得他们的和等于一个给定的数 target。

你需要实现的函数 twoSum 需要返回这两个数的下标, 并且第一个下标小于第二个下标。注意这里下标的范围是 0 到 n-1。

# 样例:

给出 numbers = [2, 7, 11, 15], target = 9, 返回 [0, 1].

给出 numbers = [2, 33, 11, 2], target = 4, 返回 [0, 3].

# 思路分析:

target减去每个元素的值,得出来的值,就是我们要搜索的值。

# 代码模板:

/**
 * @param numbers: An array of Integer
 * @param target: target = numbers[index1] + numbers[index2]
 * @return: [index1, index2] (index1 < index2)
 */
const twoSum = function(numbers, target) {};

# 想一想再看答案

# 想一想再看答案

# 想一想再看答案

# 代码:

  1. 转成对象:

这是别人的一种解法,比下面的解法复杂点,可以看看,扩展一下思路。

/**
 * @param numbers: An array of Integer
 * @param target: target = numbers[index1] + numbers[index2]
 * @return: [index1, index2] (index1 < index2)
 */
const twoSum = function(numbers, target) {
  let map = {};
  // key : the complement (target - num)
  // value: index for that num
  for (let i = 0; i < numbers.length; i++) {
    const num = numbers[i];
    if (map[num] !== undefined) {
      // 找到值
      return [map[num], i]; // 第一次保存的index 和 刚找到的下标 即结果。
    } else {
      // 第一次进入 保存 要搜索的值和index
      map[target - num] = i; // 第一次
    }
  }
  return [-1, -1];
};
console.log(twoSum([2, 7, 11, 15], 9), twoSum([2, 33, 11, 2], 4));
  1. 双循环
const twoSum = function(numbers, target) {
  for (let index of numbers.keys()) {
    let res = target - numbers[index]; // 要搜索的值
    for (let i = numbers.length - 1; i > index; i--) {
      // 倒序查找,跳过已经遍历过的值
      if (res === numbers[i]) return [index, i]; // 搜索到了 即找到
    }
  }
};
console.log(twoSum([2, 7, 11, 15], 9), twoSum([2, 33, 11, 2], 4));
  1. indexOf()

indexOf的第二个参数是开始搜索的位置,也可以跳过前面已经搜索过的值。

const twoSum = function(numbers, target) {
  for (let index of numbers.keys()) {
    let res = target - numbers[index]; // 相减
    let search = numbers.indexOf(res, index + 1); // 跳过前面已经搜索过的,防止2+2=4 搜索两个2在同一个位置
    if (search !== -1) {
      return [index, search]; // 直接返回值
    }
  }
};
console.log(twoSum([2, 7, 11, 15], 9), twoSum([2, 33, 11, 2], 4));

# 点个Star支持我一下~

博客链接

56 合并区间 | 中等级-算法

博客链接

# 56 合并区间

# 题目链接

# 难度:中等

#

#

#

#

#

#

#

#

#

#

#

#

#

#

# 代码:

排序 即将添加的元素和已添加元素之间的对比

/**
 * @param {number[][]} intervals
 * @return {number[][]}
 */
var merge = function(intervals) {
  if (intervals.length == 0) return [];
  var res = [];
  // 排序二维数组 递增的数组
  intervals.sort(function(a, b) {
    return a[0] - b[0];
  });
  // 初始化区间 从二维数组的第二个元素开始比较
  res.push(intervals[0]);
  for (var i = 1; i < intervals.length; i++) {
    // 当前元素的左边界> 已添加元素的右边界 即为新的区间
    if (intervals[i][0] > res[res.length - 1][1]) {
      res.push(intervals[i]);
    } else if (intervals[i][1] > res[res.length - 1][1]) {
      // 当前元素的右边界 大于已添加元素的右边界 它们重合 合并元素
      res[res.length - 1][1] = intervals[i][1]; // 当前元素的最大值赋值给已添加元素的最大值
    }
  }
  return res;
};

# 鼓励我一下:

觉得还不错的话,给我的项目点个star

# 点个Star支持我一下~

博客链接

11 题盛最多水的容器 | 中等级-算法

博客链接

# 11 题盛最多水的容器

# 题目链接

# 难度:中等

# 思路分析:

双指针滑窗

#

#

#

#

#

#

#

#

#

#

#

#

#

#

# 代码:

暴力法

function maxArea(height) {
  const total = height.length;
  let max = 0;
  // 双循环 每个木板都跟其他木板匹配一次
  for (const i = 0; i < total; i++) {
    for (const j = 1; j < total; j++) {
      // 两个木板的高度
      const height1 = height[i];
      const height2 = height[j];
      // 获取最小高度
      const heightNum = height1 > height2 ? height2 : height1; // 取木板最小的那个值
      const lengthNum = j - i; // 底部的长度
      const size = heightNum * lengthNum; // 当前两块木板的面积
      if (size > max) {
        max = size; // 最大面积
      }
    }
  }
  return max;
}

双指针: 滑窗

var maxArea = function(height) {
  let left = 0; //左下标
  let right = height.length - 1; //右下标
  let max = 0; //最大装水量
  while (left < right) {
    let now = (right - left) * Math.min(height[right], height[left]); // 当前水量
    max = now > max ? now : max; // 更新最大水量
    // 窗口缩小思路
    // 从数组左右两侧开始,判定两者的大小,以较小的一侧为滑动边界;
    // 如果滑动边界向内收缩一位的值比之前的值要小,那么继续滑动,这时候的面积肯定是逐渐减小的;
    // 当出现滑动边界的值比之前的大了,那么就需要重新判断下左右边界的大小,进行一次新的操作;
    // 最终会找到一个窗口的最大值 遍历一次 O(n)
    if (height[left] > height[right]) {
      right--;
    } else {
      left++;
    }
  }
};

# 鼓励我一下:

觉得还不错的话,给我的项目点个star

# 点个Star支持我一下~

博客链接

丢失的数 | 简单级-算法

博客链接

# 丢失的数

# 难度:简单

# 描述:

在数组 A 中,包含 0 到 n 的整数,其中缺失了一个数。请编写代码,以查找数组中缺失的整数。

# 样例:

array = [4,3,2,0,5] return 1

array = [0,1,2,3,4,7,6] return 5

array = [0,1,2,3] return 4

# 思路分析:

简单,不分析

# 代码模板:

const findMissing = arr => {};

# 想一想再看答案

# 想一想再看答案

# 想一想再看答案

# 代码:

遍历数组长度的n+1次(包括0),对比有没有这个值即可

const findMissing = arr => {
  let num = arr.length + 1;
  for (let i = 0; i < num; i++) {
    if (arr.indexOf(i) === -1) {
      // i不在数组中 就找到这个值
      return i;
    }
  }
};
console.log('输出', findMissing([4, 3, 2, 0, 5])); //1
console.log(findMissing([0, 1, 2, 3, 4, 7, 6])); // 5
console.log(findMissing([0, 1, 2, 3])); // 4

# 点个Star支持我一下~

博客链接

水仙花数 | 入门级-算法

博客链接

# 水仙花数

# 水仙花数的定义:

一个 N 位非负整数,其各位数字的 N 次方和等于该数本身

栗子:

153 = 1^3 + 5^3 + 3^3

370 = 3^3 + 7^3 + 0^3

371 = 3^3 + 7^3 + 1^3

1634 = 14^4 + 64^4 + 34^4 + 44^4。

更详细的推荐:维基百科

# 描述:

给出n,找到所有的n位十进制水仙花数。

# 样例:

比如 n = 1, 所有水仙花数为:[0,1,2,3,4,5,6,7,8,9]

而对于 n = 2, 则没有 2 位的水仙花数,返回 []

# 题目分析:

弄懂水仙花数!


# 判断一个数是否为水仙花数:

要找出水仙花数,首先我们需要能识别一个数是否为水仙花数:

// 判断一个数是否为水仙花数
const isTrue = num => {
  const n = num.toString().length; // 数的长度
  const str = num.toString(); // 转字符 等下取数字
  let total = 0; // 总数
  for (let i = 0; i < n; i++) {
    total += Math.pow(str.charAt(i), n); // 转字符串一个字符一个字符拿出来 计算其各位数字的N次方和
  }
  if (num === total) {
    return true; // 最终相等 即为正确
  } else {
    return false;
  }
};

# 找出所有的n位十进制水仙花数

  • 确定查找的范围(找出n位的最大值与最小值)
  • 遍历每个数,判断为水仙花数,添加到数组中
const getNarcissisticNumbers = n => {
  let min = Math.pow(10, n - 1),
    max = Math.pow(10, n),
    arr = [];
  if (n === 1) { // n == 1的时候,min应该等于0,但是min等于1,所以这边手动判断一下。
    min = 0;
  }
  for (let j = min; j < max; j++) {
    const str = j.toString(); // 转字符
    let total = 0;
    for (let i = 0; i < n; i++) {
      // 判断一个数是否为水仙花数
      total += Math.pow(str.charAt(i), n); // 转字符串一个字符一个字符拿出来 计算其各位数字的N次方和
    }
    if (j === total) {
      arr.push(j);
    }
  }
  return arr;
};

注意:

查找位数过大会出现性能问题,以及最大值溢出问题。

# 点个Star支持我一下~

博客链接

查找斐波纳契数列中第 N 个数 | 入门级-算法

博客链接

# 查找斐波纳契数列中第 N 个数

# 描述

所谓的斐波纳契数列是指

前 2 个数是 0 和 1 。

第 i 个数是第 i-1 个数和第 i-2 个数的和。

斐波纳契数列的前 10 个数字是

    0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ...

# 怎样算解成功:

给定 1,返回 0

给定 2,返回 1

给定 10,返回 34

# 题目分析:

值得注意的是:前两个数字可以算成是起始元素,从第三个元素才开始有规则。

# code:

  1. 递归解法:
const fibonacci = n => {
  if (!(typeof n === 'number' && n % 1 === 0 && n > 1)) {
    throw '请输入大于0的整数数字';
  }
  var array = [0, 0, 1];
  let temp = n => {
    if (n == 1 || n == 2) return array[n];
    array[n] = temp(n - 1) + temp(n - 2); // 递归获取推算数组每一个元素的值
    return array[n];
  };
  let num = temp(n);
  array.splice(2, 1); // 将数组恢复成 斐波纳契数列
  return num;
};
  1. 遍历保存结果
const fibonacci = n => {
  let a = 0,
    b = 1,
    c,
    d = [0];
  for (let i = 1; i < n; i++) {
    c = a + b;
    a = b;
    b = c;
    d.push(a); // 加戏 恢复数列
  }
  console.log(d, '斐波纳契数列');
  return a;
};
  1. 一次遍历 逐步推导所有元素 时间消耗:158ms 最优
const fibonacci = n => {
  let num = new Array(n).fill(0); // 初始化数组,并设置初始值
  num[1] = 1; // 设置第二个元素的值 推导第3个元素
  for (let i = 2; i <= n - 1; i++) {
    num[i] = num[i - 2] + num[i - 1]; // 遍历逐步推导元素值 数组完全符合数列不用进行判断等 运行效率最高。
  }
  return num[n - 1]; // 数组是从0开始计算 所以要减1
};

不行,我一定要秀一波,不然心里难受:

最后一题的提交,甩的第二名看不到我的车尾灯,开心!

第一回刷算法题,以后要继续坚持!

# 代码地址

2018.8.5

# 点个Star支持我一下~

博客链接

200 题岛屿数量 | 中等级-算法

博客链接

# 200 题岛屿数量

# 题目链接

# 难度:中等

# 思路分析:

递归

#

#

#

#

#

#

#

#

#

#

#

#

#

#

# 代码:

// 深度优先
var numIslands = function(grid) {
  let num = 0;
  if (grid && grid.length) {
    const maxI = grid.length - 1,
      maxJ = grid[0].length - 1;
    // 递归将连接的岛屿全都转为海水 连成一片
    function overturn(i, j) {
      if (i < 0 || j < 0 || i > maxI || j > maxJ) return;
      if (grid[i][j] === "1") {
        grid[i][j] = "0";
        overturn(i, j - 1);
        overturn(i - 1, j);
        overturn(i + 1, j);
        overturn(i, j + 1);
      }
    }
    for (let i = 0; i < grid.length; i++) {
      for (let j = 0; j < grid[i].length; j++) {
        if (grid[i][j] === "1") {
          // 每次碰到1就说明有新的岛屿 与之相连的岛屿都已经递归转化成海水了
          num++;
          overturn(i, j); // 将连接的岛屿全都转为海水 连成一片
        }
      }
    }
  }
  return num;
};

# 鼓励我一下:

觉得还不错的话,给我的项目点个star

# 点个Star支持我一下~

博客链接

15 三数之和 | 中等级-算法

博客链接

# 15 三数之和

# 题目链接

# 难度:中等

#

#

#

#

#

#

#

#

#

#

#

#

#

#

# 代码:

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function(nums) {
  let ans = [];
  const len = nums.length;
  if (nums == null || len < 3) return ans;
  nums.sort((a, b) => a - b); // 排序
  for (let i = 0; i < len; i++) {
    if (nums[i] > 0) break; // 如果当前数字大于0,则三数之和一定大于0,所以结束循环
    if (i > 0 && nums[i] === nums[i - 1]) continue; // 当前值跟上一个值重复 上面左右指针已经相加过 会导致结果重复 跳过后面的
    let L = i + 1; // 左指针比当前值大
    let R = len - 1; // 右指针
    while (L < R) {
      // 当前值 穷尽可能为0
      const sum = nums[i] + nums[L] + nums[R];
      if (sum == 0) {
        ans.push([nums[i], nums[L], nums[R]]);
        while (L < R && nums[L] === nums[L + 1]) L++; // 指针值相同 跳过 否则会添加重复值
        while (L < R && nums[R] === nums[R - 1]) R--; // 指针值相同 跳过  否则会添加重复值
        L++;
        R--;
      } else if (sum < 0) L++;
      //小于0 左侧值过小
      else if (sum > 0) R--; // 大于0 右侧值过大
    }
  }
  return ans;
};

# 鼓励我一下:

觉得还不错的话,给我的项目点个star

# 点个Star支持我一下~

博客链接

比较字符串 | 简单级-算法

博客链接

# 比较字符串

# 难度:简单

# 描述:

比较两个字符串 A 和 B,确定 A 中是否包含 B 中所有的字符。字符串 A 和 B 中的字符都是 大写字母

# 样例:

给出 A = "ABCD" B = "ACD",返回 true

给出 A = "ABCD" B = "AABC", 返回 false

# 代码模板:

/**
 * @param A: A string
 * @param B: A string
 * @return: if string A contains all of the characters in B return true else return false
 */
const compareStrings = function(A, B) {};

# 思路分析:

将字符串转成数组来处理

# 想一想再看答案

# 想一想再看答案

# 想一想再看答案

# 代码:

const compareStrings = function(A, B) {
  [A, B] = [[...A], [...B]]; // 转成数组操作
  for (let index of B.keys()) {
    if (A.indexOf(B[index]) !== -1) {
      // B数组元素和A数组元素成功匹配
      A.splice(find, 1); // 删除A数组中已匹配到的,保持数量相等
    } else {
      return false; // B数组中有A不包含的字符串
    }
  }
  return true;
};
console.log(compareStrings('ABCD', 'ACD'), compareStrings('ABC', 'A'));

# 点个Star支持我一下~

博客链接

落单的数 | 简单级-算法

博客链接

# 落单的数

# 难度:简单

# 描述:

给出 2*n + 1 个的数字,除其中一个数字之外其他每个数字均出现两次,找到这个数字。

# 样例:

给出 [1,2,2,1,3,4,3],返回 4

给出 [7, 10001, 10002, 10003, 10001, 10002, 10003, 10004, 7, 10004, 91985345, 2, 2, 3, 4, 5, 5, 4, 3, 11, 91985345],返回11

# 思路分析:

有很多解决方式,建议用indexOf来查找是否有值。

# 代码模板:

/**
 * @param A: An integer array
 * @return: An integer
 */
const singleNumber = function (A) {
}

# 想一想再看答案

# 想一想再看答案

# 想一想再看答案

# 代码:

  1. 转对象,如果第二次出现,删除该值,最后只剩一个值
const singleNumber = function (A) {
    let [obj, res] = [{}, []];
    for (let index of A.keys()) {
        if (obj[A[index]] !== undefined) { // 判断出现次数
            // 第二次出现 
            let test = A[index];
            delete res[obj[A[index]]]; // 删除数组元素 变为undefined
        } else {
            // 第一次出现
            res.push(A[index]); // 数组元素添加进去
            obj[A[index]] = res.length - 1; // 保存下标 用于等下删除第二次出现的元素
        }
    }
    return Number(res.join('')); // 转成字符串 最终只有一个值输出
}
console.log(singleNumber([1, 2, 2, 1, 3, 4, 3]), singleNumber([7, 10001, 10002, 10003, 10001, 10002, 10003, 10004, 7, 10004, 91985345, 2, 2, 3, 4, 5, 5, 4, 3, 11, 91985345]));
  1. 查找该值的前后是否有该值,如果没有,即找到落单的值
const singleNumber = function (A) {
    var b = []
    for (var i = 0; i < A.length; i++) {
        var v = A[i]
        // 如果b数组中没有v(没有push过 代表前面没有v)并且A数组在后面也没有该值(也就是后面也没有v)
        if (b.indexOf(v) === -1 && A.indexOf(v, i + 1) === -1) {
            return v // 前面没有v 后面也没有v 即是唯一的值
        }
        b.push(v); // 添加v
    }
}
console.log(singleNumber([1, 2, 2, 1, 3, 4, 3]), singleNumber([7, 10001, 10002, 10003, 10001, 10002, 10003, 10004, 7, 10004, 91985345, 2, 2, 3, 4, 5, 5, 4, 3, 11, 91985345]));
  1. 转成本地变量操作,将该值删除,再查找是否有该值,如果没有即找到该值
const singleNumber = function (A) {
    for (var i = 0; i < A.length; i++) {
        var s = [].concat(A) //  转成本地变量
        s.splice(i, 1) // 将该值删除
        if (s.indexOf(A[i]) === -1) { // 被删过一次 再查找是否还有这个值
            return A[i] // 如果没有的话 即找到该值
        }
    }
}
console.log(singleNumber([1, 2, 2, 1, 3, 4, 3]), singleNumber([7, 10001, 10002, 10003, 10001, 10002, 10003, 10004, 7, 10004, 91985345, 2, 2, 3, 4, 5, 5, 4, 3, 11, 91985345]));

# 点个Star支持我一下~

博客链接

第 k 大元素 | 中等级-算法

博客链接

# 第 k 大元素

# 难度:中等

# 描述:

在数组中找到第 k 大的元素

# 样例:

给出数组 [9,3,2,4,8],第三大的元素是 4

给出数组 [1,2,3,4,5],第一大的元素是 5,第二大的元素是 4,第三大的元素是 3,以此类推

# 思路分析:

# 代码模板:

/**
 * @param n: An integer
 * @param nums: An array
 * @return: the Kth largest element
 */
const kthLargestElement = function(n, nums) {
  // write your code here
};

# 想一想再看答案

# 想一想再看答案

# 想一想再看答案

# 代码:

  1. 从大到小,移除n个最大值
const kthLargestElement = function(n, nums) {
  let value;
  // 遍历n次,移除n个最大值,最终value即为第n大元素
  for (let i = 0; i < n; i++) {
    let item = Math.max(...nums); // 取出最大值
    value = nums.splice(nums.indexOf(item), 1)[0]; // 删除并保存最大值
  }
  return value;
};
console.log(
  '输出',
  kthLargestElement(3, [9, 3, 2, 4, 8]),
  kthLargestElement(1, [1, 3, 4, 2])
);
  1. sort排序
const kthLargestElement = function(n, nums) {
  // 降序
  nums.sort((a, b) => {
    return b - a;
  });
  return nums[n - 1]; // 第n大(数组从0开始)
};
console.log(
  '输出',
  kthLargestElement(3, [9, 3, 2, 4, 8]),
  kthLargestElement(1, [1, 3, 4, 2])
);

# 点个Star支持我一下~

博客链接

反转一个 3 位整数 | 入门级-算法

博客链接

# 反转一个 3 位整数

# 描述:

反转一个只有 3 位数的整数

# 样例:

123 反转之后是 321。 900 反转之后是 9。

# 题目分析:

  • 009这种形式需要转为9
  • 最后输出的数字。

# 转数组操作:

这是最简单,最容易想到的答案:

  1. 数字转成字符串再转成数组
  2. 颠倒数组(翻转了),恢复成字符串
  3. 输出正常数字,这里用了+号。(用parseInt等也是可以的)
const reverseInteger = function(number) {
  return +[...number.toString()].reverse().join('');
};

# 取余数,逐个颠倒

const reverseInteger = function (number) {
    return parseInt(number%10)*100+parseInt((number%100)/10)*10+parseInt(number/100)*1
}

通过取余操作,个位转百位,十位转十位,百位转个位。

比如:123=>300+20+1,输出321

# 拼接字符串:

  • 数字转字符串
  • 从后往前取对应位置字符,拼接成一个颠倒的字符串
const reverseInteger = function(number) {
  var str = number.toString(); // 转字符
  return parseInt(str.charAt(2) + str.charAt(1) + str.charAt(0)); // 取对应位置字符,拼接成新的字符串
};

# 点个Star支持我一下~

博客链接

反转整数 | 简单级-算法

博客链接

# 反转整数

# 描述

将一个整数中的数字进行颠倒,当颠倒后的整数溢出时,返回 0 (标记为 32 位整数)。

# 样例:

给定 x = 123,返回 321

给定 x = -123,返回 -321

给定 x = 1534236469, 返回 0


# 这题很简单,自己想一下!

# 这题很简单,自己想一下!

# 这题很简单,自己想一下!


# 解法:

  1. 最优:转字符串 再转数组进行操作
  2. 看到有人用四则运算+遍历反转整数,会把这个解法放到下面

# 提示:

整数溢出的值为Math.pow(2, 31) - 1Math.pow(-2, 31) + 1,转为数字:2147483647-2147483647

这部分跟位操作符,二进制有关,有兴趣可以去搜下。

# code:

  1. 转数组操作:
const reverseInteger = n => {
  if (n < 0) {
    n = n.toString().split('-')[1]; // 负数提取数字
    n = '-' + [...n].reverse().join('');
    n = +n; // 转数字
  } else {
    n = n.toString(); // 转字符
    n = +[...n].reverse().join(''); // 转为数组 颠倒数组 再合字符 最后转数字
  }
  if (n >= Math.pow(2, 31) - 1 || n <= Math.pow(-2, 31) + 1) {
    // 判断溢出
    return 0;
  }
  return n;
};
  1. 遍历,一位一位颠倒:
const reverseInteger = function(n) {
  if (n === 0) return 0;
  let res = 0;
  while (n !== 0) {
    // 从个位起一位一位的颠倒
    res = res * 10 + (n % 10);
    n = parseInt(n / 10); // n除以10, 一位数转化完成 到最后小于1 被转成0 退出循环
  }
  if (res >= 2147483647 || res <= -2147483647) {
    return 0;
  }
  return res;
};

# 转数组操作运行效率也更高点:

# 点个Star支持我一下~

博客链接

54 螺旋矩阵 | 中等级-算法

博客链接

# 54 螺旋矩阵

# 题目链接

# 难度:中等

# 思路分析:

环形遍历、

#

#

#

#

#

#

#

#

#

#

#

#

#

#

# 代码:

环形遍历 设置四个边界 由外向内遍历

var spiralOrder = function(matrix) {
  if (matrix.length === 0) return [];
  const res = [];
  let top = 0,
    bottom = matrix.length - 1,
    left = 0,
    right = matrix[0].length - 1;
  while (top < bottom && left < right) {
    for (let i = left; i < right; i++) res.push(matrix[top][i]); // 上层
    for (let i = top; i < bottom; i++) res.push(matrix[i][right]); // 右层
    for (let i = right; i > left; i--) res.push(matrix[bottom][i]); // 下层
    for (let i = bottom; i > top; i--) res.push(matrix[i][left]); // 左层
    right--;
    top++;
    bottom--;
    left++; // 四个边界同时收缩,进入内层
  }
  // 剩下一行,从左到右依次添加
  if (top === bottom && left <= right)
    for (let i = left; i <= right; i++) res.push(matrix[top][i]);
  // 剩下一列,从上到下依次添加
  else if (left === right && top <= bottom)
    for (let i = top; i <= bottom; i++) res.push(matrix[i][left]);
  return res;
};

环形遍历到底 中途退出

var spiralOrder = function(matrix) {
  if (matrix.length == 0) return [];
  const res = [];
  let top = 0,
    bottom = matrix.length - 1,
    left = 0,
    right = matrix[0].length - 1;
  // 即使top === bottom 或者 left === right 可能还剩一行或者一列
  while (top <= bottom && left <= right) {
    for (let i = left; i <= right; i++) res.push(matrix[top][i]);
    top++; // i = top 如果是最后一项 那么下面一个for循环不会运行
    for (let i = top; i <= bottom; i++) res.push(matrix[i][right]);
    right--;
    // 跟上个方法的区别
    // 当top > bottom 或者 left > right 其中有条边界将交错
    // 即所有项都添加完成
    if (top > bottom || left > right) break;
    for (let i = right; i >= left; i--) res.push(matrix[bottom][i]);
    bottom--;
    for (let i = bottom; i >= top; i--) res.push(matrix[i][left]);
    left++;
  }
  return res;
};

# 鼓励我一下:

觉得还不错的话,给我的项目点个star

# 点个Star支持我一下~

博客链接

字符串压缩 | 简单级-算法

博客链接

# 字符串压缩

# 难度:简单

# 描述:

设计一种方法,通过给重复字符计数来进行基本的字符串压缩。

例如,字符串 aabcccccaaa 可压缩为 a2b1c5a3 。而如果压缩后的字符数不小于原始的字符数,则返回原始的字符串。

可以假设字符串仅包括 a-z 的字母。

# 样例:

str=aabcccccaaa 返回 a2b1c5a3

str=aabbcc 返回 aabbcc

str=aaaa 返回 a4

# 思路分析:

解题思路:取出字符串,判断重复停止,添加到新字符串中。

注:需判断压缩后的字符串长度和原始字符串长度。

# 代码模板:

const compress = function(originalString) {};

# 想一想再看答案

# 想一想再看答案

# 想一想再看答案

# 代码:

// 取出字符串,判断重复停止,添加到新字符串中
const compress = function(originalString) {
  if (originalString.length <= 1) return originalString; // 直接返回源字符串
  let newStr = '';
  let s = originalString.charAt(0);
  let num = 1; // 跳过第一个
  let total = originalString.length;
  for (let i = 1; i < total; i++) {
    let nowS = originalString.charAt(i);
    if (nowS === s) {
      num = num + 1; // 增加数量
      if (i + 1 === total) {
        newStr += `${s}${num}`; // 遍历结束时,拼接最后的字符串
      }
    } else {
      newStr += `${s}${num}`; // 拼接字符串
      num = 1; // 重置为1
      s = nowS; // 转为下一个字符s
    }
  }
  // 生成的字符串长度大于等于源字符串 返回源字符串 否则返回生成的字符串
  if (newStr.length >= originalString.length) {
    return originalString;
  } else {
    return newStr;
  }
};
console.log(
  '输出:',
  compress('aabcccccaaa'), // a2b1c5a3
  compress('aabbcc'), // aabbcc
  compress('aaaa'), // a4
  compress('a'), // a
  compress('') // ''
);

# 点个Star支持我一下~

博客链接

分解质因数 | 简单级-算法

博客链接

# 分解质因数

# 难度:简单

# 质因数的定义:

能整除给定正整数的质数。

百度百科:质因数

# 描述:

  1. 将一个整数分解为若干质因数之乘积
  2. 你需要从小到大排列质因子

# 样例:

  • 给出 10, 返回 [2, 5]
  • 给出 660, 返回 [2, 2, 3, 5, 11]

# 题目分析:

从小到大排列质因子,需要将同一个质因子整除干净。

比如:20 可以被 2 整除两次。

提示:需要两层循环。

# 代码:

// 分解质因数
const primeFactorization = function(num) {
  let res = [];
  // 不需要判定i是否为质数,如果i不为质数,且能整除num时,num早被i的因数所除。故能整除num的i必是质数。
  // i * i > num 退出循环 num一开始会在第二层循环被i整除成比较小的数字
  for (let i = 2; i * i <= num; i++) {
    while (num % i === 0) {
      // 直到有余数退出循环
      num = num / i; // 改变num
      res.push(i); // 没有余数 能整除 这一步会找出所有质因数 不会出现4的那种情况
    }
  }
  if (num !== 1) res.push(num); // num到最后也是质因数
  return res;
};

# 点个Star支持我一下~

博客链接

5 最长回文子串 | 中等级-算法

博客链接

# 5 最长回文子串

# 题目链接

# 难度:中等

# 思路:

中心扩展法

#

#

#

#

#

#

#

#

#

#

#

#

#

#

# 代码:

双指针:中心扩展

/**
 * @param {string} s
 * @return {string}
 */
var longestPalindrome = function(s) {
  let result = s[0] || "";
  // 遍历整个字符串
  for (let i = 0; i < s.length; i++) {
    // 选择一个中心点 j<=2尝试偶数和奇数两种形式的中心点
    for (let j = 1; j <= 2; j++) {
      // 对称回文或者中心点回文
      let left = i, // 左
        right = i + j; // 右
      // 当左右两遍的字符相同时 向外扩展直到两端不相同
      // 左右的边界
      while (left >= 0 && right < s.length && s[left] === s[right]) {
        left--, right++; // 扩展
      }
      // 找到回文
      let length = right - left - 1; // (right - 1) - (left + 1) + 1
      // 对比 更新回文
      if (length > result.length) {
        result = s.substr(left + 1, length);
      }
    }
  }
  return result;
};

动态规划: 中心扩展

/**
 * @param {string} s
 * @return {string}
 */
var longestPalindrome = function(s) {
    let n = s.length;
    let res = '';
    let dp = Array.from(new Array(n),() => new Array(n).fill(0));
    for(let i = n-1;i >= 0;i--){
        for(let j = i;j < n;j++){
            dp[i][j] = s[i] == s[j] && (j - i < 2 || dp[i+1][j-1]);
            if(dp[i][j] && j - i +1 > res.length){
                res = s.substring(i,j+1);
            }
        }
    }
    return res;
};

# 鼓励我一下:

觉得还不错的话,给我的项目点个star

# 点个Star支持我一下~

博客链接

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.