Javascript算法——双指针法移除元素、数组去重、比较含退格字符、有序数组平方

embedded/2024/10/20 20:20:50/

数组移除元素(保证数组仍连续

暴力求解法(两层for循环),length单词拼写错误❌二次嵌套for的length设置

/*** @param {number[]} nums* @param {number} val* @return {number}*/
var removeElement = function(nums, val) {let length=nums.length;for(i=0;i<length;i++){const currVal=nums[i];if(currVal===val){for(k=i;k<length-1;k++){nums[k]=nums[k+1]}i--;//往前移了一个,有要从此位置开始遍历检测length--; }}return length;
};

双指针法(一层for循环)

没碰到要删除的值一样快,碰上要删除的值”慢指针就落后(不++)“

return位置❌ ,核心基础

/*** @param {number[]} nums* @param {number} val* @return {number}*/
var removeElement = function(nums, val) {let slow=0;for(fast=0;fast<nums.length;fast++){if(nums[fast]!==val){nums[slow++]=nums[fast];}}return slow;
};

数组去重

function removeDuplicatesWithTwoPointers(arr) {  // 首先对数组进行排序(如果数组未排序)  arr.sort((a, b) => a - b);  // 初始化两个指针(索引)  let left = 0;  // 遍历数组,从第二个元素开始(索引1)  for (let right = 1; right < arr.length; right++) {  // 如果当前元素与左指针指向的元素不同,则将其保留在数组中  // 并移动左指针到该位置  if (arr[right] !== arr[left]) {  //先移动指针,后替换值left++;  // 可选:如果你想要在原地修改数组,可以使用splice来替换元素  // arr.splice(left, 0, arr[right]); // 这行代码实际上是不必要的,因为我们可以直接赋值  arr[left] = arr[right]; // 直接赋值即可,因为我们已经移动了left指针  }  // 如果相同,则忽略当前元素(右指针继续移动)  }  // 截断数组,只保留去重后的部分  arr.length = left + 1;  return arr;  
}  // 示例  
const originalArray = [4, 4, 2, 5, 1, 2, 3, 3];  
const uniqueArray = removeDuplicatesWithTwoPointers(originalArray);  
console.log(uniqueArray); // 输出: [1, 2, 3, 4, 5]
------------------------------------------------------------------
/*** @param {number[]} nums* @return {number}*/
var removeDuplicates = function(nums) {let pre=0;for(i=1;i<nums.length;i++){if(nums[pre]!==nums[i]){pre++;nums[pre]=nums[i];}}return pre+1;
};

移动零

在移除元素的基础上加了一个zeroIndex而已

/*** @param {number[]} nums* @return {void} Do not return anything, modify nums in-place instead.*/
var moveZeroes = function(nums) {let zeroCounts=0;let currentIndex=0;for(let i=0;i<nums.length;i++){if(nums[i]!=0){nums[currentIndex]=nums[i];currentIndex++}else{zeroCounts++;}}for(j=0;j<zeroCounts;j++){nums[currentIndex+j]=0;}return nums;
};

比较含退格的字符串

使用栈的思路

  1. 定义函数
    首先,你需要定义一个函数,比如叫 backspaceCompare,它接收两个字符串 s 和 t 作为参数。

  2. 处理字符串
    对于每个字符串(s 和 t),你需要编写一个辅助函数(比如叫 processString)来模拟退格字符的行为。这个函数将遍历字符串,并使用一个栈(或数组,因为在这里栈的功能可以通过数组轻松模拟)来存储非退格字符。

    遍历完成后,栈中剩余的字符就是模拟退格字符后的字符串。

    • 遍历字符串时,如果当前字符不是 #,则将其压入栈中。
    • 如果当前字符是 #,则从栈中弹出一个字符(如果栈不为空)。
  3. 比较结果
    使用上述辅助函数分别处理 s 和 t,得到两个处理后的字符串。然后,比较这两个字符串是否相等。

  4. 返回结果
    根据比较的结果,返回 true 或 false

/*** @param {string} s* @param {string} t* @return {boolean}*/
var backspaceCompare = function(s, t) {let sArr=process(s);let tArr=process(t);return sArr.toString()===tArr.toString()
};
function process(str){let arr=[];for(let i=0;i<str.length;i++){if(arr[i]!=='#'){arr.push(arr[i]);}else{arr.pop();}}return arr;
}

双向指针

/*** @param {string} s* @param {string} t* @return {boolean}*/
var backspaceCompare = function(s, t) {//#数量let sSkipNum=0;let tSkipNum=0;let i=s.length-1;let j=t.length-1;while(true){while(i>=0){if(s[i]==="#"){sSkipNum++;i--;}else{//有#号抵消一个数,移动指针[key]if(sSkipNum>0){sSkipNum--;i--;}else{break;}}}while(j>=0){if(t[j]==="#"){tSkipNum++;j--;}else{if(tSkipNum>0){tSkipNum--;j--;}else{break;} }}//有一方处理完就退出if(i<0||j<0)break;//字符不同if(s[i]!==t[j]){return false;}//移动指针,继续比较i--;j--;}if(i==-1&&j==-1){//同时处理完,且字符已经相等了return true;}else{return false;}
};

有序数组平方

定义左右指针和一个索引(从大递减),比较两个数的平方,移动指针更新索引 

function sortedSquares(nums) {  const n = nums.length;  const result = new Array(n); // 创建一个与输入数组相同大小的结果数组  let left = 0; // 左指针  let right = n - 1; // 右指针  let index = n - 1; // 结果数组的索引,从后往前填充  while (left <= right) {  const leftSquare = nums[left] * nums[left];  const rightSquare = nums[right] * nums[right];  if (leftSquare > rightSquare) {  result[index] = leftSquare;  left++;  } else {  result[index] = rightSquare;  right--;  }  index--;  }  return result;  
}  // 示例用法  
const nums = [-4, -1, 0, 3, 10];  
const squaredNums = sortedSquares(nums);  
console.log(squaredNums); // 输出: [0, 1, 9, 16, 100]

在JavaScript算法中,双向指针(也称为双指针或对指针)技术是一种常用的技巧,特别适用于处理需要同时从数组或字符串的两端或中间向某个方向移动指针的问题。这种方法通常能够减少空间复杂度,并且有时也能降低时间复杂度。以下是一些常见的应用场景:

  1. 数组或字符串中的两数之和
    • 给定一个已排序的数组(或字符串)和一个目标和,使用双向指针从两端向中间遍历,找到两个数(或字符)使它们的和等于目标和。这种方法的时间复杂度为O(n)。
  2. 回文检测
    • 判断一个字符串是否是回文。可以使用两个指针,一个从字符串的开头开始,另一个从字符串的末尾开始,然后向中间移动,比较对应位置的字符是否相同。
  3. 容器中的水量
    • 给定n个非负整数表示每个柱子的高度,计算在这些柱子之间能够接到的雨水总量。这个问题可以通过使用双向指针和栈来解决。
  4. 合并两个有序数组
    • 将两个有序数组合并成一个有序数组。可以使用两个指针分别指向两个数组的开头,然后比较当前指针所指的元素,将较小的元素添加到结果数组中,并移动相应的指针。
  5. 最长公共子序列(LCS)
    • 虽然通常使用动态规划来解决LCS问题,但双向指针也可以用于一些特定形式的LCS问题,特别是在与字符串匹配相关的问题中。
  6. 滑动窗口
    • 双向指针技术可以与滑动窗口技术结合使用,以解决一些需要维护一个固定大小的窗口并计算窗口内元素的问题。
  7. 移除元素使数组有序
    • 给定一个数组,其中每个元素都是某个范围内的整数。要求移除尽可能少的元素,使得剩余的元素按非递减顺序排列。这个问题可以通过使用双向指针和贪心策略来解决。
  8. 链表中的两数相加
    • 给定两个表示非负整数的链表,每个节点包含一个数字。这些数字以逆序的方式存储,并且每个节点只能存储一个数字。将这两个数相加并以链表的形式返回它们的和。这个问题可以通过使用双向指针(或更准确地说是两个游标)来遍历两个链表并构建结果链表。

请注意,双向指针的应用场景不仅限于上述例子,它可以根据问题的具体需求进行灵活应用。在解决问题时,重要的是要识别出是否可以通过同时从两个方向遍历数据来简化问题或提高效率。


http://www.ppmy.cn/embedded/129075.html

相关文章

动态规划之打家劫舍

大纲 题目思路第一步&#xff1a;确定下标含义第二步&#xff1a;确定递推公式第二步&#xff1a;dp数组如何初始化第三步&#xff1a;确定遍历顺序第四步&#xff1a;举例推导dp数组 总结 最近有人询问我 LeetCode 「打家劫舍」系列问题&#xff08;英文版叫 House Robber&…

mysql多表关系与查询

一、多表关系 1.多表操作分类 1.1 多对一 关系举例&#xff1a; 多对一&#xff1a;多名学生在一个班级里 一对多主要是靠 “外键” 实现。在 “多” 的表中建立外键&#xff0c;指向 "一"的主键 一对多的关系在实际生产当中使用非常常见。 一对多的核心解决方案是…

使用React Router实现前端的权限访问控制

前段时间学习了React Router&#xff0c;发现没有Vue里面的路由功能强大&#xff0c;没有直接提供路由中间件&#xff0c;不能像Vue里面一样在路由配置上设置任意的额外属性&#xff0c;但是可以通过一些技巧来实现这些功能。 1、配置菜单 后台管理系统一般都会在左侧显示菜单…

SQL第19课——使用存储过程

介绍什么是存储过程&#xff1f;为什么要使用存储过程&#xff1f;如何使用存储过程&#xff1f;创建和使用存储过程的基本语法&#xff1f; 19.1 存储过程 到目前为止&#xff0c;使用的大多数SQL语句都是针对一个或多个表的单条语句。 对于一些复杂的操作需要多条语句才能…

超GPT3.5性能,无限长文本,超强RAG三件套,MiniCPM3-4B模型分享

MiniCPM3-4B是由面壁智能与清华大学自然语言处理实验室合作开发的一款高性能端侧AI模型&#xff0c;它是MiniCPM系列的第三代产品&#xff0c;具有4亿参数量。 MiniCPM3-4B模型在性能上超过了Phi-3.5-mini-Instruct和GPT-3.5-Turbo-0125&#xff0c;并且与多款70亿至90亿参数的…

爬虫逆向学习(十二):一个案例入门补环境

此分享只用于学习用途&#xff0c;不作商业用途&#xff0c;若有冒犯&#xff0c;请联系处理 反爬前置信息 站点&#xff1a;aHR0cDovLzEyMC4yMTEuMTExLjIwNjo4MDkwL3hqendkdC94anp3ZHQvcGFnZXMvaW5mby9wb2xpY3k 接口&#xff1a;/xjzwdt/rest/xmzInfoDeliveryRest/getInfoDe…

线程池原理(一)

一、常用线程池体系结构图如下&#xff1a; 由上边的体系图可以知道&#xff0c;要想了解线程池 ThreadPoolExecutor 的实现原理&#xff0c;则需要先 了解下 Executor、ExecutorService、AbstractExecutorService 的实现&#xff0c;下面就分别看下 这3个类的实现 二、Executo…

6.计算机网络_UDP

UDP的主要特点&#xff1a; 无连接&#xff0c;发送数据之前不需要建立连接。不保证可靠交付。面向报文。应用层给UDP报文后&#xff0c;UDP并不会抽象为一个一个的字节&#xff0c;而是整个报文一起发送。没有拥塞控制。网络拥堵时&#xff0c;发送端并不会降低发送速率。可以…