当前位置: 首页 > news >正文

js算法基础-01

文章目录

  • 1、双指针
  • 2、快慢指针
  • 3、滑动指针
  • 4、哈希表
  • 5、汇总区间
  • 6、栈
  • 7、进制求和
  • 8、数学
  • 9、动态规划

js算法基础, 每个重要逻辑思路,做一下列举

1、双指针

  • 有序数组合并:一般思路就是合并、排序,当然效率略低
  • 题目1:nums1中取前m个元素,nums2中取前n个元素,组成新的有序数组,最后结果写入nums1
  • 思路:双指针,两个数组各自一个指针,该位置就是未参与排序的最小值
// 一般思路
const merge = function (nums1, m, nums2, n) {
  const mergedArray = [...nums1.slice(0, m), ...nums2.slice(0, n)]; // 合并
  mergedArray.sort((a, b) => a - b); // 修改原数组,重新排序
  // 数组内容粘贴,一般出现在修改原数组的时候
  for (let i = 0; i < merged.length; i++) {
    nums1[i] = merged[i];
  }
  return nums1;
};

console.log(merge([1, 2, 3, 0, 0, 0], 3, [2, 5, 6], 3));
// 双指针
const merge = function (nums1, m, nums2, n) {
  const target1 = nums1.slice(0, m); // 截取后目标数组
  const target2 = nums2.slice(0, n);
  let index1 = 0; // 每个数组一个排序位置的指针
  let index2 = 0;
  for (let i = 0; i < m + n; i++) {
    const num_1 = target1[index1]; // 每个数组取出的元素
    const num_2 = target2[index2];
    // 边界条件
    if (index1 >= m) {
      // 这里表示数组1已经取完了,那么剩下的全部取数组2的元素即可
      nums1[i] = num_2;
      index2++;
    } else if (index2 >= n) {
      nums1[i] = num_1;
      index1++;
    } else if (num_1 < num_2) {
      // 取最小值
      nums1[i] = num_1;
      index1++;
    } else {
      nums1[i] = num_2;
      index2++;
    }
  }
  return nums1;
};
console.log(merge([1, 2, 3, 0, 0, 0], 3, [2, 5, 6], 3));
  • 题目2:验证回文,返回是或者否
  • 思路1:当需要得到是否的时候,可以正向思考,所有满足即为true,也常见于反向思考,找出一个不符合的即为false,一般看条件苛刻程度,比如预测大部分都符合条件,那么就找出不符合的
  • 思路2:for循环里自变量i就是单指针
// 快速
var isPalindrome = function (s) {
  const str = s.toLowerCase().replace(/[^a-z0-9]/g, "");
  const strR = str.split("").reverse().join("");
  return str === strR;
};

// 双指针
var isPalindrome = function (s) {
  const str = s.toLowerCase().replace(/[^a-z0-9]/g, "");
  for (let i = 0, j = str.length - 1; i < j; i++, j--) {
    if (str[i] !== str[j]) return false;
  }
  return true;
};

// for循环,单指针
var isPalindrome = function (s) {
  const str = s.toLowerCase().replace(/[^a-z0-9]/g, "");
  for (let i = 0; i < str.length / 2; i++) {
    if (str[i] !== str[str.length - 1 - i]) {
      return false;
    }
  }
  return true;
};
console.log(isPalindrome("A man, a plan, a canal: Panama")); // true

2、快慢指针

  • 快慢指针:一般会出现在一个数组、一个目标数据中
  • 题目:删除有序数组中的重复项
  • 思路:由于有序数组中会出现重复的元素,所以慢指针指示写入的位置,快指针跳过重复的元素,直到下一个不同元素出现
var removeDuplicatesArray = [1, 1, 2];
var removeDuplicates = function (nums) {
  let slow = 0,
    fast = 1;
  while (fast < nums.length) {
    if (nums[slow] !== nums[fast]) {
      slow++;
      nums[slow] = nums[fast];
    }
    fast++;
  }
  return slow + 1;
};
console.log(removeDuplicates([1, 1, 2]), removeDuplicatesArray);

补充while 和 for的差异;可以看到差异很小,while没有初始化变量的位置,没有自增位置,只有终止逻辑;一般来说for更全面,while循环都可以改造成for循环;while适用于略微简化逻辑;

var removeDuplicates2 = function (nums) {
  let slow = 0,
    fast = 1;
  for (fast = 1; fast < nums.length; fast++) {
    if (nums[slow] !== nums[fast]) {
      slow++;
      nums[slow] = nums[fast];
    }
  }
  return slow + 1;
};

3、滑动指针

  • 滑动指针:需要对两个指针之间的元素进行计算,比如求和
  • 题目1:找出该数组nums中满足其总和大于等于 target 的长度最小的子数组,返回长度,
var minSubArrayLen = function (target, nums) {
  let length = nums.length,
    // 左指针缩小范围
    left = 0,
    // 右指针扩大范围
    right = 0,
    // sum 是动态和,有时比 target 小,有时比 target 大
    sum = 0,
    // 加1是为了断定是不是所有的元素都加起来都不够target
    minLen = length + 1;
  for (right = 0; right < length; right++) {
    sum += nums[right];
    // 只要sum大于等于target,一直移动左指针,并得到sum和
    while (sum >= target) {
      minLen = Math.min(minLen, right - left + 1); // 计算当前窗口的长度
      sum -= nums[left]; // 减去左指针的值
      left++; // 左指针右移
    }
  }
  return minLen === length + 1 ? 0 : minLen;
};
console.log(minSubArrayLen(5, [1, 4, 4])); // 2

4、哈希表

  • 哈希表:可以统计字符和存储数据
  • 题目1:判断 ransomNote 能不能由 magazine 里面的字符构成;每个字符只能使用一次;
var canConstruct = function (ransomNote, magazine) {
  // 对字符的个数进行统计
  const magazineObj = {};
  // 统计
  for (let i = 0; i < magazine.length; i++) {
    const char = magazine[i];
    if (magazineObj[char]) {
      magazineObj[char]++;
    } else {
      magazineObj[char] = 1;
    }
  }
  // 检验
  for (let i = 0; i < ransomNote.length; i++) {
    const char = ransomNote[i];
    // 减到0就说明没了,返回false
    if (magazineObj[char]) {
      magazineObj[char]--;
    } else {
      return false;
    }
  }
  return true;
};
console.log(canConstruct("aa", "ab")); // false
  • 题目2:两数之和,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标;这里规定只有一对满足条件
  • 思路:两个值,刚好可以用哈希key:value完成记录;(还有三数之和等)
var twoSum = function (nums, target) {
  const numsObj = {};
  // 统计
  for (let i = 0; i < nums.length; i++) {
    const item = nums[i];
    // 注意 索引0是存在配对数的,要判断undefined才准确
    if (numsObj[item] !== undefined) {
      return [numsObj[item], i];
    } else {
      numsObj[target - item] = i; // 记录配对数 对应的索引
    }
  }
  return [];
};

console.log(twoSum([2, 7, 11, 15], 9)); // [0, 1]

5、汇总区间

汇总区间

  • 题目1:给定一个 无重复元素 的 有序 整数数组 nums,返回区间汇总
var summaryRanges = function (nums) {
  // 区间起点指针
  let index = 0,
    result = []; // 结果数组
  for (let i = 0; i < nums.length; i++) {
    const item = nums[i];
    // 判断 是否 连续;不连续就要添加区间
    if (item !== nums[i + 1] - 1) {
      // 是否是单个数字;然后添加区间
      result.push(index === i ? `${item}` : `${nums[index]}->${item}`);
      index = i + 1; // 记录下一个开始的索引
    }
  }
  return result;
};

console.log(summaryRanges([0, 1, 2, 4, 5, 7])); // ["0->2", "4->5", "7"]

6、栈

  • 栈:储存了顺序结构
  • 题目1:判断字符串是不是有效括号
// 方式1,所有括号都有配对的另一半,把配对子都放进去;
// 如果栈最后一个配对上了。说明他是偏向内部的完整括号,那么删除,也不再增加配对子;
// 栈为空,添加配对子;没有匹配上,添加配对子;
var isValid = function (s) {
  // 偶数位
  if (s.length % 2 !== 0 || s.length === 0) return false;
  const configObj = {
    "(": ")",
    "{": "}",
    "[": "]"
  };
  const stack = [];
  for (let i = 0; i < s.length; i++) {
    const char = s[i];
    const target = configObj[char];
    if (stack.length > 0) {
      // 检测是否配对
      if (stack[stack.length - 1] === char) {
        stack.pop();
      } else {
        // 存配对子
        stack.push(target);
      }
    } else {
      // 存配对子
      stack.push(target);
    }
  }
  return stack.length === 0;
};
console.log(isValid("([]{})")); // true
// console.log(isValid("([)")); // false

方式2,只关注左侧括号,只存储左侧括号对应的配对子

// 碰到左侧括号就放入配对子,
// 否则检查最后一个元素是否配对,
// 配对上了就删除最后一个元素
var isValid = function (s) {
  // 偶数位
  if (s.length % 2 !== 0 || s.length === 0) return false;
  const configObj = {
    "(": ")",
    "{": "}",
    "[": "]"
  };
  const stack = [];
  for (let i = 0; i < s.length; i++) {
    const char = s[i];
    const target = configObj[char];
    if (target) {
      // 放入配对子
      stack.push(target);
    } else {
      //  右侧括号 对应
      if (stack[stack.length - 1] !== char) {
        return false;
      }
      // 配对上了 必须删除一个
      stack.pop();
    }
  }
  return stack.length === 0;
};
console.log(isValid("([]{})")); // true
// console.log(isValid("([)")); // false

7、进制求和

  • 进制求和:模仿进制
  • 题目1:二进制求和
var addBinary = function (a, b) {
  const maxLength = Math.max(a.length, b.length);
  let carry = 0; // 进位
  let result = ""; // 结果
  for (let i = 0; i < maxLength; i++) {
    const num1 = a[a.length - 1 - i] || 0; // 取最后一位,不存在则补0
    const num2 = b[b.length - 1 - i] || 0;
    const count = parseInt(num1) + parseInt(num2) + carry; // 加法
    result = (count % 2) + result; // 取最后一位
    carry = count >= 2 ? 1 : 0; // 进位
  }
  return carry === 1 ? 1 + result : result;
};

console.log(addBinary("11", "1")); // "100"

8、数学

  • 题目1:数字加一,[1, 2, 9]=>[1, 3, 0]
  • 熟练使用自增和自减
var plusOne = function (digits) {
  const length = digits.length;
  let carry = 1;
  for (let i = length - 1; i >= 0; i--) {
    const num = digits[i];
    // 处理的位置
    // 进制为10 检查是否进制
    // 不进制,原位置加数
    if (num + carry === 10) {
      digits[i] = 0;
      carry = 1;
    } else {
      digits[i] = num + carry;
      carry = 0;
      break; // 找到第一个不为9的就退出, 不用再加
    }
  }
  // 如果超出进制,在最前面再加一
  if (carry === 1) {
    digits.unshift(1);
  }
  return digits;
};
console.log(plusOne([1, 2, 9])); // [1, 2, 4]

进制时,循环索引可以自增,也可以自减;都可以定位到最后一位

const arr = [1, 2, 9];
// 自增
for (let i = 0; i < arr.length; i++) {
  // 处理的位置
  const index = length - 1 - i;
  const num = digits[index];
}
// 自减
for (let i = arr.length - 1; i >= 0; i--) {
  const num = arr[i];
}

9、动态规划

  • 题目1:假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

var climbStairs = function (n) {
  if (n === 1) return 1;
  if (n === 2) return 2;
  let prev1 = 1;
  let prev2 = 2; // 两种方案 1+1,2
  let result = 0;
  // 由于最后一步只能爬1或2,
  // 所以只需要计算前两步的和,相当于分类讨论;
  // 比如假设最后一步是1,那就是在n-1的位置,爬1个台阶,
  // 最后一步是2,那么就在n-2的位置,爬2个台阶;
  // f(n) = f(n-1) + f(n-2)
  // 如果可以爬1\2\3,则f(n) = f(n-1) + f(n-2) + f(n-3)
  for (let i = 3; i <= n; i++) {
    result = prev1 + prev2;
    prev1 = prev2;
    prev2 = result;
  }
  return result;
};

console.log(climbStairs(5)); // 8

相关文章:

  • python日期和时间、文件和目录操作
  • openstack云平台部署(脚本版)
  • 2025 年浙江保安员职业资格考试高效备考指南​
  • 学会把选择题变成填空题:如何主动设计人生答案
  • BACnet协议+设备数据接入
  • 2025 数字中国创新大赛数字安全赛道数据安全产业积分争夺赛初赛-东部赛区WriteUp
  • AI大模型与未来社会结构的重构:从工具到共生体
  • 代码随想录算法训练营第十一天
  • 前端AJAX请求上传下载进度监控指南详解与完整代码示例
  • ModuleNotFoundError: No module named ‘pandas‘
  • 苹果在中国组装要交关税吗
  • 顺序表——C语言实现
  • Python•判断循环
  • 嵌入式C语言11(宏/程序的编译过程)
  • Linux内核中TCP协议栈的实现:tcp_close函数的深度剖析
  • 深入理解Socket编程:构建简单的计算器服务器
  • Vim搜索和替换
  • 图解AUTOSAR_SWS_FlexRayInterface
  • 京东云智能体平台joybuilder v3.0.0测试
  • Unity中在点击屏幕的地方生成一个自定义的游戏物体(对象池管理生成的游戏物体,在电脑和移动设备能正常运行)
  • 官方网站建设报价/设计网络营销方案
  • wordpress自动填写表格/常州百度关键词优化
  • 个人建设网站/网络平台推广有哪些渠道
  • 电子商务网站与建设实践报告/什么样的人适合做策划
  • 芜湖龙湖建设网站/做网站找哪家好
  • 做导航网站赚钱吗/网店运营公司