数组里的双指针, 链表里的双指针 前缀和和双指针的一些区别, 滑动窗口
数组里一般分为二种指针:
一种是左右指针,一种是快慢指针
左右指针:
一个在头部,一个在尾部,往中间移动,直到相遇;
模板
leetcode 167 两数之和 II - 输入有序数组。
题目名称:两数之和 II - 输入有序数组
题目描述:给定一个已按照 升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
说明:
返回的下标值(index1 和 index2)不是从零开始的。
你可以假设每个输入都只有一个解决方案,而且你不会重复使用相同的元素。
示例:
输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 和 7 的下标值分别为 1 和 2,它们相加之和等于 9。
注意!! 这道题返回的数组,不是从0开始的下标,所以我们返回时,要加1!!!
function twoSum(numbers, target) {
let left = 0,
right = numbers.length - 1;
while (left < right) { //!!双指针这里 left和right是不能重合的!!
let sum = numbers[left] + numbers[right];
if (sum === target) {
return [left + 1, right + 1];
} else if (sum < target) {
left++;
} else {
right--;
}
}
return [];
}
python版本的
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
left, right = 0, len(nums) - 1
while left < right:
sum = nums[left] + nums[right]
if sum == target:
return [left + 1, right + 1]
elif sum < target:
left += 1
else:
right -= 1
return []
java版本的
class Solution {
public int[] twoSum(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int sum = nums[left] + nums[right];
if (sum == target) {
return new int[]{left + 1, right + 1};
} else if (sum < target) {
left++;
} else {
right--;
}
}
return new int[0];
}
}
leetcode 11: 盛最多水的容器
You are given an integer array height of length n. There are n vertical lines drawn such that the two endpoints of the ith line are (i, 0) and (i, height[i]).
Find two lines that together with the x-axis form a container, such that the container contains the most water.
Return the maximum amount of water a container can store.
Notice that you may not slant the container.
Example 1:
Input: height = [1,8,6,2,5,4,8,3,7]
Output: 49
Explanation: The above vertical lines are represented by array [1,8,6,2,5,4,8,3,7]. In this case, the max area of water (blue section) the container can contain is 49.
/**
* @param {number[]} height
* @return {number}
*/
//下标i就是它的宽,height【i】就是它的高
//面积由最小的高乘宽得出,
//移动考虑的条件是谁的高短移动谁
var maxArea = function(height) {
let answer=0;
let i=0;
let j=height.length-1;
while(i<j){
h=Math.min(height[i],height[j]);
w=j-i;
answer=Math.max(answer,h*w);
if(height[i]<height[j]){
i++;
}
else{
j--;
}
}
return answer;
};
插入一个知识点: 和=的区别 (javascript)
==(相等)操作符比较两个值是否相等。如果比较的两个值类型不同,会进行类型转换,然后再比较它们的值。比如:
1 == ‘1’ // true,因为字符串 ‘1’ 被转换成了数字 1
true == 1 // true,因为 true 被转换成了数字 1
===(全等)操作符比较两个值是否完全相等,包括它们的值和类型。如果比较的两个值类型不同,不会进行类型转换,直接返回 false。比如:
1 === ‘1’ // false,因为它们的类型不同
true === 1 // false,因为它们的类型不同
null === undefined // false,因为它们的类型不同
在一般情况下,建议使用 === 操作符进行比较,因为它比 == 更严格,可以避免类型转换带来的一些问题。
leetcode 15 三数之和 (高频题)
题目:找三个数 相加和为0
n数之和的类型
转化为二数之和
思路!!:
一:因为二数之和是要把数字排序,所以三数要先把数字排序
二:确定第一个数num,然后遍历后面的数 i+1,找二数之和为 -num
因为结果可能不止一个,所以用数组保存下来
而且返回的不是下标,是输出的数
如果 (1,2,3) 是一个不重复的三元组,那么 (2,1,3) 和 (1,3,2) 都是重复的三元组。
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function(nums) {
const res=[];
nums.sort((a,b)=>a-b);
for(let i=0;i<nums.length-2;i++){
if (i > 0 && nums[i] === nums[i - 1]) {
continue;
}
// 这一步是为了避免重复计算。在排序后的数组中,如果两个元素的值相等,则它们在数组中的位置相邻。所以当当前元素的值和前一个元素的值相等时,我们跳过当前元素的计算,因为它已经被计算过了。这可以避免重复计算,减少时间复杂度。
let left=i+1,right=nums.length-1;
while(left<right){
const sum=nums[i]+nums[left]+nums[right];
if(sum===0){
res.push([nums[i],nums[left],nums[right]]);
while(left<right && nums[left]===nums[left+1]){
left++;
continue;
}
while(left<right && nums[right]===nums[right-1]){
right++;
continue;
}
// 这个条件的作用是避免重复的元素被计算,因为题目要求的是不重复的三元组,所以当 sum 等于0时,需要排除掉相同的元素。而当 sum 小于0或大于0时,由于左右指针都已经移动过了,所以不会出现重复的情况,所以不需要再检查是否有重复的元素。
left++;
right--;
}
else if (sum<0){
left++;
}
else{
right--;
}
}
}
return res;
};
/**
* @param {number[]} nums
* @return {number[][]}
*/
var threeSum = function(nums) {
const res = [];
nums.sort((a, b) => a - b);
for (let i = 0; i < nums.length - 2; i++) {
if (i > 0 && nums[i] === nums[i - 1]) {
continue;
}
let left = i + 1, right = nums.length - 1;
while (left < right) {
const sum = nums[i] + nums[left] + nums[right];
if (sum === 0) {
res.push([nums[i], nums[left], nums[right]]);
while (left < right && nums[left] === nums[left + 1]) {
left++;
}
while (left < right && nums[right] === nums[right - 1]) {
right--;
}
left++;
right--;
} else if (sum < 0) {
left++;
} else {
right--;
插入一个小知识点:res.push()
res.push() 是 JavaScript 数组的一个方法,用于将一个元素添加到数组的末尾。在这个题目中,每次找到符合条件的三元组后,我们将它们存储到 res 数组中,方便最后返回所有符合条件的三元组。
python版本的!!
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
res = []
nums.sort()
for i in range(len(nums) - 2):
if i > 0 and nums[i] == nums[i - 1]:
continue
left, right = i + 1, len(nums) - 1
while left < right:
s = nums[i] + nums[left] + nums[right]
if s == 0:
res.append([nums[i], nums[left], nums[right]])
while left < right and nums[left] == nums[left + 1]:
left += 1
while left < right and nums[right] == nums[right - 1]:
right -= 1
left += 1
right -= 1
elif s < 0:
left += 1
else:
right -= 1
return res
java版本
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length - 2; i++) {
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1, right = nums.length - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0) {
res.add(Arrays.asList(nums[i], nums[left], nums[right]));
while (left < right && nums[left] == nums[left + 1]) {
left++;
}
while (left < right && nums[right] == nums[right - 1]) {
right--;
}
left++;
right--;
} else if (sum < 0) {
left++;
} else {
right--;
}
}
}
return res;
}
}
Leetcode 344 345 125
这三个题都是一样的类型,都是双指针往中间走的时候,作一些逻辑判断
有一些是替换字符
有一些是验证是否相等
leetcode 344 反转字符串
(简单的双指针,互相替换)
(而且给的是简单的已经分隔的字符串数组)
var reverseString = function(s) {
let left=0;
let right=s.length-1;
while(left<right){
//互相替换的步骤 先设个变量保存前一个
let i=s[left];
s[left]=s[right];
s[right]=i;
left++;
right--;
}
return s;
};
var reverseString = function(s) {
let left=0;
let right=s.length-1;
while(left<right){
[s[left], s[right]] = [s[right], s[left]];
left++;
right--;
}
return s;
};
//可以直接使用ES6的解构语法简化代码
leetcode 345 反转字符串中的元音字母
(与上题相比,加了条件,有条件的替换)
思路:
一:要先把字符串给分隔开成一个个数组
二:作判断
(和上一题类似,只是多了一步 如果不是元音字母就跳过,i++,j–,
否则就互换)
javascript知识点
【
const vowels = new Set([‘a’, ‘e’, ‘i’, ‘o’, ‘u’, ‘A’, ‘E’, ‘I’, ‘O’, ‘U’]);
vowels = set([‘a’, ‘e’, ‘i’, ‘o’, ‘u’, ‘A’, ‘E’, ‘I’, ‘O’, ‘U’]) (python)
Set vowels = new HashSet<>(Arrays.asList(‘a’, ‘e’, ‘i’, ‘o’, ‘u’, ‘A’, ‘E’, ‘I’, ‘O’, ‘U’)); (java)
】
{
Arrays.asList 是 Java 中将数组转换为列表的方法。
在这个例子中,Arrays.asList(arr) 将字符串数组 arr 转换为列表 list。然后可以使用 list 中的方法,例如 list.get(0) 可以得到 “a”,
}
【
let arr = s.split(‘’); // 将字符串转换为数组
t = list(s) (python)
char[] t = s.toCharArray(); (java)
】
【
!vowels.has(arr[left])
if t[i] not in vowels: (python)
!vowels.contains(t[i])
】
【
return arr.join(‘’); // 将数组转换为字符串并返回
return “”.join(t) (python)
return new String(t);
】
{
string.join(iterable)
“”.join(t) 的语法。其中,双引号表示一个空字符串,表示不需要在连接的元素之间添加任何字符。join() 方法会将列表 t 中的元素连接起来,并返回一个新的字符串。
}
var reverseVowels = function(s) {
const vowels = new Set(['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']); // 定义元音字母的集合
let arr = s.split(''); // 将字符串转换为数组
let left = 0, right = arr.length - 1; // 定义双指针
while (left < right) {
while (left < right && !vowels.has(arr[left])) { // 找到左边的元音字母
left++;
}
while (left < right && !vowels.has(arr[right])) { // 找到右边的元音字母
right--;
}
if (left < right) { // 交换左右元音字母
[arr[left], arr[right]] = [arr[right], arr[left]];
left++;
right--;
}
}
return arr.join(''); // 将数组转换为字符串并返回
};
python版的
class Solution:
def reverseVowels(self, s: str) -> str:
vowels = set(['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'])
t = list(s)
i, j = 0, len(s) - 1
while i < j:
if t[i] not in vowels:
i += 1
elif t[j] not in vowels:
j -= 1
else:
t[i], t[j] = t[j], t[i]
i += 1
j -= 1
return "".join(t)
java版的
class Solution {
public String reverseVowels(String s) {
Set<Character> vowels = new HashSet<>(Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'));
char[] t = s.toCharArray();
int i = 0, j = s.length() - 1;
while (i < j) {
if (!vowels.contains(t[i])) {
i++;
} else if (!vowels.contains(t[j])) {
j--;
} else {
char temp = t[i];
t[i] = t[j];
t[j] = temp;
i++;
j--;
}
}
return new String(t);
}
}
var reverseVowels = function(s) {
const vowels = new Set(['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']); // 定义元音字母的集合
let t=s.split('');
let i=0;
let j=s.length-1;
while(i<j){
while( i<j && !vowels.has(t[i])){ //!!!注意这里是while 不是 if !!! 易错点!!
i++;
}
while( i<j && !vowels.has(t[j])){
j--;
}
[t[i],t[j]]=[t[j],t[i]]; //解构赋值左边的变量应该是一个数组或对象
i++;
j--;
}
return t.join('');
};
Leetcode125 验证回文字符串
什么是回文字符串? abccba
在这道题中,空格什么的忽略
两边指针从二边走判断是不是相等
思路:
一:字符串转成数组
二:题目有要求,要把大写字符转化成小写字符
三:一些特殊的字符,比如逗号,空格要跳过,要continue,这里要用一个方法来写;
只比较字母和数字
四:根据题目要求,空字串也算回文子串
javascript 知识点!!
1:正则表达式:
const regExp = /^[0-9a-zA-Z]*$/;
这是一个正则表达式,用于匹配字符串是否只包含数字和字母。具体解释如下:
^ 表示匹配字符串的开始位置。
[0-9a-zA-Z] 表示匹配数字和字母的字符集。
*表示匹配前面的字符集出现 0 次或多次,即匹配任意长度的数字或字母。
$ 表示匹配字符串的结束位置。
因此,整个正则表达式的含义是:匹配一个只包含数字和字母的字符串。
2:regExp.test
!regExp.test(s[left])
test是JavaScript中RegExp对象自带的方法之一,
用于测试字符串是否符合正则表达式的规则。
其语法为RegExpObject.test(string),
其中RegExpObject是正则表达式对象,
string是待测试的字符串。
该方法返回一个布尔值,如果测试字符串符合正则表达式的规则,返回true,否则返回false。
3:s = s.toLowerCase(); // 将字符串转为小写字母
var isPalindrome = function(s) {
// 将字符串转为小写字母
s = s.toLowerCase();
// 定义正则表达式,去掉非字母和数字字符
const regExp = /^[0-9a-zA-Z]*$/;
let left = 0, right = s.length - 1;
while (left < right) {
// 如果左指针对应的字符不是字母或数字,则左指针向右移动
if (!regExp.test(s[left])) {
left++;
continue; //!!要continue!!
}
// 如果右指针对应的字符不是字母或数字,则右指针向左移动
if (!regExp.test(s[right])) {
right--;
continue;
}
// 如果左右指针对应的字符相同,则继续向中间遍历
if (s[left] === s[right]) {
left++;
right--;
} else {
// 如果左右指针对应的字符不同,则不是回文串,返回 false
return false;
}
}
// 如果遍历完成后左右指针重合或者左指针大于右指针,则是回文串,返回 true
return true;
};
java版本的
一: s = s.replaceAll(“[^0-9a-zA-Z]”, “”).toLowerCase();
【正则表达式 [^0-9a-zA-Z] 表示除了数字和字母以外的所有字符】
二:s.charAt(i)
class Solution {
public boolean isPalindrome(String s) {
s = s.replaceAll("[^0-9a-zA-Z]", "").toLowerCase();
int i = 0, j = s.length() - 1;
while (i < j) {
if (s.charAt(i) != s.charAt(j)) {
return false;
}
i++;
j--;
}
return true;
}
}
python版本的
一:''.join(filter(str.isalnum, s)).lower()
【str.isalnum 是一个内置的字符串方法,用于判断一个字符是否为字母或数字,返回布尔值。
filter() 方法的第一个参数是一个函数,该函数接受一个参数并返回一个布尔值,用于判断元素是否保留在结果中。
str.isalnum 是一个内置的字符串方法,用于判断一个字符是否为字母或数字,返回布尔值。因此,
.join(filter(str.isalnum, s)) 表示将字符串 s 中所有字母和数字连接起来,其他字符被过滤掉。
】
二: return s == s[::-1]
【Python 的切片(slice)操作,
[start:stop:step] 表示从字符串中选出从下标 start 开始到下标 stop(不包含 stop)之间的子串,步长为 step。
如果 start 和 stop没有给出,则默认选出整个字符串,步长为1。
当 step` 为负数时,则表示从右向左选取子串。
在 s[::-1] 中,由于 start 和 stop 都没有给出,因此默认选出整个字符串,步长为 -1,即从右向左反转字符串。因此 s == s[::-1] 的作用是判断字符串 s 是否与它反转后的字符串相等。
】
class Solution:
def isPalindrome(self, s: str) -> bool:
s = ''.join(filter(str.isalnum, s)).lower()
return s == s[::-1]
Leetcode 5 找最长的回文子串
思路:
一:判断是不是回文
二:再找最大
三:从二边往中间走会有点困难,
所以我们换个方向,
从中间往二边走 (中心拓展法)
四:这时候就要判断!!
如果是奇数,就是一个点往二边扩散
如果是偶数,就是二个点往二边扩散
javascript知识!!
JavaScript中字符串的取值要用charAt方法
s.substring(start, end + 1);
这里是闭开区间,所以要加1,才得正确结果。
Math.max?
是的,Math.max() 方法只能用于比较数字大小,不能用于比较字符串长度。当然,可以通过将字符串转换为数字或将其长度属性与数字进行比较来实现此目的。
在这道题中,我们需要比较的是字符串的长度,所以可以通过字符串的 length 属性进行比较。例如,将 Math.max(res.length, s1.length, s2.length) 用于比较字符串的长度。
slice? 切片
JavaScript中的数组有slice()方法,可以实现类似切片的功能。slice()方法会返回一个新的数组,该数组包含从原始数组中选择的元素,而不会改变原始数组。slice()方法可以接受一个或两个参数。第一个参数是起始索引,第二个参数是结束索引(不包括该索引对应的元素)。
const arr = [1, 2, 3, 4, 5];
const slicedArr = arr.slice(1, 4); // [2, 3, 4]
const lastTwoElements = arr.slice(-2); // [4, 5]
const copyOfArray = arr.slice(); // [1, 2, 3, 4, 5]
/**
* @param {string} s
* @return {string}
*/
var longestPalindrome = function(s) {
let res='';
for(let i=0;i<s.length;i++){
let s1=expand(i,i,s); //是奇数时从一个点向外扩散
let s2=expand(i,i+1,s); //是偶数时则为二个点
res=res.length>s1.length? res : s1;
res=res.length>s2.length? res : s2;
}
return res;
};
function expand(left,right,s){
while((left>=0) && (right<s.length) && (s[left]===s[right])){
left--;
right++;
}
return s.slice(left+1,right); //这里left已经减了。所以要加回去,right下标不包括
}
python 版本的
class Solution:
def longestPalindrome(self, s: str) -> str:
res = ''
for i in range(len(s)):
odd = self.expand(i, i, s)
even = self.expand(i, i+1, s)
res = max(res, odd, even, key=len)
return res
def expand(self, left: int, right: int, s: str) -> str:
while left >= 0 and right < len(s) and s[left] == s[right]:
left -= 1
right += 1
return s[left+1:right]
java版本的
class Solution {
public String longestPalindrome(String s) {
String res = "";
for (int i = 0; i < s.length(); i++) {
String odd = expand(i, i, s);
String even = expand(i, i+1, s);
res = res.length() > odd.length() ? res : odd;
res = res.length() > even.length() ? res : even;
}
return res;
}
public String expand(int left, int right, String s) {
while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
left--;
right++;
}
return s.substring(left+1, right);
}
}
有序数组可以用双指针来做二分查找
要是无序,就用hashmap